在多线程UDP服务器

时间:2015-09-25 10:33:09

标签: c++ multithreading sockets

Requirement: 

我需要编写一个能够接收多个客户端连接(FCFS命令)的服务器,并能够在一个单独的线程中处理每个客户端,这样处理看起来好像所有客户端都是同时提供的。使用的传输协议应该是UDP。此外,在客户端和服务器之间也会发生几次数据传输,并且从客户端接收的数据必须存储在服务器端,以便进一步处理该客户端。

Implementation:

为实现这一目标,我采用了队列/线程池机制。最初,我创建了一个固定数量的线程池,并有一个队列数据结构来存储客户端地址。此队列在所有线程之间共享,因此我使用“mutex”来锁定/解锁此队列。在主服务器线程中,我创建一个套接字,将其绑定到全局端口/地址,然后在“recvfrom”调用时阻止服务器。任何想要与服务器通信的客户端都会向监听全局端口地址的主线程服务器发送“Hi”消息。接收到这个“Hi”数据报后,主服务器线程将客户端的sockaddr结构推送到队列上,然后返回阻止“recvfrom”调用以接受其他客户端。

任何获取队列锁定的池线程,弹出客户端的地址并开始自行处理此客户端。也就是说,池线程现在将创建一个套接字,将其绑定到与全局端口不同的端口,并使用弹出的客户端地址将此端口地址发送到客户端。在客户端,在发送“Hi”消息后,它会阻止“recvfrom”呼叫,一旦它从服务器(具有不同的端口)接收到消息,它就会存储此端口/地址,然后它会与之通信服务器仅使用池服务器线程发送的新端口。

Clarification needed:

我注意到在许多Stack溢出问题中,单个SOCKET在多线程UDP服务器中总是足够的,但根据我的要求,我认为我需要多个UDP套接字,每个套接字都适用于客户端。请根据我的要求向我提供有关线程UDP服务器方法的建议

编辑:改进设计

感谢您对我的设计的所有评论和讨论。在掌握了关于单插槽连接和上下文切换的所有观点之后,我考虑了改进的设计。请提供有关我改进设计的反馈

在此实现中,我将仅使用绑定到全局已知端口地址的一个全局UDP套接字。就像以前一样,我最初会创建一个固定数量的线程,比如5个线程。然后我在所有线程中的单个套接字上执行recvfrom()。无论哪个线程首先接收数据报,都能够从这个数据报中知道三件事:

  • 客户端sockaddr(客户地址)
  • 客户数据(已发送实际数据)
  • 进程号(与数据一起发送。这是为了让服务器知道必须对客户端数据进行哪种处理)

根据上述信息,服务器线程将根据进程号处理数据,并将回复发送到收到它的同一客户端。在处理数据并发送回复(如果需要)之后,该线程将返回到recvfrom()调用的阻塞。

此外,有关各种客户端的详细信息将存储在全局哈希表和映射中(可供所有线程访问),以便任何线程都可以访问它以根据该特定客户端进行处理。

客户端的更改:服务器应该能够知道它必须对客户端数据执行哪些处理。因此,客户端将发送进程号和数据。

2 个答案:

答案 0 :(得分:1)

一旦套接字足够,因为每次收到数据包时都可以通过使用其源地址(src_addr)调用recvfrom来识别客户端:

recvfrom(int sockfd, void *buf, size_t len, int flags,
             struct sockaddr *src_addr, socklen_t *addrlen);

现在,这个解决方案只有一个接收点。这意味着服务器必须接收数据,然后将其传递给应该执行该过程的线程。这会降低单插槽解决方案的并行性,因为您只使用一个端口。

<德尔> 在我看来,你的解决方案在并行性方面更好,因为你的线程在不同的端口上运行,所以如果你的机器有几个处理器和几个可以用来参加客户的网卡,它们可以并行服务几个客户端。在最好的情况下,您可能有多个并行运行的线程,在不同的处理器中,每个处理器服务于来自不同网络接口的客户端。

修改

在与David讨论你的设计后,他说服了几个套接字会导致多个上下文切换,这可能会降低服务器性能。所以在这种情况下最好的是为所有客户端使用多个线程但只有一个套接字。

答案 1 :(得分:1)

嗯,我不确定这些要求来自哪里 - 主要是那些用于现实生活计划或某些作业/面试/等等。

我将继续推定这是一个真实的计划,因为我没有帮助任何人进行任务:)

UDP不是面向连接的协议(即使UDP套接字可以'连接',但这只会增加混乱)。它是一种面向消息的协议。现在,设计线程模型的第一个问题始终是“我在这里需要线程模型吗?”如果您的操作可以彼此独立地执行并且需要花费很长时间,那么您将需要线程。在您的情况下,这取决于您对邮件的真实作用。让我们假设,你有一些处理它,这个处理时间与潜在的同时cliens的数量相比是显着的(记住,有成功的单线程Web服务器!简单地做所有的单线程请求没有错)。

设计方面,我认为给客户端连接新端口的想法不是一个好主意。这有几个原因,其中一个原因可能是客户端和服务器之间的防火墙。实现方面它也不简单,并以某种方式模仿TCP与UDP。如果您需要TCP,请使用TCP,不要尝试通过UDP构建TCP - 您将无法成功。

我建议改为在套接字上有一个侦听器,它会将请求(连同发送者信息)分派给工作线程,后者会将回复发送回客户端。请记住,对于UDP,您不需要通过用于处理请求的同一套接字发送响应。