我正在写一个音频流(客户端-服务器)作为我的一个项目(C/c++),我决定为这个项目做一个多线程的UDP服务器。
背后的逻辑是,每个客户端将在自己的线程中处理。我遇到的问题是线程之间的干扰。
我的服务器做的第一件事是创建一个线程池;它创造了5所有被recvfrom()
函数自动阻塞的线程,不过,似乎在大多数时候,当我连接其他设备时对于服务器,不止一个线程正在响应这将导致服务器完全被阻塞,无法进一步操作。
这也很难调试,所以我在这里按顺序写以获得有关通常如何实现多线程UDP服务器的一些建议。
我应该在部分代码中使用互斥或信号量吗?如果有,在哪里?任何想法都会非常有帮助。
退后一步:你说
每个客户端将在自己的线程中处理
但是UDP不是面向连接的。如果所有客户端都使用相同的多播地址,就没有自然的方法来决定哪个线程应该处理给定的数据包。
如果您坚持每个客户端都有自己的线程的想法(我通常不建议这样做,但在这里可能是有意义的),您需要一些方法来确定每个数据包来自哪个客户端。
意思是要么
- 使用TCP(因为你似乎正在尝试面向连接的行为)
-
读取每个数据包,找出它属于哪个逻辑客户端连接,并将其发送到正确的线程。注意,由于路由信息是全局/共享状态,所以这两个是等价的:
- 保留一个源IP ->线程映射,由互斥锁保护,读取&所有线程的访问
- 在单个线程中完成所有读取,使用本地源IP ->线程映射
第一个似乎是你想要的,但它是糟糕的设计。当一个数据包进来时,您将唤醒一个线程,然后它锁定互斥锁并进行查找,并可能唤醒另一个线程。你想处理这个连接的线程也可能被阻塞读取,所以你需要一些机制来唤醒它。
第二种方法至少提供了关注点的分离(读/分派与处理)。
显然,你的设计应该依赖于
- 客户端数量
- I/O负载
- 非i/O处理量(或IO:CPU比率,或…)
我的服务器做的第一件事是创建一个线程池;它创建了5个线程,所有线程都被recvfrom()函数自动阻塞,尽管似乎,在大多数时候,当我将另一个设备连接到服务器时,多个线程正在响应,后来导致服务器完全被阻塞,而不是进一步操作
与其让所有线程都在同一个套接字连接上等待recvfrom(),不如用信号量保护连接,并让工作线程等待信号量。当一个线程获取信号量时,它可以调用recvfrom(),当它返回一个包时,线程可以释放信号量(让另一个线程获取)并自己处理这个包。当它完成对数据包的服务后,它可以返回等待信号量。这样可以避免在线程之间传输数据。
您的recvfrom应该在主线程中,当它获得数据时,您应该将地址IP:端口和UDP客户端的数据传递给辅助线程。
传递IP:端口和数据可以通过每次主线程接收到UDP数据包时生成一个新线程来完成,或者可以通过消息队列传递给辅助线程
我认为你的主要问题是非持久的udp连接。Udp不会保持你的连接存活,它每次会话只交换两个数据报。根据您的应用程序,在最坏的情况下,它将有并发线程读取第一个可用信息,即,recvfrom()将解除阻塞,即使它不是轮到它这样做。
我认为要去的方法是在主线程中使用select,并使用并发缓冲区,管理哪个线程将做什么。在这个解决方案中,您可以为每个客户端设置一个线程,或者为每个文件设置一个线程,假设您保留了客户端必要的信息,以确保发送正确的文件部分。
TCP是另一种方式,因为它为您运行的每个线程保持连接活动,但对于允许数据丢失的应用程序不是最好的传输方式。