繁体   English   中英

在 C linux 中使用带有 UDP 服务器的线程时,如何阻止 recvfrom() 阻塞?

[英]How to stop recvfrom() from blocking while using in threads with UDP server in C linux?

我在两台计算机之间实现服务器-客户端,想法是从连接的从机读取输入到 PC3 并将其发送到 PC4 以及返回的方式(来自连接的从机 P4 的输入并将其发送到 PC3)。

服务器端:

client:
static void FSoEThread()
{
   struct timeval time1;

   uint8_t  pdu_in_local[sizeof(pdu_in)];
   uint8_t  pdu_out_local[sizeof(pdu_out)];

   uint8_t  pdu_in_tmp[sizeof(pdu_in)];
   uint8_t  pdu_out_tmp[sizeof(pdu_out)];

   FILE *fp;
   fp = fopen("Log.txt", "w");//opening file.
   SerialPort sp(MODEMDEVICE);
   
    int socket_desc;
    struct sockaddr_in server_addr;
    unsigned int  server_message[255], client_message[255];
    socklen_t server_struct_length = sizeof(server_addr);
;

   printf("Thread_quit\n");

     while(!thread_quit){

    
        std::vector<uint8_t> uart_in(10);
        sp.Read(uart_in);

       if(1){
        switch(ecatGetMasterState())
        {
        default:

            break;
        case _UNKNOWN:
        case _INIT:
        case _PREOP:
        case _SAFEOP:
            break;
        case eEcatState_OP:
        {

            {
                uint8_t buf[sizeof(pdu_in)]; // In data comes from Slave
                if(uart_in.size() == 10){
                    PD_mutex.lock();
                    memcpy(pdu_out,&uart_in.data()[4],sizeof(pdu_out));//out data coming from Master to the Slave
                    memcpy(pdu_out_local,&uart_in.data()[4],sizeof(pdu_out));

                    memcpy(buf,pdu_in,sizeof(pdu_in));
                    memcpy(pdu_in_local,pdu_in,sizeof(pdu_in));

                    PD_mutex.unlock();
                        printf("\n");
                    sp.Write(buf,sizeof(pdu_in));
        }
        }
            

            gettimeofday(&time1,NULL);

            if(memcmp(pdu_in_tmp,pdu_in_local,sizeof(pdu_in)) != 0){
                memcpy(pdu_in_tmp,pdu_in_local,sizeof(pdu_in));

                fprintf(fp,"[%05d.%06d]in  %02x %02x %02x %02x %02x %02x\n",time1.tv_sec,time1.tv_usec,
                        (unsigned int)pdu_in_tmp[0],
                        (unsigned int)pdu_in_tmp[1],
                        (unsigned int)pdu_in_tmp[2],
                        (unsigned int)pdu_in_tmp[3],
                        (unsigned int)pdu_in_tmp[4],
                    (unsigned int)pdu_in_tmp[5]
                );
            unsigned int *PDU_UDP_IN = (unsigned int*)pdu_in_tmp;  
            // Create socket:
            socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if(socket_desc < 0){
            printf("Error while creating socket\n");
            exit(1);
            }

            // Clean buffers:
            memset(server_message, 0, sizeof(server_message));
            memset(client_message, 0, sizeof(client_message));


            // Set port and IP:
            server_addr.sin_family = AF_INET;
            server_addr.sin_port = htons(2000);
            server_addr.sin_addr.s_addr = inet_addr("10.100.20.54");

            printf("sending\n");
            // Send the message to server:
            if(sendto(socket_desc, PDU_UDP_IN, sizeof(PDU_UDP_IN), 0,
             (struct sockaddr*)&server_addr, server_struct_length) < 0){
            printf("Unable to send message\n");
            exit(1);
            }

            printf("sendto: msg send to IPC4 :\t%x\n",*PDU_UDP_IN);    

                socklen_t server_struct_length = sizeof(server_addr);
            
                
            //recvfrom(socket_desc, server_message, sizeof(server_message), 0,(struct sockaddr*)&server_addr, &server_struct_length);
            //if(int errn_ = recvfrom(socket_desc, server_message, sizeof(server_message), 0,(struct sockaddr*)&server_addr, &server_struct_length) < 0){
            //printf("Error while receiving server's msg\n");
            //printf("n=%d, Errno%d\n",errn_,errno);
            //exit(1);
            //}
            printf("Recved\n");
            printf("MSG from IPC4:\t%x\n",*server_message);    
            close(socket_desc);
            }

            if(memcmp(pdu_out_tmp,pdu_out_local,sizeof(pdu_out)) != 0){
                memcpy(pdu_out_tmp,pdu_out_local,sizeof(pdu_out));
    
                fprintf(fp,"[%05d.%06d]out %02x %02x %02x %02x %02x %02x\n",time1.tv_sec,time1.tv_usec,
                        (unsigned int)pdu_out_tmp[0],
                        (unsigned int)pdu_out_tmp[1],
                        (unsigned int)pdu_out_tmp[2],
                        (unsigned int)pdu_out_tmp[3],
                        (unsigned int)pdu_out_tmp[4],
                        (unsigned int)pdu_out_tmp[5]
                );
    }


        }
    }
        }
     }


   pthread_exit(NULL);
}


这面如果工作得好……它可以发送和接收……

现在发生问题的客户端....说我在客户端(PC3)的这一侧评论/删除recvfrom ..所有工作正常(服务器端recvfrom和sendto也可以工作,我获取数据并可以发送数据)尝试时接收(在客户端再次使用 recvfrom 函数)数据交换 4 次,然后客户端和服务器都不再执行任何操作 -> 我将陷入死锁,并且两者都在 RECVFROM 中被阻塞


static void FSoEThread()
{
   struct timeval time1;

   uint8_t  pdu_in_local[sizeof(pdu_in)];
   uint8_t  pdu_out_local[sizeof(pdu_out)];

   uint8_t  pdu_in_tmp[sizeof(pdu_in)];
   uint8_t  pdu_out_tmp[sizeof(pdu_out)];



    int socket_desc;
    struct sockaddr_in server_addr, client_addr;
    unsigned int server_message[255], client_message[255];
            socklen_t client_struct_length = sizeof(client_addr);


       // Clean buffers:
       memset(server_message, '\0', sizeof(server_message));
       memset(client_message, '\0', sizeof(client_message));
       FILE *fp;
       fp = fopen("Llog.txt", "w");//opening file.
       long save_fd;
    SerialPort sp(MODEMDEVICE);


   printf("thread_quit\n");

           while(!thread_quit){

        std::vector<uint8_t> uart_in(10);
        sp.Read(uart_in);

       if(1){
        switch(ecatGetMasterState())
        {
        default:
            break;
        case _UNKNOWN:
        case _INIT:
        case _PREOP:
        case _SAFEOP:
            break;
        case eEcatState_OP:    
        {
            {
                uint8_t buf[sizeof(pdu_in)];
                if(uart_in.size() == 10){
                    PD_mutex.lock();
                    memcpy(pdu_out,&uart_in.data()[4],sizeof(pdu_out));
                    memcpy(pdu_out_local,&uart_in.data()[4],sizeof(pdu_out));

                    memcpy(buf,pdu_in,sizeof(pdu_in));
                    memcpy(pdu_in_local,pdu_in,sizeof(pdu_in));
                    PD_mutex.unlock();

                    sp.Write(buf,sizeof(pdu_in));

                }
            }

            gettimeofday(&time1,NULL);

            if(memcmp(pdu_in_tmp,pdu_in_local,sizeof(pdu_in)) != 0){
                memcpy(pdu_in_tmp,pdu_in_local,sizeof(pdu_in));

                fprintf(fp,"[%05d.%06d]in  %02x %02x %02x %02x %02x %02x\n",time1.tv_sec,time1.tv_usec,
                        (unsigned int)pdu_in_tmp[0],
                        (unsigned int)pdu_in_tmp[1],
                        (unsigned int)pdu_in_tmp[2],
                        (unsigned int)pdu_in_tmp[3],
                        (unsigned int)pdu_in_tmp[4],
                        (unsigned int)pdu_in_tmp[5]
                );
            
            unsigned int * PDU_UDP_IN = (unsigned int*)pdu_in_tmp;
            printf("in \t%X\n",*PDU_UDP_IN);
                           // Create UDP socket:
            socket_desc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if(socket_desc < 0){
            printf("Error while creating socket\n");
            exit(1);
            }
            // Set port and IP:
            server_addr.sin_family = AF_INET;
            server_addr.sin_port = htons(2000);
            server_addr.sin_addr.s_addr = inet_addr("10.100.20.54");

            // Bind to the set port and IP:
            if(bind(socket_desc, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){

            exit(1);
            }

            //printf("Listening for incoming messages...\n\n");
            // Receive client's message:
            
            printf("Recving\n");
            if (recvfrom(socket_desc, client_message, sizeof(client_message), 0,(struct sockaddr*)&client_addr, &client_struct_length) < 0){
            //printf("Couldn't receive\n");
            //continue;
            }
            printf("Received\n");
            //     inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

            printf("msg from IPC3:\t%x\n", *client_message);

            // Respond to client:
            
            printf(" sending\n");
            // strcpy(server_message, client_message);
            socklen_t client_struct_length = sizeof(client_addr);
            if (sendto(socket_desc, PDU_UDP_IN, sizeof(PDU_UDP_IN), 0,
             (struct sockaddr*)&client_addr, client_struct_length) < 0){
            printf("Can't send\n");
            exit(EXIT_FAILURE);
            }
            printf(" sent\n");
            // Close the socket:
            close(socket_desc);
            }

            if(memcmp(pdu_out_tmp,pdu_out_local,sizeof(pdu_out)) != 0){
                memcpy(pdu_out_tmp,pdu_out_local,sizeof(pdu_out));

                fprintf(fp,"[%05d.%06d]out %02x %02x %02x %02x %02x %02x\n",time1.tv_sec,time1.tv_usec,
                        (unsigned int)pdu_out_tmp[0],
                        (unsigned int)pdu_out_tmp[1],
                        (unsigned int)pdu_out_tmp[2],
                        (unsigned int)pdu_out_tmp[3],
                        (unsigned int)pdu_out_tmp[4],
                        (unsigned int)pdu_out_tmp[5]
                );

            }

        }
        }
        }
     }

   pthread_exit(NULL);
}





我的问题:

1-我在这里做错了吗?

2-是否有任何解决方法可以以更简单的方式克服这个问题? 我正在阅读有关 select 但我不知道它在我使用 2 台具有不同线程的 PC 时是否有效

3-我从几天开始就尝试使用 MSG_DONTWAIT 之类的标志,尝试使用 setsockopt() ...

4-有没有办法真正调试这种情况?

我会非常感谢你的回答

该程序真的很大......这些是UDP实现的部分......用C / C ++编写,所有其他部分都完美无缺,问题只是知道如何让UDP停止在recvfrom阻塞

  1. 我在这里做错了吗?

您似乎对 UDP 有很多竞争条件和误解。

如果服务器向客户端发送数据报,当客户端没有绑定套接字时——数据报就丢失了。 直到稍后才排队。 你在反向路径上有同样的问题。

如果服务器和客户端套接字都保持打开状态,则至少数据报将排队,直到它们的缓冲区填满。 这使您有很好的机会在消息永远丢失之前阅读它。

尽管如此在这里阻止recvfrom肯定是一个错误。 数据报可能由于网络拥塞而丢失,而无限期挂起并不是一个好的响应。

  1. 是否有任何解决方法可以以更简单的方式克服此问题? 我正在阅读有关 select 但我不知道它在我使用 2 台具有不同线程的 PC 时是否有效

如果您不知道如何使用非阻塞 UDP 来处理此问题(或者非常合理地不想花费所需的时间),请使用 TCP,并记住设置TCP_NODELAY

  1. 我一直在尝试使用 MSG_DONTWAIT 之类的标志,尝试使用 setsockopt()

尝试找到TCP/IP Illustrated Vol.1的二手副本,如果可能的话,找到旧的 Stevens 版本(它缺少较新的更新,但涵盖了基础知识并且序列图更好 IIRC)。 通过反复试验来弄清楚这些东西是痛苦的。

  1. 有没有办法真正调试这种情况?
  • 运行 wireshark 或 tcpdump 或其他一些数据包捕获工具
  • strace下运行您的客户端和服务器,并确保显示所有套接字系统调用,并带有准确(并且希望同步良好)的时间戳

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM