如何使用单个端口处理多个逻辑数据流(Winsock)?
我正在开发客户端-服务器 Winsock 应用程序(Visual C++),它应该通过网络传输各种类型的数据(视频流、音频流、服务通知等)。我知道更干净的方法是为每个单独的数据类型在单独的线程上使用单独的端口(我在这里称之为“流”)。但这需要占用至少 5 个不同的端口,这会给某些网络基础设施(防火墙端口转发等)带来问题。
所以我试图实现单端口连接(TCP),仅使用一个套接字来传输不同的流。各个数据包将在标头中包含信息,指示它属于哪个流、预期的总消息大小等。假设我有 5 个不同的流。我计划使用 5 个线程来调用同一套接字的 send() 。这安全吗?我知道来自不同流的数据包将混合到达,但如果我在每个发送的数据包中包含必要的元信息,则可以在另一端正确重建它,对吧?
但真正的问题是接收端。虽然从同一个套接字上的多个线程调用 send() 可能没问题(尽管我不确定并且需要您的确认!),但从多个线程调用 recv()线程没有多大意义。所以我应该从一个线程使用一个阻塞 recv() 。但是,然后,根据数据包标头(识别特定数据包属于哪个流),我应该在不同的线程中分叉处理。视频流应该由一个线程处理,声音流应该由另一个线程处理,等等。但我不太知道如何做到这一点 - 如何从接收线程分叉数据处理。性能是重中之重,因此不能考虑通过一个线程处理所有流。
总而言之,我有三个问题:
- 从多个线程为同一个套接字调用 send() 是否可以? (假设数据包标头中有关于它所属的发送者线程(即子系统)的信息)。
- 在接收端有一个阻塞套接字,从单个线程循环调用recv(),如何将接收到的不同逻辑流的数据包“fork”到不同的工作线程?
- 对于通过一个端口实现多流传输,您有何其他建议?
PS:据我所知,多个套接字无法使用一个端口,对吗?
它是Windows平台、Winsock2、Visual C++(如果你能提供特定于平台的提示那就太棒了)。
= UPDATE =
I'm developing the client-server Winsock app (Visual C++) that should transmit the various kind of data (video stream, audio stream, service notifications, etc.) over the network. I know that the cleaner approach would be to use separate ports on separate threads for each individual data type (I call it "stream" here). But that would require occupying at least 5 different ports that would get problematic for some network infrastructures (firewall port forwarding, etc.).
So I'm trying to implement one-port connection (TCP), only one socket will be used to transmit different streams. The individual packets will have the info in header that will denote which stream it belongs to, total message size to be expected, etc. Say I have 5 different streams. I'm planning to use 5 threads to call send() of the same socket. Is this safe? I know the packets from different streams will arrive mixed but if I include necessary meta information in each sent packet, it could be reconstructed properly on the other side, right?
But the real problem is the receiving end. While it's probably OK to call send() from multiple threads on the same socket (though I'm not sure and need your confirmation!), calling recv() from multiple threads would not make much sense. So I should use one blocking recv() from one thread. But then, based on the packet header (identifying which stream the particular packet belongs to) I should fork the processing in different threads. Video stream should be processed by one thread, sound stream - by another, etc. But I don't quite know how to do this - how to fork the data processing from the receiving thread. Performance is a top priority so processing all streams by one thread can't be considered.
So to sum it up, I have three questions:
- Is it OK to call send() for same socket from multiple threads? (assuming there will be info in packet header about which sender thread (i.e. subsystem) it belongs).
- Having one blocking socket on the receiving end, calling recv() in loop from a single thread, how to "fork" the received packets of different logical streams to different worker threads?
- What would be your additional recommendations for implementing multiple-stream transfer through one port?
P.S.: Afaik, there's no way to use one port by multiple sockets, correct?
It's Windows platform, Winsock2, Visual C++ (if you could provide platform-specific hints that would be awesome).
= UPDATE =
When you say to "lock socket", you mean serialize the access to send() function? e.g. with Critical Section?
As for receiving end... I think I'll assemble messages (I call "message" the logical complete data structure, e.g. video frame, or sound sample, etc.) when recv()-ing them in loop (buffering messages from individual streams into individual buffers), and then I just pass the assembled messages (when one will be fully received), to forking threads. Now that is the question - how to pass them. The one way I've thought of is Event Objects: SetEvent() from receiver thread to trigger WaitForSingleObject() (which is in some loop) in different threads. Can you advice if this is the acceptable solution? Can you suggest anything better? Aren't there any faster solutions to do this ("trigger" another thread of the same application) than Event Objects? And how to pass the data?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当然,您可以从多个线程调用它。但请记住,您必须在 的套接字上加锁同步它。否则,您最终可能会得到不一致的信息。
如何编写类似于 boost asios 库(或切换到 Boost Asio。它是独立于平台的!)。当您收到完整的消息后,您可以将其发送出去。我想您想将其放在其他地方以便继续从网络接收信息?您可以使用 CreateThread 并发送数据通过
lpParameter
参数,这将是别人的问题而不是recv。在下载数据之前分叉它并没有多大意义,因为您仍然需要在下一个包下载之前下载完整的包。对此不太了解,但我之前写过关于 Winsock 的 一个答案我描述了recv() 的不确定性。不要忘记同步,以确保一个包在下一个包之前写入。
确实是的。您可以通过使用例如 RAII 来实现这一点(然后它会被自动删除) /在函数返回时解锁)。但是,可以使用
sendData
函数,首先锁定对象,然后发送数据。好吧,你可以将它作为一个新线程传递。
我在这里有点模糊..但我认为你应该检查诸如 auto_ptr 之类的结构 或 shared_ptr 传输数据时 - 这将帮助您销毁数据。
活动也可以,但这不是我冒险尝试的事情。然后你就可以让你的线程一直运行,而不是在有数据要处理时启动。
记住您还必须保持数据传输同步。如果您有某种队列,则必须确保写入和读取相继完成。
不知道有什么更快。但是要传递数据,要么在创建线程时执行,要么使用某种保持同步的队列。
希望这有帮助。
Of course you can call it from multiple threads. But remember that you have to put a lock on the socket to synchronize it. Otherwise you might end up getting information written inconsistent.
How about writing a BeginRecv and EndRecv as similar to boost asios library (or switching to Boost Asio. It is plattform independent!). When you have recieved a complete message you can send it away. I guess you would like to put that somewhere else as to continue recieving from the network? You could just use CreateThread and send your data via the
lpParameter
parameter and it would be someone elses problem than recv. There wouldn't be much sence in forking it prior to your data being downloaded because you still need to download the complete package before the next one is able to be.Wouldn't know much about it, but I have written an answer earlier regarding Winsock where I describe how uncertain recv() is. Don't forget to synchronize as to make sure that one package is written before the next one is.
Yes indeed. You can do this pretty by using for instance RAII for the lock (then it would be automagically deleted/unlocked on function return). But yes, use a
sendData
function where you first lock an object and then send your data.Well , You can just pass it as a new thread.
I am kind of hazy here.. But I think you should check for constructions such as auto_ptr or shared_ptr when transferring the data - this will help you with the destruction of the data.
Events would work as well, but it is not something i have ventured in to. Then you would just keep your thread going all the time instead of starting when you have data to process.
Remember that you have to keep the data transfer synchronized as well. If you have some sort if queue you have to make sure that writes and reads are done after one another.
Wouldn't know of anything faster. But to pass the data, either do it when you create the thread or use some sort of queue that you keep synchronized.
Hope this helps.
可以从多个线程为同一个套接字调用 send() 吗?
当然可以,前提是你锁住插座。如果我这样做,我会将所有传出协议单元排队到一个线程并随其发送,可能使用优先级队列来保证及时交付到需要它的那些流。
如何将收到的不同逻辑流的数据包“fork”到不同的工作线程?
你的协议必须做到这一点。从传入的字节流中组装视频/音频/任何协议单元,并将 PDU 排队到适当的处理程序线程。
Windows - 如果您需要高性能,则服务器上的 IO 完成端口(如果您尝试流式传输视频),您会的。
平均值,
马丁
Is it OK to call send() for same socket from multiple threads?
Sure, providing you lock the socket up. If I was doing this, I would queue up all the outgoing protocol units to one thread and send with that, possibly using a priority queue to enable some guarantee of prompt delivery to those streams that need it.
how to "fork" the received packets of different logical streams to different worker threads?
Your protocol is going to have to do that. Assemble a video/audio/whatever protocol-unit from the incoming byte-stream and queue the PDU to an appropriate handler thread.
Windows - IO completion ports at the server if you need high performance which, if you are attempting to stream video, you will.
Rgds,
Martin