QT中UDPSocket丢包问题(续)

之前描述了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.宏定义和头文件的引用

#define _WIN_SOCKET_   1
#define _QT_SOCKET_    0
#define _LINUX_SOCKET_ 0

#if _WIN_SOCKET_   // for windows OS 
#include 
#include 
#endif

#if _LINUX_SOCKET_  // for Linux OS
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#endif

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]

你可能感兴趣的:(QT)