简体   繁体   English

实现多线程UDP服务器的问题(线程池?)

[英]Problems implementing a multi-threaded UDP server (threadpool?)

I am writing an audio streamer (client-server) as a project of mine (C/C++), and I decided to make a multi threaded UDP server for this project. 我正在写一个音频流(客户端 - 服务器)作为我的项目(C / C ++),我决定为这个项目制作一个多线程UDP服务器。

The logic behind this is that each client will be handled in his own thread. 这背后的逻辑是每个客户端将在他自己的线程中处理。 The problems I`m having are the interference of threads to one another. 我遇到的问题是线程相互干扰。

The first thing my server does is create a sort of a thread-pool; 我的服务器做的第一件事就是创建一种线程池; it creates 5 threads that all are blocked automatically by a recvfrom() function, though it seems that, on most of the times when I connect another device to the server, more than one thread is responding and later on that causes the server to be blocked entirely and not operate further. 它创建了5个线程,所有线程都被recvfrom()函数自动阻塞,虽然看起来,在我将多个线程连接到服务器的大多数时候,多个线程正在响应,之后导致服务器变为完全阻止而不是进一步操作。

It's pretty difficult to debug this as well so I write here in order to get some advice on how usually multi-threaded UDP servers are implemented. 调试这个也很困难所以我写这里是为了获得一些关于多线程UDP服务器通常实现的建议。

Should I use a mutex or semaphore in part of the code? 我应该在代码的一部分中使用互斥锁或信号量吗? If so, where? 如果是的话,在哪里?

Any ideas would be extremely helpful. 任何想法都会非常有帮助。

Take a step back: you say 退后一步:你说

each client will be handled in his own thread 每个客户端都将在他自己的线程中处理

but UDP isn't connection-oriented. 但UDP不是面向连接的。 If all clients use the same multicast address, there is no natural way to decide which thread should handle a given packet. 如果所有客户端使用相同的多播地址,则没有自然的方法来决定哪个线程应该处理给定的数据包。


If you're wedded to the idea that each client gets its own thread (which I would generally counsel against, but it may make sense here), you need some way to figure out which client each packet came from. 如果你坚持认为每个客户端都有自己的线程(我通常会反对,但这里可能有意义),你需要一些方法来确定每个客户端来自哪个客户端。

That means either 这意味着

  • using TCP (since you seem to be trying for connection-oriented behaviour anyway) 使用TCP(因为你似乎正在尝试面向连接的行为)
  • reading each packet, figuring out which logical client connection it belongs to, and sending it to the right thread. 读取每个数据包,找出它所属的逻辑客户端连接,并将其发送到正确的线程。 Note that since the routing information is global/shared state, these two are equivalent: 请注意,由于路由信息是全局/共享状态,因此这两者是等效的:

    1. keep a source IP -> thread mapping, protected by a mutex, read & access from all threads 保持源IP - >线程映射,受互斥锁保护,读取和访问所有线程
    2. do all the reads in a single thread, use a local source IP -> thread mapping 在单个线程中执行所有读取,使用本地源IP - >线程映射

    The first seems to be what you're angling for, but it's poor design. 第一个似乎是你正在寻找的东西,但它的设计很糟糕。 When a packet comes in you'll wake up one thread, then it locks the mutex and does the lookup, and potentially wakes another thread. 当一个数据包进入时,你将唤醒一个线程,然后它锁定互斥锁并进行查找,并可能唤醒另一个线程。 The thread you want to handle this connection may also be blocked reading, so you need some mechanism to wake it. 您要处理此连接的线程也可能被阻止读取,因此您需要一些机制来唤醒它。

    The second at least gives a seperation of concerns (read/dispatch vs. processing). 第二个至少给出了一个关注点(读/分配与处理)。


Sensibly, your design should depend on 明智地,您的设计应该依赖于

  • number of clients 客户数量
  • I/O load I / O负载
  • amount of non-I/O processing (or IO:CPU ratio, or ...) 非I / O处理量(或IO:CPU比率,或......)

The first thing my server does is create a sort of a thread-pool; 我的服务器做的第一件事就是创建一种线程池; it creates 5 threads that all are blocked automatically by a recvfrom() function, though it seems that, on most of the times when I connect another device to the server, more than one thread is responding and later on that causes the server to be blocked entirely and not operate further 它创建了5个线程,所有线程都被recvfrom()函数自动阻塞,虽然看起来,在我将多个线程连接到服务器的大多数时候,多个线程正在响应,之后导致服务器变为完全阻止而不是进一步操作

Rather than having all your threads sit on a recvfrom() on the same socket connection, you should protect the connection with a semaphore, and have your worker threads wait on the semaphore. 您应该使用信号量保护连接,并让工作线程等待信号量,而不是让所有线程都位于同一套接字连接上的recvfrom()上。 When a thread acquires the semaphore, it can call recvfrom(), and when that returns with a packet, the thread can release the semaphore (for another thread to acquire) and handle the packet itself. 当一个线程获取信号量时,它可以调用recvfrom(),当它返回一个数据包时,线程可以释放信号量(对于另一个要获取的线程)并处理数据包本身。 When it's done servicing the packet, it can return to waiting on the semaphore. 当它完成对数据包的服务时,它可以返回等待信号量。 This way you avoid having to transfer data between threads. 这样您就可以避免在线程之间传输数据。

Your recvfrom should be in the master thread and when it gets data you should pass the address IP:Port and data of the UDP client to the helper threads. 您的recvfrom应该在主线程中,当它获取数据时,您应该将地址IP:UDP客户端的端口和数据传递给帮助程序线程。

Passing the IP:port and data can be done by spawning a new thread everytime the master thread receives a UDP packet or can be passed to the helper threads through a message queue 传递IP:端口和数据可以通过每次主线程接收UDP数据包时生成新线程来完成,或者可以通过消息队列传递给辅助线程

I think that your main problem is the non-persistent udp connection. 我认为你的主要问题是非持久性udp连接。 Udp is not keeping your connections alive, it exchanges only two datagrams per session. Udp没有保持你的连接存活,它每个会话只交换两个数据报。 Depending on your application, in the worst case, it will have concurrent threads reading from the first available information, ie, recvfrom() will unblock even if it is not it's turn to do it. 根据您的应用程序,在最坏的情况下,它将从第一个可用信息中读取并发线程,即recvfrom()将取消阻止,即使轮到它也不会。

I think the way to go is using select in the main thread and, with a concurrent buffer, manage what wich thread will do. 我认为可行的方法是在主线程中使用select,并使用并发缓冲区来管理线程将执行的操作。 In this solution, you can have one thread per client, or one thread per file, assuming that you keep the clients necessary information to make sure you're sending the right file part. 在此解决方案中,每个客户端可以有一个线程,或者每个文件有一个线程,假设您保留客户端必要的信息以确保您发送正确的文件部分。

TCP is another way to do it, since it keeps the connection alive for every thread you run, but is not the best transmission way on data lost allowed applications. TCP是另一种方法,因为它为您运行的每个线程保持连接活动,但不是数据丢失允许的应用程序的最佳传输方式。

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

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