首页 文章

UDP多个套接字有效地接收数据和进程 - C&Linux

提问于
浏览
3

我必须从15个不同的客户端接收数据,每个客户端在5个不同的端口上发送 . 完全15 * 5插座 .

对于每个客户端端口,没有定义和修复 . 示例客户端1,端口3001至3005,客户端2,端口3051至3055等 . 它们有一个共同点,即第一个端口(3001,3051)用于发送命令 . 其他端口发送一些数据 .

收到数据后,我必须检查校验和 . 跟踪recvd数据包,如果丢失则重新请求数据包,还必须写入硬盘上的文件 .

Restriction 我无法更改上述设计,我无法从UDP更改为TCP . 阅读后我知道的两种方法是

  • 使用select()进行异步多路复用 .

  • 每个插槽的线程 .

我尝试了第一个,我能够接收数据 . 我有一些处理要这样做我想为每个套接字(或)启动一个线程,以便套接字处理(比如所有第一个端口,所有第二个,等等.ie3001,3051等)但是如果客户端发送任何数据,那么FD_ISSET变为真,所以如果我开始一个线程,那么它就成了每个消息的线程 . Question: 如何在这里添加线程代码,如果我在if(FD_ISSET ..)中包含pthread_create然后为我收到的每条消息创建一个线程 . 但我想要每个插槽一个线程 .

while(1)
   {
      int nready=0;
      read_set = active_set;

      if((nready = select(fdmax+1,&read_set,NULL,NULL,NULL)) == -1)
      {
        printf("Select Errpr\n");
        perror("select");
        exit(EXIT_FAILURE);
      }
      printf("number of ready desc=%d\n",nready);

      for(index=1;index <= 15*5;index++)
      {
         if(FD_ISSET(sock_fd[index],&read_fd_set))
         {              
           rc = recvfrom(sock_fd[index],clientmsgInfo,MSG_SIZE,0,
                    (struct sockaddr *)&client_sockaddr_in,
                      &sockaddr_in_length);
           if(rc < 0)
               printf("socket %d down\n",sock_fd[index]);

           printf("Recieved packet from %s: %d\nData: %s\n\n", inet_ntoa(client_sockaddr_in.sin_addr), ntohs(client_sockaddr_in.sin_port), recv_client_message);                                      
                }
         } //for
     } //while

3 回答

  • 0

    在程序启动时创建线程并将它们分成处理数据,命令e.t.c.

    怎么样?

    1. lets say you created 2 threads, one for data and another for the commands.
    2. make them sleep in the thread handler or let them wait on a lock that the main thread
       acquired, seems to be that mainthread got two locks one for each of them.
    3. when any client data or command that got into the recvfrom at mainthread, depending on the 
       type of the buffer(data, commands), copy the buffer into the shared data by mainthread and 
       other threads and unlock the mutex. 
    4. at threads lock the mutex so that mainthread wont' corrupt the data and once processing is 
       done at the threads unlock and sleep.
    

    更好的方法是拥有一个队列,该队列由主线程填充,并且可以由其他线程以元素方式访问 .

  • 0

    我假设每个客户端上下文独立于其他客户端,即 . 一个客户端套接字组可以单独管理,从套接字中提取的数据可以单独处理 .

    您表达了处理问题的两种可能性:

    • 异步多路复用:在此设置中,套接字全部由一个线程管理 . 这线程 select s接下来必须读取哪个套接字,并从中拉出数据

    • 每个套接字的线程数:在这种情况下,你有多少个线程和套接字一样多,或者更多可能是套接字组,即 . 客户 - 这将是我将构建的解释 .

    在这两种情况下,线程必须保持其各自资源的所有权,即插槽 . 如果你开始在线程之间移动套接字,你将使它变得更加困难 .

    在需要完成的工作之外,您将需要处理线程管理:

    • 线程如何开始?

    • 他们如何以及何时停止?

    • 什么是错误处理政策?

    您的问题不包括这些问题,但它们可能会在您的最终设计中发挥重要作用 .

    场景(2)似乎更简单:你有一个主"template"(我在这里使用一般意义上的单词)来处理一组使用 select 的套接字,并在同一个线程中接收和处理数据 . 实现起来非常简单,使用包含上下文特定数据的结构(套接字端口,用于数据包处理的函数的指针),以及在select和process上循环的单个函数,以及可能的其他一些错误检查和线程生命管理 .

    场景(1)需要不同的设置:一个I / O线程读取所有数据包并将它们传递给专用工作线程进行处理 . 如果发生处理错误,工作线程必须生成要发送到客户端的adhoc数据包,并将其传递给I / O线程进行发送 . 您将需要数据包队列以允许I / O和工作程序之间的通信,并让I / O线程以某种方式检查工作队列以获得重新发送请求 . 因此,这种解决方案在开发方面要贵一些,但将I / O争用降低到一个单点 . 它也更灵活,以防必须对来自多个客户端的数据进行某些处理,或者如果您想以某种方式链接处理 . 例如,您可以在每个客户端套接字中使用一个线程,然后在每个客户端套接字的另一个线程中进一步向下工作管道,管道的每个步骤通过数据包队列互连 .

    当然可以实现两种解决方案的混合,每个客户端有一个IO线程,以及流水线工作线程 .

    两个概述的解决方案的优点是固定数量的线程:无需按需生成和销毁线程(尽管您也可以设计一个线程池来处理它) .

    对于涉及移动插座的解决方案在线程之间,问题是:

    • 什么时候应该传递这些资源?工作线程读取数据包后会发生什么?它是应该将套接字返回到IO线程,还是冒着在套接字上阻塞读取下一个数据包的风险?如果它使用 select 轮询套接字以获取更多数据包,我们将陷入方案(2),其中每个客户端都有自己的I / O线程,当有来自所有客户端的网络流量时,在这种情况下,增益是多少执行 select 的初始I / O线程?

    • 如果它再次通过套接字,IO线程是否应该等待所有工作者在启动另一个 select 之前回放其套接字?如果它等待,则需要使未服务的客户端等待已经在网络缓冲区中的数据包的风险,从而导致处理延迟 . 如果它没有等待,并返回到 select 以避免延迟未服务的套接字,那么服务的服务器将不得不等待下一次唤醒,以便在 select 池中看到它们的套接字 .

    如您所见,问题很难处理 . 这就是为什么我推荐使用线程的独占套接字所有权的原因,如scenarii(1)和(2)中所述 .

  • 0

    您的解决方案需要固定的,相对较少的连接数 .

    创建一个帮助过程,创建线程过程,监听五个端口中的每个端口并阻塞 recvfrom() ,处理数据,然后再次阻止 . 然后,您可以调用帮助程序15次以创建线程 .

    这样可以避免所有轮询,并允许Linux在I / O完成时安排每个线程 . 等待时不使用CPU,这可以扩展到更大的解决方案 .

    如果需要大规模扩展,为什么不使用一组端口,并从 client_sockaddr_in 结构中获取合作伙伴地址 . 如果处理花费了大量时间,您可以通过保持一个可用线程池来扩展它,并在每次收到消息时分配一个新线程,然后继续处理该消息,并在响应后将该线程添加回池中已发送 .

相关问题