简体   繁体   中英

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

I'm implementing server-client between 2 computers, the idea is to read inputs from connected slave to PC3 and to send it to PC4 and also the way back (inputs from connected slave P4 and send it to PC3).

the side of server:

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);
}


This side if working good... it can send and receive ...

now the client side where the problem happens.... say i comment/delete recvfrom in this side of client(PC3)..all works fine (also server side recvfrom and sendto works and i get data and can send data) when trying to receive (using the recvfrom function again in Client) data are exchanged 4 times and then both client and server do nothing anymore -> I'm going in deadlock and both are BLOCKED IN 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);
}





my questions:

1-Am i doing something wrong here?

2-is there any work-around to overcome this problem in easier way? i was reading about select but i dont know it it works when im using 2 PCs with different Threads

3- im trying since days with flags like MSG_DONTWAIT , tried to use setsockopt() ...

4- is there is a way to really debug this situation?

I would be really thankful for your answers

The programm is really big...those are the parts for UDP implmentaion... in is written in C/C++ and all other sections works perfect, the problem is only to know how to make UDP stop blocking at recvfrom

  1. Am i doing something wrong here?

You seem to have a bunch of race conditions and misconceptions about UDP.

If the server sends a datagram to the client, when the client doesn't have the socket bound - the datagram is just lost. It isn't queued up until later. You have the same problem on the reverse path.

If both server & client sockets are kept open, at least datagrams will be queued until their buffers fill up. This gives you a pretty good chance to read a message before it is lost for ever.

Even with this though, blocking recvfrom is certainly a mistake here. A datagram could simply be lost due to network congestion, and hanging indefinitely is not a good response to that.

  1. Is there any workaround to overcome this problem in easier way? i was reading about select but i dont know it it works when im using 2 PCs with different Threads

If you don't know enough to handle this with non-blocking UDP (or quite reasonably don't want to spend the time required), just use TCP, and remember to set TCP_NODELAY .

  1. I'm trying since days with flags like MSG_DONTWAIT , tried to use setsockopt()

Try to find a second hand copy of TCP/IP Illustrated Vol.1 , if possible the older Stevens version (it's missing the newer updates, but covers the basics just fine and the sequence diagrams are better IIRC). Figuring this stuff by trial and error is painful.

  1. is there is a way to really debug this situation?
  • Run wireshark or tcpdump or some other packet capture tool
  • Run your client & server under strace and make sure all the socket syscalls are shown, with accurate (and hopefully well-synchronized) timestamps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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