尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

UDP编程与网络通信

UDP编程与网络通信 摘要本文详细介绍了UDP编程的基本流程、函数接口并深入讲解了广播和组播两种网络通信方式。通过对比TCP与UDP的区别结合完整的代码示例和流程图帮助读者全面理解UDP协议的特点和应用场景。1. UDP编程基础1.1 UDP编程流程UDP通信流程类比发短信服务器端流程创建数据报套接字socket(AF_INET, SOCK_DGRAM, 0)→ 相当于拥有手机绑定网络信息bind()→ 相当于绑定手机号码接收信息recvfrom()→ 接收短信关闭套接字close()→ 通信结束客户端流程创建数据报套接字socket(AF_INET, SOCK_DGRAM, 0)→ 相当于拥有手机指定服务器的网络信息→ 知道对方的手机号码发送信息sendto()→ 发送短信关闭套接字close()→ 发送完毕1.2 UDP核心函数接口recvfrom() 函数ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 功能接收UDP数据包 参数 sockfd套接字描述符 buf接收缓冲区的首地址 len接收缓冲区的大小 flags通常设置为0 src_addr发送端的网络信息结构体指针可获取发送方地址 addrlen发送端网络信息结构体大小的指针 返回值 成功接收到的字节数 失败-1并设置errnosendto() 函数ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 功能发送UDP数据包 参数 sockfd套接字描述符 buf发送缓冲区的首地址 len发送缓冲区的大小 flags通常设置为0 dest_addr接收端的网络信息结构体指针 addrlen接收端网络信息结构体的大小 返回值 成功发送的字节数 失败-1并设置errno重要说明在UDP中recvfrom()函数无法检测客户端是否异常退出因为UDP是无连接的。1.3 UDP服务器端完整示例#include sys/types.h #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include stdio.h #include arpa/inet.h #include unistd.h #include string.h #include stdlib.h int main(int argc, char const *argv[]) { // 1. 创建数据报套接字 int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(socket err); return -1; } // 2. 填充服务器地址结构体 struct sockaddr_in saddr; saddr.sin_family AF_INET; saddr.sin_addr.s_addr inet_addr(argv[1]); // IP地址 saddr.sin_port htons(atoi(argv[2])); // 端口号 // 3. 绑定网络信息 if (bind(sockfd, (struct sockaddr *)saddr, sizeof(saddr)) 0) { perror(bind err); return -1; } printf(bind ok\n); // 4. 接收数据 char buf[128] ; recvfrom(sockfd, buf, 128, 0, NULL, NULL); printf(Received: %s\n, buf); // 5. 关闭套接字 close(sockfd); return 0; }1.4 UDP客户端完整示例#include sys/types.h #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include stdio.h #include arpa/inet.h #include unistd.h #include string.h #include stdlib.h int main(int argc, char const *argv[]) { // 1. 创建数据报套接字 int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(socket err); return -1; } // 2. 填充服务器地址结构体 struct sockaddr_in saddr; saddr.sin_family AF_INET; saddr.sin_addr.s_addr inet_addr(argv[1]); // 服务器IP saddr.sin_port htons(atoi(argv[2])); // 服务器端口 // 3. 发送数据 char buf[128] hello; sendto(sockfd, buf, 128, 0, (struct sockaddr *)saddr, sizeof(saddr)); printf(send ok\n); // 4. 关闭套接字 close(sockfd); return 0; }1.5 双向通信示例练习实现客户端发送hello给服务器端服务器接收后回复recv:hello!!!!!。UDP服务器端双向通信#include sys/types.h #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include stdio.h #include arpa/inet.h #include unistd.h #include string.h #include stdlib.h int main(int argc, char const *argv[]) { // 1. 创建数据报套接字 int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(socket err); return -1; } // 2. 填充服务器地址结构体 struct sockaddr_in saddr, caddr; saddr.sin_family AF_INET; saddr.sin_addr.s_addr inet_addr(argv[1]); saddr.sin_port htons(atoi(argv[2])); // 3. 绑定网络信息 if (bind(sockfd, (struct sockaddr *)saddr, sizeof(saddr)) 0) { perror(bind err); return -1; } printf(bind ok\n); // 4. 接收客户端数据 char buf[128] ; int len sizeof(caddr); recvfrom(sockfd, buf, 128, 0, (struct sockaddr *)caddr, len); printf(Client says: %s\n, buf); // 5. 发送回复给客户端 memset(buf, 0, 128); strcpy(buf, recv:hello!!!!!); sendto(sockfd, buf, 128, 0, (struct sockaddr *)caddr, sizeof(caddr)); // 6. 关闭套接字 close(sockfd); return 0; }UDP客户端双向通信#include sys/types.h #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include stdio.h #include arpa/inet.h #include unistd.h #include string.h #include stdlib.h int main(int argc, char const *argv[]) { // 1. 创建数据报套接字 int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(socket err); return -1; } // 2. 填充服务器地址结构体 struct sockaddr_in saddr; saddr.sin_family AF_INET; saddr.sin_addr.s_addr inet_addr(argv[1]); saddr.sin_port htons(atoi(argv[2])); // 3. 发送数据到服务器 char buf[128] hello; sendto(sockfd, buf, 128, 0, (struct sockaddr *)saddr, sizeof(saddr)); printf(send ok\n); // 4. 接收服务器回复 recvfrom(sockfd, buf, 128, 0, NULL, NULL); printf(Server reply: %s\n, buf); // 5. 关闭套接字 close(sockfd); return 0; }1.6 UDP与TCP的重要区别连接方式TCP必须先运行服务器客户端才能连接UDP服务器和客户端运行顺序没有先后因为UDP是无连接的。并发处理一个UDP服务器可以同时处理多个客户端请求可以通过recvfrom()的src_addr参数获取客户端IP和端口号。connect()函数的作用在UDP中客户端使用send()前可以调用connect()但这并不是建立连接而是指定默认的发送目标之后就可以使用send()代替sendto()。函数通用性在TCP中也可以使用recvfrom()和sendto()使用时将最后两个参数设置为NULL即可。2. 广播通信2.1 广播理论基础前面介绍的数据包发送方式只有一个接收方称为单播。广播定义如果同时发给局域网中的所有主机称为广播。协议限制只有用户数据报使用UDP协议套接字才能广播。应用场景一般被设计成局域网搜索协议如DHCP、ARP等。广播地址以192.168.1.0子网掩码255.255.255.0网段为例最大的主机地址192.168.1.255代表该网段的广播地址发到该地址的数据包被网段内所有主机接收2.2 setsockopt()函数详解Linux中socket属性设置函数#include sys/types.h #include sys/socket.h int setsockopt(int sockfd, int level, int optname, void *optval, socklen_t optlen); 功能设置套接字属性 参数 sockfd套接字描述符指定要设置/获取哪个套接字的属性 level协议层指定要控制的协议层次 SOL_SOCKET应用层 - 通用套接字选项 IPPROTO_TCP传输层 IPPROTO_IP网络层 optname选项名指定要控制的内容和方式 --- SOL_SOCKET常用选项man 7 socket--- SO_REUSEADDR允许端口快速重用int* SO_BROADCAST允许发送广播数据int SO_RCVBUF接收缓冲区大小int SO_SNDBUF发送缓冲区大小int SO_RCVTIMEO接收超时struct timeval SO_SNDTIMEO发送超时struct timeval optval根据optname不同该指针类型不同 optlenoptval指针指向的内存空间的大小 返回值 成功返回0 失败返回-1并更新errnoman 7 socket文档截图2.3 广播发送和接收流程发送广播流程int opt 1; // 类似于广播开关0为关闭1为打开 setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, opt, sizeof(opt));创建数据报套接字填充结构体信息使用广播IP地址允许发送广播数据发送数据关闭套接字接收广播流程创建数据报套接字填充结构体信息使用广播IP地址绑定到广播地址接收数据关闭套接字2.4 广播通信完整示例广播发送端send.c#include sys/types.h #include sys/socket.h #include netinet/in.h #include netinet/ip.h #include stdio.h #include arpa/inet.h #include unistd.h #include string.h #include stdlib.h int main(int argc, char const *argv[]) { // 1. 创建数据报套接字 int sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(socket err); return -1; } // 2. 填充广播地址结构体 struct sockaddr_in saddr; saddr.sin_family AF_INET; saddr.sin_addr.s_addr inet_addr(argv[1]); // 广播IP地址 saddr.sin_port htons(atoi(argv[2])); // 端口号 // 3. 允许发送广播数据 int opt 1; // 类似于广播开关0为关闭1为打开 setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, opt, sizeof(opt)); // 4. 发送广播数据 char buf[128] hello; sendto(sockfd, buf, 128, 0, (struct sockaddr *)saddr, sizeof(saddr)); printf(Broadcast send ok\n); // 5. 关闭套接字 close(sockfd); return 0; }广播接收端recv.c
返回列表