简体   繁体   English

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

[英]UDP Multiple sockets Receive data and process efficiently - C & Linux

I have to receive data from 15 different clients each of them sending on 5 different ports. 我必须从15个不同的客户端接收数据,每个客户端在5个不同的端口上发送。 totally 15 *5 sockets. 完全15 * 5插座。

for each client port no is defined and fixed. 对于每个客户端端口,没有定义和修复。 example client 1 ,ports 3001 to 3005. client 2 ,ports 3051 to 3055 etc. They have one thing in common say first port (3001 , 3051) is used to send commands. 示例客户端1,端口3001至3005,客户端2,端口3051至3055等。它们有一个共同点,即第一个端口(3001,3051)用于发送命令。 other ports send some data. 其他端口发送一些数据。

After receiving the data i have to check for checksum. 收到数据后,我必须检查校验和。 keep track of recvd packets, Re request the packet if lost and also have to write to files on hard disk. 跟踪recvd数据包,如果丢失则重新请求数据包,还必须写入硬盘上的文件。

Restriction I cannot change the above design and i cannot change from UDP to TCP. 限制我无法改变上述设计,我无法从UDP更改为TCP。 The two methods i'm aware of after reading are 阅读后我知道的两种方法是

  • asynchronous multiplexing using select(). 使用select()进行异步复用。
  • Thread per socket. 每个插槽的线程。

I tried the first one and i'm stuck at the point when i get the data. 我尝试了第一个,当我得到数据时,我陷入困境。 I'm able to receive data. 我能够收到数据。 I have some processing to do so i want to start a thread for each socket (or) for sockets to handle (say all first ports, all second, etc ..ie3001,3051 etc) But here if client sends any data then FD_ISSET becomes true , so if i start a thread ,then it becomes thread for every message. 我有一些处理要做到这一点我想为每个套接字(或)启动一个线程,以便套接字处理(比如所有第一个端口,所有第二个,等等.ie3001,3051等)但是如果客户端发送任何数据,那么FD_ISSET变为是的,所以如果我启动一个线程,那么它就成了每个消息的线程。 Question: How to add thread code here, Say if i include pthread_create inside if(FD_ISSET .. ) then for every message that i receive i create a thread. 问题:如何在这里添加线程代码,如果我在if(FD_ISSET ..)中包含pthread_create然后为我收到的每条消息创建一个线程。 But i wanted a thread per socket. 但我想要每个插槽一个线程。

  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

create the threads at the startup of program and divide them to handle data, commmands etc 在程序启动时创建线程并将它们分成处理数据,命令等

how? 怎么样?

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.

The better one would be to have a queue, that fills up by main thread and can be accessed element wise by the other threads. 更好的方法是拥有一个队列,该队列由主线程填充,并且可以由其他线程以元素方式访问。

I assume that each client context is independent of the others, ie. 我假设每个客户端上下文独立于其他客户端,即。 one client socket group can be managed on its own, and the data pulled from the sockets can be processed alone. 一个客户端套接字组可以单独管理,从套接字中提取的数据可以单独处理。

You express two possibilities of handling the problem: 您表达了处理问题的两种可能性:

  1. Asynchronous multiplexing: in this setting, the sockets are all managed by one single thread. 异步多路复用:在此设置中,套接字全部由一个线程管理。 This threads select s which socket must be read next, and pulls data out of it 这个线程select下一个必须读取哪个套接字,然后从中拉出数据

  2. Thread per socket: in this scenario, you have as many threads as there are sockets, or more probably group of sockets, ie. 每个套接字的线程:在这种情况下,您拥有与套接字一样多的线程,或者更多可能是套接字组,即。 clients - this the interpretation I will build from. 客户 - 这将是我将构建的解释。

In both cases, threads must keep ownership of their respective resources, meaning sockets. 在这两种情况下,线程必须保持其各自资源的所有权,即插槽。 If you start moving sockets around between threads, you will make things more difficult that it needs to be. 如果你开始在线程之间移动套接字,你将使它变得更加困难。

Outside the work that needs to be done, you will need to handle thread management: 在需要完成的工作之外,您将需要处理线程管理:

  • How do threads get started? 线程如何开始?
  • How and when are they stopped? 它们如何以及何时停止?
  • What are the error handling policies? 什么是错误处理政策?

Your question doesn't cover these issues, but they might play a significant role in your final design. 您的问题不包括这些问题,但它们可能会在您的最终设计中发挥重要作用。

Scenario (2) seems simpler: you have one main "template" (I use the word in a general meaning here) for handling a group of sockets using select on them, and in the same thread receive and process the data. 场景(2)似乎更简单:你有一个主要的“模板”(我在这里使用一般意义上的单词)来处理一组使用select的套接字,并在同一个线程中接收和处理数据。 It's quite straightforward to implement, with a struct to contain the context specific data (socket ports, pointer to function for packet processing), and a single function looping on select and process, plus perhaps some other checks for errors and thread life management. 实现起来非常简单,使用包含上下文特定数据的结构(套接字端口,用于数据包处理的函数的指针),以及在select和process上循环的单个函数,以及可能的其他一些错误检查和线程生命管理。

Scenario (1) requires a different setup: one I/O thread reads all the packets and pass them on to specialized worker threads to do the processing. 场景(1)需要不同的设置:一个I / O线程读取所有数据包并将它们传递给专用工作线程进行处理。 If processing error occurs, worker threads will have to generate the adhoc packet to be sent to the client, and pass it to the I/O thread for sending. 如果发生处理错误,工作线程必须生成要发送到客户端的adhoc数据包,并将其传递给I / O线程进行发送。 You will need packet queues both ways to allow communication between I/O and workers, and have the I/O thread check the worker queues somehow for resend requests. 您将需要数据包队列以允许I / O和工作程序之间的通信,并让I / O线程以某种方式检查工作队列以获得重新发送请求。 So this solution is a bit more expensive in terms of developement, but reduce the I/O contention to one single point. 因此,这种解决方案在开发方面要贵一些,但将I / O争用降低到一个单点。 It's also more flexible, in case some processing must be done against data coming from several clients, or if you want to chain up processing somehow. 它也更灵活,以防必须对来自多个客户端的数据进行某些处理,或者如果您想以某种方式链接处理。 For instance, you could have instead one thread per client socket, and then one other thread per client group of socket further down the work pipeline, with each step of the pipeline interconnected by packet queue. 例如,您可以在每个客户端套接字中使用一个线程,然后在每个客户端套接字的另一个线程中进一步向下工作管道,管道的每个步骤通过数据包队列互连。

A blend of both solution can of course be implemented, with one IO thread per client, and pipelined worker threads. 当然可以实现两种解决方案的混合,每个客户端有一个IO线程,以及流水线工作线程。

The advantage of both outlined solutions is the fixed number of threads: no need to spawn and destroy threads on demand (although you could design a thread pool to handle that as well). 两个概述的解决方案的优点是固定数量的线程:无需按需生成和销毁线程(尽管您也可以设计一个线程池来处理它)。

For a solution involving moving sockets between threads, the questions are: 对于涉及在线程之间移动套接字的解决方案,问题是:

  • When should these resources be passed on? 什么时候应该传递这些资源? What happens after a worker thread has read a packet? 工作线程读取数据包后会发生什么? Should it return the socket to the IO thread, or risk a blocking read on the socket for the next packet? 它是应该将套接字返回到IO线程,还是冒着在套接字上阻塞读取下一个数据包的风险? If it does a select to poll the socket for more packets, we fall in scenario (2), where each client will has its own I/O thread when there is network trafic from all of them, in which case what is the gain of the initial I/O thread doing the select ? 如果它select轮询套接字以获取更多数据包,我们将陷入方案(2),其中每个客户端都有自己的I / O线程,当有来自所有客户端的网络流量时,在这种情况下,增益是多少进行select的初始I / O线程?

  • If it passes the socket back, should the IO thread wait for all workers to give back their socket before initiating another select ? 如果它再次通过套接字,IO线程是否应该等待所有工作人员在启动另一个select之前回放其套接字? If it waits, it takes the risk of making unserved client wait for packets already in the network buffers, inducing processing lag. 如果它等待,则需要使未服务的客户端等待已经在网络缓冲区中的数据包的风险,从而导致处理延迟。 If it does not wait, and return to select to avoid lag on unserved sockets, then the served ones will have to wait for the next wake up to see their sockets back in the select pool. 如果它没有等待,并返回select以避免延迟未服务的套接字,那么服务的服务器必须等待下一次唤醒才能在select池中看到它们的套接字。

As you can see, the problem is difficult to handle. 如您所见,问题很难处理。 That's the reason why I recommend exclusive sockets ownership by threads as described in scenarii (1) and (2). 这就是为什么我推荐使用线程的独占套接字所有权的原因,如scenarii(1)和(2)中所述。

Your solution requires a fixed, relatively small, number of connections. 您的解决方案需要固定的,相对较少的连接数。

Create a help procedure that creates thread procedures that listen to each of the five ports and block on the recvfrom() , process the data, and block again. 创建一个帮助过程,创建线程过程,监听五个端口中的每个端口并阻塞recvfrom() ,处理数据并再次阻塞。 You can then call the helper 15 times to create the threads. 然后,您可以调用帮助程序15次以创建线程。

This avoids all polling, and allows Linux to schedule each thread when the I/O completes. 这样可以避免所有轮询,并允许Linux在I / O完成时安排每个线程。 No CPU used while waiting, and this can scale to somewhat larger solutions. 等待时不使用CPU,这可以扩展到更大的解决方案。

If you need to scale massively, why not use a single set of ports, and get the partner address from the client_sockaddr_in structure. 如果需要大规模扩展,为什么不使用一组端口,并从client_sockaddr_in结构中获取合作伙伴地址。 If the processing takes a material amount of time, you could extend it by keeping a pool of threads available and assign a new one each time a message is received and continue processing the message thereafter, and adding the thread back to the pool after the response is sent. 如果处理花费了大量时间,您可以通过保持一个可用线程池来扩展它,并在每次收到消息时分配一个新线程,然后继续处理该消息,并在响应后将该线程添加回池中已发送。

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

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