簡體   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