Linux下使用C/C++进行UDP网络编程详解
作者:袁本美
UDP 是User Datagram Protocol 的简称,中文名是用户数据报协议,是一种无连接、不可靠的协议,本文主要介绍了如何在Linux下使用C/C++进行UDP网络编程,有需要的可以了解下
UDP 是User Datagram Protocol 的简称,中文名是用户数据报协议,是一种无连接、不可靠的协议,同样它也是工作在传顺层。它只是简单地实现从一端主机到另一端主机的数据传输功能,这些数据通过 IP 层发送,在网络中传输,到达目标主机的顺序是无法预知的,因此需要应用程序对这些数据进行排序处理,这就带来了很大的不方便,此外,UDP 协议更没有流量控制、拥塞控制等功能,在发送的一端,UDP 只是把上层应用的数据封装到UDP 报文中,在差错检测方面,仅仅是对数据进行了简单的校验,然后将其封装到 IP 数据报中发送出去。而在接收端,无论是否收到数据,它都不会产生一个应答发送给源主机,并且如果接收到数据发送校验错误,那么接收端就会丢弃该UDP 报文,也不会告诉源主机,这样子传输的数据是无法保障其准确性的,如果想要其准确性,那么就需要应用程序来保障了。
UDP 协议的特点:
- 无连接、不可靠;
- 尽可能提供交付数据服务,出现差错直接丢弃,无反馈;
- 面向报文,发送方的UDP 拿到上层数据直接添加个UDP 首部,然后进行校验后就递交给 IP 层而接收的一方在接收到UDP 报文后简单进行校验,然后直接去除数据递交给上层应用;
- 速度快,因为UDP 协议没有TCP 协议的握手、确认、窗口、重传、拥塞控制等机制,UDP 是一个无状态的传输协议,所以它在传递数据时非常快,即使在网络拥塞的时候UDP 也不会降低发送的数据。 UDP 虽然有很多缺点,但也有自己的优点,所以它也有很多的应用场合,因为在如今的网络环境下, UDP 协议传输出现错误的概率是很小的,并且它的实时性是非常好,常用于实时视频的传输,比如直播、网络电话等,因为即使是出现了数据丢失的情况,导致视频卡帧,这也不是什么大不了的事情,所以,UDP协议还是会被应用与对传输速度有要求,并且可以容忍出现差错的数据传输中。
在Linux使用socket网络编程实现udp通信流程如下:
1. 初始化socket
int sock_fd = socket(AF_INET , SOCK_DGRAM , 0); if(sock_fd < 0){ perror("failed to open socket"); return -1; }
2. 绑定IP和端口号
/** 绑定IP和端口号 */ struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; // 本地任意IP server_addr.sin_port = htons(8888); // 指定端口号 int ret = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if(ret < 0) { perror("failed to bind"); close(sock_fd); return -1; }
3. 设置组播接收(可选)
std::string multi_addr = "224.0.0.10"; struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(multi_addr.c_str()); mreq.imr_interface.s_addr = INADDR_ANY; ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)); if (0 >ret) { perror("set socket multicast error"); return false; }
4. 设置接收超时(可选)
/** 设置接收超时(可选) */ int mill_sec = 2000; // 毫秒 struct timeval time_out; time_out.tv_sec = mill_sec / 1000; time_out.tv_usec = (mill_sec- time_out.tv_sec * 1000) * 1000; ret = setsockopt(sock_fd, SOL_SOCKET,SO_RCVTIMEO,&time_out,sizeof (timeval)); if(ret < 0) { perror("udp setTimeOut error!"); }
5. 发送数据
unsigned char buf[1024]; std::string ip = "192.168.1.10"; int port = 1234; struct sockaddr_in client{}; memset(&client, 0, sizeof(client)); client.sin_addr.s_addr = inet_addr(ip.c_str()); client.sin_family = AF_INET; client.sin_port = htons(port); ret = sendto(sock_fd, buf, sizeof(buf), 0, reinterpret_cast<struct sockaddr *>(&client), sizeof(struct sockaddr)); if(ret < 0){ perror("udpServer send error!"); }else{ std::cout << "send success!" << std::endl; }
6. 接收数据
unsigned char buffer[1024]; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); while(true) { memset(buffer, 0, sizeof(buffer)); ssize_t len = recvfrom(sock_fd_, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr *>(&addr), &addr_len); if(len > 0) { std::cout << "received message len : " << len << std::endl; }else{ perror("recv error"); } }
7. 完整代码
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <netdb.h> #include <net/if.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <iostream> #include <cstring> #include <thread> void recv_func(int sock_fd_) { unsigned char buffer[1024]; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); while(true) { memset(buffer, 0, sizeof(buffer)); ssize_t len = recvfrom(sock_fd_, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr *>(&addr), &addr_len); if(len > 0) { std::cout << "received message len : " << len << std::endl; }else{ perror("recv error"); } } } int main(int agrc, char** argv) { int sock_fd = socket(AF_INET , SOCK_DGRAM , 0); if(sock_fd < 0){ perror("failed to open socket"); return -1; } /** 绑定IP和端口号 */ struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; // 本地任意IP server_addr.sin_port = htons(8888); // 指定端口号 int ret = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if(ret < 0) { perror("failed to bind"); close(sock_fd); return -1; } /** 设置组播接收 (可选)*/ std::string multi_addr = "224.0.0.10"; struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(multi_addr.c_str()); mreq.imr_interface.s_addr = INADDR_ANY; ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)); if (0 >ret) { perror("set socket multicast error"); return false; } /** 设置接收超时(可选) */ int mill_sec = 2000; // 毫秒 struct timeval time_out; time_out.tv_sec = mill_sec / 1000; time_out.tv_usec = (mill_sec- time_out.tv_sec * 1000) * 1000; ret = setsockopt(sock_fd, SOL_SOCKET,SO_RCVTIMEO,&time_out,sizeof (timeval)); if(ret < 0) { perror("udp setTimeOut error!"); } /** 开启线程接收 */ std::thread recv_t(recv_func, sock_fd); recv_t.detach(); /** 主线程发送 */ while (true) { unsigned char buf[1024]; std::string ip = "192.168.1.10"; int port = 1234; struct sockaddr_in client{}; memset(&client, 0, sizeof(client)); client.sin_addr.s_addr = inet_addr(ip.c_str()); client.sin_family = AF_INET; client.sin_port = htons(port); ret = sendto(sock_fd, buf, sizeof(buf), 0, reinterpret_cast<struct sockaddr *>(&client), sizeof(struct sockaddr)); if(ret < 0){ perror("udpServer send error!"); }else{ std::cout << "send success!" << std::endl; } usleep(50*1000); } }
8. 编译运行
# 编译 g++ udp_main.cpp -o main -lpthread # 运行 ./main
以上就是Linux下使用C/C++进行UDP网络编程详解的详细内容,更多关于C++ UDP网络编程的资料请关注脚本之家其它相关文章!