QT中UDPSocket丢包问题(续)

来源:互联网 发布:odis工程师刷低层数据 编辑:程序博客网 时间:2024/06/02 05:21

之前描述了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 <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

2.头文件中相关代码

#if _QT_SOCKET_private:    QUdpSocket * p_echo_socket;#endif#if _WIN_SOCKET_  // for windows OSprivate:    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]

原创粉丝点击