3

我必须从 15 个不同的客户端接收数据,每个客户端都在 5 个不同的端口上发送。总共15 * 5个插座。

为每个客户端端口号定义和固定。例如客户端 1,端口 3001 到 3005。客户端 2,端口 3051 到 3055 等。它们有一个共同点,即第一个端口(3001,3051)用于发送命令。其他端口发送一些数据。

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

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

  • 使用 select() 进行异步多路复用。
  • 每个套接字的线程。

我尝试了第一个,但当我获取数据时,我被困在了这一点上。我能够接收数据。我有一些处理要做,所以我想为每个套接字(或)启动一个线程,让套接字处理(比如所有第一个端口,所有第二个端口等 ..ie3001,3051 等)但是在这里,如果客户端发送任何数据,则 FD_ISSET 变为是的,所以如果我启动一个线程,那么它就会成为每条消息的线程。 问题: 如何在此处添加线程代码,假设我在 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
4

3 回答 3

0

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

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

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

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

于 2013-04-07T18:57:24.633 回答
0

在程序启动时创建线程并将它们划分为处理数据、命令等

如何?

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.

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

于 2013-04-07T10:09:32.413 回答
0

我假设每个客户端上下文都独立于其他上下文,即。一个客户端socket组可以单独管理,从socket中拉取的数据可以单独处理。

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

  1. 异步多路复用:在此设置中,套接字都由一个线程管理。这个线程selects 下一个必须读取哪个套接字,并从中拉出数据

  2. 每个套接字的线程:在这种情况下,您拥有与套接字一样多的线程,或者更可能是一组套接字,即。客户 - 这是我将建立的解释。

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

除了需要完成的工作之外,您还需要处理线程管理:

  • 线程如何启动?
  • 他们如何以及何时停止?
  • 错误处理策略是什么?

您的问题未涵盖这些问题,但它们可能在您的最终设计中发挥重要作用。

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

场景 (1) 需要不同的设置:一个 I/O 线程读取所有数据包并将它们传递给专门的工作线程进行处理。如果发生处理错误,工作线程将不得不生成临时数据包发送给客户端,并将其传递给 I/O 线程进行发送。您将需要两种方式的数据包队列来允许 I/O 和工作人员之间的通信,并让 I/O 线程以某种方式检查工作人员队列以获取重新发送请求。所以这个解决方案在开发方面有点贵,但是将 I/O 争用减少到一个点。它也更加灵活,以防必须对来自多个客户端的数据进行某些处理,或者如果您想以某种方式链接处理。例如,您可以改为每个客户端套接字有一个线程,

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

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

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

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

  • 如果它把套接字传回来,IO 线程是否应该等待所有工作人员在启动另一个套接字之前返回他们的套接字select?如果它等待,它会冒使未服务的客户端等待已经在网络缓冲区中的数据包的风险,从而导致处理延迟。如果它不等待,并返回以select避免延迟未服务的套接字,那么服务的套接字将不得不等待下一次唤醒才能看到它们的套接字回到select池中。

如您所见,这个问题很难处理。这就是为什么我建议线程独占套接字所有权的原因,如场景 (1) 和 (2) 中所述。

于 2013-04-07T12:13:23.683 回答