https://blog.youkuaiyun.com/rabbitjerry/article/details/72830004
来源:
之前描述了Qt中编写UDP收发程序的丢包问题,
见http://blog.youkuaiyun.com/rabbitjerry/article/details/72674458
后来终于得到了彻底解决,并且在Windows操作系统和Linux操作系统下均得到了验证。
一、解决思路
1.在程序中利用QThread类开辟一个用来接收UDP包的新线程;
2.在Windows操作系统下使用Windows封装的Socket,在Linux下使用Linux的Socket,摒弃了Qt的QSocket;
3.在新线程中使用while死循环,并采用Socket默认的阻塞模式接收数据;
4.为了避免维护多个程序,使用宏控制是使用Windows的Socket还是Linux的Socket,在不同的环境下更改宏定义后重新编译即可,便于使用和维护。
由此一来,不再丢包,且CPU占用率也较低(因为采用了阻塞模式)。
二、核心代码
1.宏定义和头文件的引用
- <span style="font-size:14px;">#define _WIN_SOCKET_ 1
- #define _QT_SOCKET_ 0
- #define _LINUX_SOCKET_ 0
- #if _WIN_SOCKET_ // for windows OS
- #include <stdio.h>
- #include <winsock2.h>
- #endif
- #if _LINUX_SOCKET_ // for Linux OS
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #endif</span>
2.头文件中相关代码
- #if _QT_SOCKET_
- private:
- QUdpSocket * p_echo_socket;
- #endif
- #if _WIN_SOCKET_ // for windows OS
- private:
- WSADATA wsaData;
- WORD sockVersion;
- SOCKET echo_socket_WIN;
- sockaddr_in addr_WIN;
- sockaddr_in src_addr_WIN;
- int src_addr_len = sizeof(src_addr_WIN);
- #endif
- #if _LINUX_SOCKET_
- private:
- int socket_len;
- int socket_descriptor;
- struct sockaddr_in echo_socket_LINUX;
- #endif
3. 构造函数中与socket相关的内容
- /************* socket **************/
- #if _QT_SOCKET_
- p_echo_socket = new QUdpSocket(this);
- p_echo_socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 4*1024*1024);//设置缓存
- if(!p_echo_socket->bind(DRY_ECHO_NET_PORT)) // 端口绑定
- {
- qDebug()<<"BIND failed for receiving echo port.";
- }
- #endif
- #if _WIN_SOCKET_ // for windows OS
- sockVersion = MAKEWORD(2,2);
- if(WSAStartup(sockVersion, &wsaData) != 0)
- {
- printf("winsock initialization FAILED.");
- }
- echo_socket_WIN = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if(echo_socket_WIN == INVALID_SOCKET)
- {
- printf("winsocket error !");
- }
- addr_WIN.sin_family = AF_INET;
- addr_WIN.sin_port = htons(DRY_ECHO_NET_PORT);
- addr_WIN.sin_addr.S_un.S_addr = INADDR_ANY;
- if(bind(echo_socket_WIN, (sockaddr *)&addr_WIN, sizeof(addr_WIN)) == SOCKET_ERROR)
- {
- printf("bind error !");
- closesocket(echo_socket_WIN);
- }
- // set socket buffer size
- int optVal = 0;
- int optLen = sizeof(optVal);
- optVal = 4*1024*1024;
- setsockopt(echo_socket_WIN, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, optLen);
- #endif
- #if _LINUX_SOCKET_
- bzero(&echo_socket_LINUX,sizeof(echo_socket_LINUX));
- echo_socket_LINUX.sin_family=AF_INET;
- echo_socket_LINUX.sin_addr.s_addr=htonl(INADDR_ANY);
- echo_socket_LINUX.sin_port=htons(DRY_ECHO_NET_PORT);
- socket_len=sizeof(echo_socket_LINUX);
- socket_descriptor=socket(AF_INET,SOCK_DGRAM,0);
- bind(socket_descriptor,(struct sockaddr *)&echo_socket_LINUX,sizeof(echo_socket_LINUX));
- int buffer_size_LINUX=0;
- socklen_t optlen_LINUX;
- optlen_LINUX = sizeof(buffer_size_LINUX);
- getsockopt(socket_descriptor,SOL_SOCKET,SO_RCVBUF,&buffer_size_LINUX,&optlen_LINUX);
- buffer_size_LINUX = 4*1024*1024;
- if(setsockopt(socket_descriptor, SOL_SOCKET, SO_RCVBUF, &buffer_size_LINUX, optlen_LINUX) < 0)
- {
- qDebug()<<"set recv buffer size FAILED.";
- }
- getsockopt(socket_descriptor,SOL_SOCKET,SO_RCVBUF,&buffer_size_LINUX,&optlen_LINUX);
- #endif
4. 接收数据函数中与socket有关的代码
- while(1)
- {
- net_pack_size = 0;
- #if _QT_SOCKET_
- if( p_echo_socket->hasPendingDatagrams()) // 有数据
- {
- net_pack_size = p_echo_socket->pendingDatagramSize();
- p_echo_socket->readDatagram((char*)p_echo_net_pack,net_pack_size);
- }
- #endif
- #if _WIN_SOCKET_
- net_pack_size = recvfrom(echo_socket_WIN, (char*)p_echo_net_pack, 1600, 0, (sockaddr *)&src_addr_WIN, &src_addr_len);
- #endif
- #if _LINUX_SOCKET_
- net_pack_size = recvfrom(socket_descriptor,(char*)p_echo_net_pack,1600,0,(struct sockaddr *)&echo_socket_LINUX,(socklen_t*)&socket_len);
- #endif
- ...
三、注意事项
1.pro文件中在Windows操作系统下要添加如下库,但在Linux系统下则要注释掉该行代码
LIBS+=-lpthreadlibwsock32libws2_32#forwindowsOS
2.Ubuntu操作系统下,设置缓存大小的上限受到操作系统中某个文件的限制,此时需要手动修改默认的接收缓存最大值:
打开/proc/sys/net/core/rmem_max:改为4194304
Ubuntu 16.0默认是212992
[20170601]