QT中UDPSocket丢包问题(续)


https://blog.csdn.net/rabbitjerry/article/details/72830004


来源:

之前描述了Qt中编写UDP收发程序的丢包问题,

http://blog.csdn.net/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.宏定义和头文件的引用

  1. <span style="font-size:14px;">#define _WIN_SOCKET_   1  
  2. #define _QT_SOCKET_    0  
  3. #define _LINUX_SOCKET_ 0  
  4.   
  5. #if _WIN_SOCKET_   // for windows OS   
  6. #include <stdio.h>  
  7. #include <winsock2.h>  
  8. #endif  
  9.   
  10. #if _LINUX_SOCKET_  // for Linux OS  
  11. #include <stdio.h>  
  12. #include <stdlib.h>  
  13. #include <string.h>  
  14. #include <sys/socket.h>  
  15. #include <netinet/in.h>  
  16. #include <arpa/inet.h>  
  17. #include <netdb.h>  
  18. #endif</span>  

2.头文件中相关代码

  1. #if _QT_SOCKET_  
  2. private:  
  3.     QUdpSocket * p_echo_socket;  
  4. #endif  
  5.   
  6. #if _WIN_SOCKET_  // for windows OS  
  7. private:  
  8.     WSADATA wsaData;  
  9.     WORD sockVersion;  
  10.     SOCKET echo_socket_WIN;  
  11.     sockaddr_in addr_WIN;  
  12.     sockaddr_in src_addr_WIN;  
  13.     int src_addr_len = sizeof(src_addr_WIN);  
  14. #endif  
  15.   
  16. #if _LINUX_SOCKET_  
  17. private:  
  18.     int socket_len;  
  19.     int socket_descriptor;  
  20.     struct sockaddr_in echo_socket_LINUX;  
  21. #endif  

3. 构造函数中与socket相关的内容

  1.     /************* socket **************/  
  2. #if _QT_SOCKET_     
  3.     p_echo_socket = new QUdpSocket(this);  
  4.     p_echo_socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 4*1024*1024);//设置缓存  
  5.     if(!p_echo_socket->bind(DRY_ECHO_NET_PORT))  // 端口绑定  
  6.     {  
  7.         qDebug()<<"BIND failed for receiving echo port.";  
  8.     }  
  9. #endif  
  10.   
  11. #if _WIN_SOCKET_  // for windows OS  
  12.     sockVersion = MAKEWORD(2,2);  
  13.     if(WSAStartup(sockVersion, &wsaData) != 0)  
  14.     {  
  15.         printf("winsock initialization FAILED.");  
  16.     }  
  17.     echo_socket_WIN = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  
  18.     if(echo_socket_WIN == INVALID_SOCKET)  
  19.     {  
  20.         printf("winsocket error !");  
  21.     }  
  22.     addr_WIN.sin_family = AF_INET;  
  23.     addr_WIN.sin_port = htons(DRY_ECHO_NET_PORT);  
  24.     addr_WIN.sin_addr.S_un.S_addr = INADDR_ANY;  
  25.     if(bind(echo_socket_WIN, (sockaddr *)&addr_WIN, sizeof(addr_WIN)) == SOCKET_ERROR)  
  26.     {  
  27.         printf("bind error !");  
  28.         closesocket(echo_socket_WIN);  
  29.     }  
  30.     // set socket buffer size  
  31.     int optVal = 0;  
  32.     int optLen = sizeof(optVal);  
  33.     optVal = 4*1024*1024;  
  34.     setsockopt(echo_socket_WIN, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, optLen);  
  35. #endif  
  36.   
  37. #if _LINUX_SOCKET_  
  38.     bzero(&echo_socket_LINUX,sizeof(echo_socket_LINUX));  
  39.     echo_socket_LINUX.sin_family=AF_INET;  
  40.     echo_socket_LINUX.sin_addr.s_addr=htonl(INADDR_ANY);  
  41.     echo_socket_LINUX.sin_port=htons(DRY_ECHO_NET_PORT);  
  42.     socket_len=sizeof(echo_socket_LINUX);  
  43.     socket_descriptor=socket(AF_INET,SOCK_DGRAM,0);  
  44.     bind(socket_descriptor,(struct sockaddr *)&echo_socket_LINUX,sizeof(echo_socket_LINUX));  
  45.     int buffer_size_LINUX=0;  
  46.     socklen_t optlen_LINUX;  
  47.     optlen_LINUX = sizeof(buffer_size_LINUX);  
  48.     getsockopt(socket_descriptor,SOL_SOCKET,SO_RCVBUF,&buffer_size_LINUX,&optlen_LINUX);  
  49.     buffer_size_LINUX = 4*1024*1024;  
  50.     if(setsockopt(socket_descriptor, SOL_SOCKET, SO_RCVBUF, &buffer_size_LINUX, optlen_LINUX) < 0)  
  51.     {  
  52.         qDebug()<<"set recv buffer size FAILED.";  
  53.     }  
  54.     getsockopt(socket_descriptor,SOL_SOCKET,SO_RCVBUF,&buffer_size_LINUX,&optlen_LINUX);  
  55. #endif  

4. 接收数据函数中与socket有关的代码

  1.     while(1)  
  2.     {  
  3.         net_pack_size = 0;  
  4.   
  5. #if _QT_SOCKET_  
  6.         if( p_echo_socket->hasPendingDatagrams())  // 有数据  
  7.         {  
  8.             net_pack_size = p_echo_socket->pendingDatagramSize();  
  9.             p_echo_socket->readDatagram((char*)p_echo_net_pack,net_pack_size);  
  10.         }  
  11. #endif  
  12.   
  13. #if _WIN_SOCKET_  
  14.         net_pack_size = recvfrom(echo_socket_WIN, (char*)p_echo_net_pack, 1600, 0, (sockaddr *)&src_addr_WIN, &src_addr_len);  
  15. #endif  
  16.   
  17. #if _LINUX_SOCKET_  
  18.         net_pack_size = recvfrom(socket_descriptor,(char*)p_echo_net_pack,1600,0,(struct sockaddr *)&echo_socket_LINUX,(socklen_t*)&socket_len);  
  19. #endif  
  20. ...  

三、注意事项

1.pro文件中在Windows操作系统下要添加如下库,但在Linux系统下则要注释掉该行代码

LIBS+=-lpthreadlibwsock32libws2_32#forwindowsOS

2.Ubuntu操作系统下,设置缓存大小的上限受到操作系统中某个文件的限制,此时需要手动修改默认的接收缓存最大值:
打开/proc/sys/net/core/rmem_max:改为4194304
Ubuntu 16.0默认是212992

[20170601]





阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭