网站欣赏网站asp.net 4.0网站建设基础教程 下载
网站欣赏网站,asp.net 4.0网站建设基础教程 下载,网站建设客户确认单,wordpress升级vipUDP Socket 编程笔记一、UDP 基础知识1. UDP 特点无连接#xff1a;无需建立连接即可通信不可靠#xff1a;不保证数据到达、不保证顺序面向数据报#xff1a;有明确的报文边界高效#xff1a;开销小#xff0c;速度快2. TCP vs UDP特性TCPUDP连接方式面向连接无连接可靠性…UDP Socket 编程笔记一、UDP 基础知识1. UDP 特点无连接无需建立连接即可通信不可靠不保证数据到达、不保证顺序面向数据报有明确的报文边界高效开销小速度快2. TCP vs UDP特性TCPUDP连接方式面向连接无连接可靠性可靠传输不可靠数据边界流式无边界数据报有边界速度较慢较快头部大小20-60字节8字节3. 核心数据结构同TCPstruct sockaddr_in { short sin_family; // AF_INET unsigned short sin_port; // 端口号 struct in_addr sin_addr; // IP地址 char sin_zero[8]; // 填充 };总结UDP编程相对TCP更简单但需要应用层处理可靠性问题。适合实时性要求高、可容忍少量丢失的场景如视频流、DNS查询、在线游戏等。二、UDP 编程核心函数1. 发送数据ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);dest_addr: 目标地址结构体addrlen: 地址结构体长度返回实际发送的字节数-1表示错误2. 接收数据ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);src_addr: 用于保存发送方地址addrlen: 输入输出参数需要初始化返回实际接收的字节数-1表示错误0表示对方关闭连接三、示例程序分析示例1简单回显服务器带时间戳服务器端 (server.c)#include arpa/inet.h #include netinet/in.h #include netinet/ip.h #include stdio.h #include stdlib.h #include string.h #include sys/socket.h #include sys/types.h #include time.h #include unistd.h typedef struct sockaddr *(SA); int main(int argc, char **argv) { // 1. 创建UDP套接字 int sockfd socket(AF_INET, SOCK_DGRAM, 0); // 2. 绑定地址 struct sockaddr_in ser, cli; bzero(ser, sizeof(ser)); bzero(cli, sizeof(cli)); ser.sin_family AF_INET; ser.sin_port htons(50000); ser.sin_addr.s_addr inet_addr(192.168.14.128); bind(sockfd, (SA)ser, sizeof(ser)); // 3. 循环处理客户端请求 socklen_t len sizeof(cli); while (1) { char buf[512] {0}; // 接收数据获取客户端地址 recvfrom(sockfd, buf, sizeof(buf), 0, (SA)cli, len); printf(recv:%s\n, buf); // 添加时间戳 time_t tm; time(tm); struct tm *info localtime(tm); sprintf(buf, %s %d:%d:%d, buf, info-tm_hour, info-tm_min, info-tm_sec); // 发送回客户端 sendto(sockfd, buf, strlen(buf) 1, 0, (SA)cli, len); } close(sockfd); return 0; }客户端 (cli.c)#include arpa/inet.h #include netinet/in.h #include netinet/ip.h #include stdio.h #include stdlib.h #include string.h #include sys/socket.h #include sys/types.h #include time.h #include unistd.h typedef struct sockaddr *(SA); int main(int argc, char **argv) { // 1. 创建UDP套接字 int sockfd socket(AF_INET, SOCK_DGRAM, 0); // 2. 设置服务器地址 struct sockaddr_in ser; bzero(ser, sizeof(ser)); ser.sin_family AF_INET; ser.sin_port htons(50000); ser.sin_addr.s_addr inet_addr(192.168.14.128); // 3. 发送10次数据 int i 10; while (i--) { char buf[512] hello; // 发送数据 sendto(sockfd, buf, strlen(buf), 0, (SA)ser, sizeof(ser)); // 接收响应 bzero(buf, sizeof(buf)); recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL); printf(recv:%s\n, buf); sleep(1); } close(sockfd); return 0; }示例2UDP聊天程序父子进程服务器端 (server.c - 聊天版本)// 创建UDP套接字并绑定 int sockfd socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in ser, cli; // ... 绑定代码 ... // 首次接收获取客户端地址 char buf[512] {0}; recvfrom(sockfd, buf, sizeof(buf), 0, (SA)cli, len); // 创建父子进程 pid_t pid fork(); if (pid 0) { // 父进程发送消息 while (1) { bzero(buf, sizeof(buf)); printf(to B:); fgets(buf, sizeof(buf), stdin); sendto(sockfd, buf, strlen(buf) 1, 0, (SA)cli, sizeof(cli)); if (0 strcmp(buf, #quit\n)) { kill(pid, 9); exit(0); } } } else if (0 pid) { // 子进程接收消息 while (1) { bzero(buf, sizeof(buf)); recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL); if (0 strcmp(buf, #quit\n)) { kill(getppid(), 9); exit(0); } printf(from B:%s, buf); fflush(stdout); } }客户端 (cli.c - 聊天版本)// 代码结构与服务器端类似 // 父子进程分别处理发送和接收实现原理父子进程分离了输入和输出父进程负责读取用户输入并发送子进程负责接收并显示消息使用#quit作为退出指令示例3UDP文件传输服务器端 (文件接收)int fd open(2.png, O_WRONLY | O_CREAT | O_TRUNC, 0666); while (1) { char buf[1024] {0}; int rd_ret recvfrom(sockfd, buf, sizeof(buf), 0, (SA)cli, len); if (rd_ret 0) { break; } write(fd, buf, rd_ret); // 发送确认 bzero(buf, sizeof(buf)); strcpy(buf, go on); sendto(sockfd, buf, strlen(buf), 0, (SA)cli, len); }客户端 (文件发送)int fd open(/home/linux/1.png, O_RDONLY); char buf[1024] {0}; while (1) { bzero(buf, sizeof(buf)); int rd_ret read(fd, buf, sizeof(buf)); if (rd_ret 0) { break; } // 发送文件数据 sendto(sockfd, buf, rd_ret, 0, (SA)ser, sizeof(ser)); // 等待确认 bzero(buf, sizeof(buf)); recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL); } // 发送结束标志 sendto(sockfd, buf, 0, 0, (SA)ser, sizeof(ser));特点使用固定大小缓冲区传输简单的go on确认机制发送0长度数据表示传输结束四、UDP编程注意事项1. 地址绑定// 监听所有接口 ser.sin_addr.s_addr INADDR_ANY; // 监听特定IP ser.sin_addr.s_addr inet_addr(192.168.1.100); // 仅本地通信回环地址 ser.sin_addr.s_addr inet_addr(127.0.0.1);2. 数据包大小限制UDP数据包最大长度65535字节实际受MTU限制通常1500字节建议应用层分片传输大文件3. 可靠性问题解决方案// 1. 超时重传 struct timeval tv; tv.tv_sec 5; // 5秒超时 tv.tv_usec 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, tv, sizeof(tv)); // 2. 序列号机制 typedef struct { uint32_t seq; // 序列号 uint32_t total; // 总包数 char data[1400]; // 数据 } udp_packet_t;4. 并发处理UDP本身是无连接的可以通过以下方式处理多个客户端记录每个客户端的地址使用多线程/多进程使用select()/poll()/epoll()多路复用五、完整编程模板服务器模板#include stdio.h #include stdlib.h #include string.h #include unistd.h #include arpa/inet.h #include sys/socket.h #define PORT 50000 #define BUFFER_SIZE 1024 int main() { int sockfd; struct sockaddr_in server_addr, client_addr; socklen_t client_len; char buffer[BUFFER_SIZE]; // 1. 创建套接字 sockfd socket(AF_INET, SOCK_DGRAM, 0); // 2. 配置服务器地址 memset(server_addr, 0, sizeof(server_addr)); server_addr.sin_family AF_INET; server_addr.sin_port htons(PORT); server_addr.sin_addr.s_addr INADDR_ANY; // 3. 绑定地址 bind(sockfd, (struct sockaddr*)server_addr, sizeof(server_addr)); printf(UDP Server listening on port %d\n, PORT); while (1) { // 4. 接收数据 client_len sizeof(client_addr); int n recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)client_addr, client_len); // 5. 处理数据 buffer[n] \0; printf(Received from %s:%d - %s\n, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buffer); // 6. 发送响应 sendto(sockfd, buffer, n, 0, (struct sockaddr*)client_addr, client_len); } close(sockfd); return 0; }客户端模板#include stdio.h #include stdlib.h #include string.h #include unistd.h #include arpa/inet.h #include sys/socket.h #define SERVER_IP 127.0.0.1 #define PORT 50000 #define BUFFER_SIZE 1024 int main() { int sockfd; struct sockaddr_in server_addr; char buffer[BUFFER_SIZE]; // 1. 创建套接字 sockfd socket(AF_INET, SOCK_DGRAM, 0); // 2. 配置服务器地址 memset(server_addr, 0, sizeof(server_addr)); server_addr.sin_family AF_INET; server_addr.sin_port htons(PORT); server_addr.sin_addr.s_addr inet_addr(SERVER_IP); while (1) { printf(Enter message: ); fgets(buffer, BUFFER_SIZE, stdin); // 3. 发送数据 sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr*)server_addr, sizeof(server_addr)); // 4. 接收响应 int n recvfrom(sockfd, buffer, BUFFER_SIZE, 0, NULL, NULL); buffer[n] \0; printf(Server response: %s\n, buffer); } close(sockfd); return 0; }六、常见错误及解决方法1. 地址已在使用# 错误bind: Address already in use # 解决 netstat -anp | grep 50000 # 查看占用进程 kill -9 PID # 结束进程 # 或使用 SO_REUSEADDR int opt 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));2. 数据包丢失实现应用层的确认重传机制增加超时设置使用更小的数据包大小3. 端口选择问题避免使用知名端口0-1023确保客户端和服务器使用相同端口七、进阶主题1. 广播通信// 设置广播权限 int broadcast 1; setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, broadcast, sizeof(broadcast)); // 广播地址 ser.sin_addr.s_addr inet_addr(255.255.255.255);2. 组播通信// 加入组播组 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr inet_addr(224.0.0.1); mreq.imr_interface.s_addr htonl(INADDR_ANY); setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, mreq, sizeof(mreq));3. 非阻塞UDP// 设置非阻塞模式 int flags fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);