【问题标题】:How to reduce message dropping in UDP socket communication, using only single port?如何减少 UDP 套接字通信中的消息丢失,仅使用单个端口?
【发布时间】:2012-05-22 10:15:50
【问题描述】:

我有 UDP 客户端和服务器的编写器代码 sn-p。我使用相同的端口进行发送和接收。我的问题是客户端有很多消息丢失,所以有人可以帮我优化我的代码,这是我的 UDP 客户端代码:

#define SERVERIP "192.168.170.155"
#define SERVERPORT 5000
#define DEVICE_SEND_PORT 5000
#define DEVICE_RECEIVE_PORT 5000
#define BUFFERSIZE 2048

/**For socket file descriptor identification*/
#define S1READY 0x01

int m_SendSocketId;
int m_ReceiveSocketId;
int msgcount;
int socketbuffsize = 1*1024*1024;


/**
* FUNCTION NAME : waitToRead
* Implementation of select and non-blocking socket mechanism
* @param socket Socket that needs to be in select and non blocking mode
* @return Returnd the file descriptors which, returned by select function
*/
int waitToRead(int socket)
{
    fd_set fds;
    struct timeval timeout;
    int rc; // number of file descriptor returned
    int result; // result 
    int fd; // file descriptor

    fd=fcntl(socket,F_GETFL,0);
    fcntl(socket,F_SETFL,fd | O_NONBLOCK);

    // Set time limit. 
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    // Create a descriptor containing our sockets.
    FD_ZERO(&fds);
    FD_SET(socket, &fds);
    rc = select(sizeof(fds)*8, &fds, NULL, NULL, &timeout);
    if (rc==-1)
    {
        printf("[%s:%d#%s] Select Failed\n",__FILE__, __LINE__,__func__);
        return -1;
    }

    result = 0;
    if (rc > 0)
    {
        if (FD_ISSET(socket, &fds))
            result |= S1READY;
    }
    return result;
}
/**
* FUNCTION NAME : receiveMessage
* This function opens particular port that is defined in the 
* Configuration file, and listens on that port.
* @return if there'll be any issue in listening, then it will return 
* false otherwise it will return true.
*/
bool receiveMessage()
{
    struct sockaddr_in serverAddr; //Information about the Device UDP Server
    struct sockaddr_in client_addr; // Information about Qgate Server
    char buffer[BUFFERSIZE]; // Buffer to store incoming message
    int addr_len; // to store client address length 
    int serverlen; // to store server address length
    int sockResult; // to store result given by waitToRead
    int optval = 1;
    int receivedByte = 0;

    //Open a datagram Socket
    if((m_ReceiveSocketId = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("[%s:%d#%s] UDP Client - socket() error\n",__FILE__, __LINE__,__func__);
        return false;
    }

    //Configure Server Address.
    //set family and port
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(DEVICE_RECEIVE_PORT);


    setsockopt(m_ReceiveSocketId, SOL_SOCKET,SO_REUSEADDR, &optval, sizeof(optval));

    /*if (setsockopt(m_ReceiveSocketId, SOL_SOCKET, SO_RCVBUF, &socketbuffsize, sizeof(socketbuffsize)) == -1) 
    {
        printf("Recieve Socket memory Allocation fail\n");
    }*/

    if((serverAddr.sin_addr.s_addr = INADDR_ANY) == (unsigned long)INADDR_NONE)
    {

        printf("[%s:%d#%s] Host Not found(%d)\n",__FILE__, __LINE__,__func__,h_errno);
        close(m_ReceiveSocketId); // close the socket
        return false;
    }

    if (bind(m_ReceiveSocketId, (struct sockaddr *) &serverAddr,sizeof(struct sockaddr_in)) < 0 )
    {
        printf("[%s:%d#%s] UDP Client- Socket Bind error=%s\n",__FILE__, __LINE__,__func__,strerror(errno));
        close(m_ReceiveSocketId); // close the socket
        return false;
    }

    serverlen = (int )sizeof(serverAddr);
    addr_len = sizeof(struct sockaddr); 
    // Loop and listen for incoming message 
    while(1)
    {
        //wait at select to, read
        sockResult = waitToRead(m_ReceiveSocketId);

        if(sockResult == S1READY)
        {
            receivedByte = read(m_ReceiveSocketId,buffer,BUFFERSIZE);   
            buffer[receivedByte] = '\0';

            if(receivedByte == -1)
            {
                printf("[%s:%d#%s] UDP Client - receive error", __FILE__,__LINE__,__func__);
                close(m_ReceiveSocketId);
                return false;
            }
            else if(receivedByte > 0)
            {
                //printf("[%s:%d#%s] received message = %d bytes\n",__FILE__,__LINE__,__func__,(int)strlen(buffer));
                printf("count: %d, buffer %s \n", msgcount++, buffer);
            }
        }

        memset(buffer, 0, BUFFERSIZE);
        fflush(stdout);
    }

    close(m_ReceiveSocketId); // close the socket
    printf("[%s:%d#%s] Recieve socket closed:%s\n",
                            __FILE__, __LINE__,__func__, strerror(errno));
    return true;
}


bool sendMessage(char *message)
{
    struct sockaddr_in  serverAddr; //Information about the server
    struct sockaddr_in deviceAddr; //Device UDP Client Address for sending message
    int optval = 1;

    //Open a datagram Socket
    if((m_SendSocketId = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("[%s:%d#%s] UDP Client - socket() error\n",__FILE__, __LINE__,__func__);
        return false;
    }

    // Clear out the device struct
    memset(&deviceAddr, 0x00, sizeof(struct sockaddr_in));

    deviceAddr.sin_family = AF_INET;
    deviceAddr.sin_port = htons(DEVICE_SEND_PORT);

    setsockopt(m_SendSocketId, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    /*if (setsockopt(m_SendSocketId, SOL_SOCKET, SO_SNDBUF, &socketbuffsize, sizeof(socketbuffsize)) == -1) 
    {
        printf("send Socket memory Allocation fail\n");
    }*/

     if((deviceAddr.sin_addr.s_addr = INADDR_ANY) == (unsigned long)INADDR_NONE)
    {
        // in netdb.h 
        printf("[%s:%d#%s] Host Not found(%d)\n",__FILE__, __LINE__,__func__,  h_errno);
        close(m_SendSocketId); // close the socket
        return false;
    }

    if (bind(m_SendSocketId, (struct sockaddr *) &deviceAddr,sizeof(struct sockaddr_in)) < 0 )
    {
        printf("[%s:%d#%s] UDP Client- Socket Bind error=%s\n",__FILE__, __LINE__,__func__,strerror(errno));
        close(m_SendSocketId); // close the socket
        return false;
    }


    // Clear out the server struct
    memset(&serverAddr, 0x00, sizeof(struct sockaddr_in));

    //Configure Server Address.
    //set family and port
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVERPORT);
    //serverAddr.sin_addr.s_addr = htonl(39898);

    if((serverAddr.sin_addr.s_addr = inet_addr(SERVERIP)) == (unsigned long)INADDR_NONE)
    {
        printf("[%s:%d#%s] Host Not found %d\n",__FILE__, __LINE__,__func__,h_errno);
        close(m_SendSocketId);
        return false;
    }

    // Send data to the server.
    if( sendto(m_SendSocketId, message,strlen(message) ,0, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0 )
    {
        printf("[%s:%d#%s] UDP Client - sendto() error=%s \n",__FILE__, __LINE__,__func__,strerror(errno));
        close(m_SendSocketId);  
        return false;
    }

    close(m_SendSocketId);
    return true;
}

int main ()
{
    int loop;
    char str[10];
    msgcount = 1;   
    pthread_t receiveThread;

    if(pthread_create(&receiveThread, NULL,(void *)&receiveMessage, NULL) != 0)
    {
        printf("[%s:%d#%s] thread create Failed(%s)\n",
            __FILE__, __LINE__,__func__, strerror(errno));
        return false;
    }

    for(loop =0; loop < 1000; loop++)
    {
        sprintf(str,"%4d",loop);
        sendMessage(str);   
    }

    pthread_join(receiveThread, NULL);  
    return 0;
}

这里是临时 UDP 服务器代码,它接收几乎 90% 以上的消息并发送相同的消息,但是 udpclient 无法接收消息。

int main()
{
        int sock;
        int addr_len, bytes_read, bytes_send;
        char recv_data[1024];
        int i;
        int count=0;
        struct sockaddr_in server_addr , client_addr;


        if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
            perror("Socket");
            exit(1);
        }

        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(5000);
        server_addr.sin_addr.s_addr = INADDR_ANY;
        bzero(&(server_addr.sin_zero),8);

        //client_addr.sin_family = AF_INET;

        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

        if (bind(sock,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == -1)
        {
            perror("Bind");
            exit(1);
        }

        addr_len = sizeof(struct sockaddr);

        printf("\nUDPServer Waiting for client on port 5000");
        fflush(stdout);

    while (1)
    {
        bytes_read = recvfrom(sock,recv_data,1024,0,(struct sockaddr *)&client_addr, (socklen_t *)&addr_len);
        recv_data[bytes_read] = '\0';


        if(recv_data[0]!=0x07)
        {
            recv_data[0] = 0x09;
            //client_addr.sin_port = htons(51254);

            bytes_send = sendto(sock, recv_data, bytes_read, 0, (struct sockaddr *)&client_addr, (socklen_t)sizeof(client_addr));
            if(bytes_send < 0 )
            {
                perror("send to ");
            }
            printf("\nNumber  %d", ++count);
            memset(&recv_data, 0x00, 1024);

        }
        else
        {
            printf("Received Keep ALive\n");        
        }
        memset(&client_addr, 0x00, sizeof(struct sockaddr_in));

        fflush(stdout);

    }
    return 0;
}

任何帮助将不胜感激。 谢谢尤维

【问题讨论】:

  • 使用数据包嗅探器(如 WireShark)查看正在接收的数据包。如果数据包嗅探器显示的数据包数量与您的应用相同,那么您不会因为您的应用而丢失任何数据,这一定是系统问题。

标签: c linux udp udpclient


【解决方案1】:

您的代码与 UDP 丢弃数据包无关,除非您发送数据包的速度对于网络或接收器而言太快。 UDP不可靠。它丢弃数据包。如果您的应用程序协议要求不丢包,则必须通过基于 ACK 或基于 NACK 的协议重试来构建该级别的可靠性。

或者在这种情况下像其他人一样使用 TCP。

【讨论】:

  • 是的,在真正的应用程序中,ACKing 就在那里。这只是为了静态。但是用其他语言编写的程序并没有下降那么多。在我的情况下,它下降了 70-80%,但对于其他语言,它最多只有 30%。那么在我的接收相关代码中是否存在任何与性能相关的问题?
  • @Yuvi 我不知道什么是“静态”,但是您不需要所有 waitToRead() 东西,因为您处于阻塞模式。只是阻止。
  • 另一种尝试保护 UDP 连接的方法是在某个合适的时间间隔发送纠错数据包。然后您可以重新创建丢失的数据(当然,除非您丢失了太多数据包)。当 TCP/IP 处理速度有点慢但可以添加纠错包时,这是一个可行的解决方案。
【解决方案2】:

问题出在 sendMessage 函数中,这里我每次需要发送消息时都在重新创建套接字,我认为这需要时间。我还不知道哪个调用被阻塞,但是发送套接字可以解决我的问题。现在消息的丢弃率只有 20% 到 30%。

【讨论】:

  • 您当然应该使用单个套接字,但是在数据包之间花费更多时间会导致更少的数据包丢失,而不是更多。
猜你喜欢
  • 2018-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-15
相关资源
最近更新 更多