如何使用单个端口处理多个逻辑数据流(Winsock)?

发布于 2024-11-15 05:07:55 字数 1680 浏览 1 评论 0原文

我正在开发客户端-服务器 Winsock 应用程序(Visual C++),它应该通过网络传输各种类型的数据(视频流、音频流、服务通知等)。我知道更干净的方法是为每个单独的数据类型在单独的线程上使用单独的端口(我在这里称之为“流”)。但这需要占用至少 5 个不同的端口,这会给某些网络基础设施(防火墙端口转发等)带来问题。

所以我试图实现单端口连接(TCP),仅使用一个套接字来传输不同的流。各个数据包将在标头中包含信息,指示它属于哪个流、预期的总消息大小等。假设我有 5 个不同的流。我计划使用 5 个线程来调用同一套接字的 send() 。这安全吗?我知道来自不同流的数据包将混合到达,但如果我在每个发送的数据包中包含必要的元信息,则可以在另一端正确重建它,对吧?

但真正的问题是接收端。虽然从同一个套接字上的多个线程调用 send() 可能没问题(尽管我不确定并且需要您的确认!),但从多个线程调用 recv()线程没有多大意义。所以我应该从一个线程使用一个阻塞 recv() 。但是,然后,根据数据包标头(识别特定数据包属于哪个流),我应该在不同的线程中分叉处理。视频流应该由一个线程处理,声音流应该由另一个线程处理,等等。但我不太知道如何做到这一点 - 如何从接收线程分叉数据处理。性能是重中之重,因此不能考虑通过一个线程处理所有流。

总而言之,我有三个问题:

  • 从多个线程为同一个套接字调用 send() 是否可以? (假设数据包标头中有关于它所属的发送者线程(即子系统)的信息)。
  • 在接收端有一个阻塞套接字,从单个线程循环调用recv(),如何将接收到的不同逻辑流的数据包“fork”到不同的工作线程?
  • 对于通过一个端口实现多流传输,您有何其他建议?

PS:据我所知,多个套接字无法使用一个端口,对吗?

它是Windows平台、Winsock2、Visual C++(如果你能提供特定于平台的提示那就太棒了)。

= UPDATE =

  • 当你说“锁定套接字”时,你的意思是序列化对send()函数的访问?例如关键部分

  • 至于接收端...我想我会在recv()-将它们循环起来(将来自各个流的消息缓冲到各个缓冲区中),然后我将组装的消息(当消息被完全接收时)传递给分叉线程。 现在的问题是如何通过它们。我想到的一种方法是事件对象:从接收器线程触发的SetEvent()不同线程中的 WaitForSingleObject() (位于某个循环中)。您能否建议这是否是可接受的解决方案?你能提出更好的建议吗?难道没有比事件对象更快的解决方案来执行此操作(“触发”同一应用程序的另一个线程)吗?以及如何传递数据?

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

遇见了你 2024-11-22 05:07:55
  • 可以从多个线程为同一个套接字调用 send() 吗?
    (假设数据包中有信息
    关于哪个发送者线程的标头(即
    子系统)它所属)。

当然,您可以从多个线程调用它。但请记住,您必须在 的套接字上加锁同步它。否则,您最终可能会得到不一致的信息。

  • 接收端有一个阻塞套接字,循环调用recv()
    从单个线程,如何“分叉”
    收到的不同数据包
    逻辑流到不同的worker
    线程?

如何编写类似于 boost asios 库(或切换到 Boost Asio。它是独立于平台的!)。当您收到完整的消息后,您可以将其发送出去。我想您想将其放在其他地方以便继续从网络接收信息?您可以使用 CreateThread 并发送数据通过lpParameter参数,这将是别人的问题而不是recv。在下载数据之前分叉它并没有多大意义,因为您仍然需要在下一个包下载之前下载完整的包。

  • 您对于实施有何额外建议
    通过一个流进行多流传输
    端口?

对此不太了解,但我之前写过关于 Winsock 的 一个答案我描述了recv() 的不确定性。不要忘记同步,以确保一个包在下一个包之前写入。

当你说“锁定套接字”时,你
意味着序列化对 send() 的访问
功能?例如关键部分?

确实是的。您可以通过使用例如 RAII 来实现这一点(然后它会被自动删除) /在函数返回时解锁)。但是,可以使用 sendData 函数,首先锁定对象,然后发送数据。

现在的问题是 - 如何通过它们。

好吧,你可以将它作为一个新线程传递。

DWORD WINAPI myVideoProcessor(LPVOID theData){
    StructForKeepingVideoData* data = dynamic_cast<StructForKeepingVideoData*>(theData);
    // process the data that is passed in theData
    ...
}
...
void ReceiveData(){
    while (true){
        ...
        char buffer[SIZE];
        recv(mySocket, buffer, SIZE, 0);
        StructForKeepingVideoData* data = new StructForKeepingVideoData(buffer);

        HANDLE mId = CreateThread(NULL, 0, myVideoProcessor, data, 0, NULL);
        // now, the video processing will be done somewhere else. let's continue receiving data!
    }
}

我在这里有点模糊..但我认为你应该检查诸如 auto_ptr 之类的结构shared_ptr 传输数据时 - 这将帮助您销毁数据。

我想到的一种方法是
事件对象:SetEvent() 来自
接收者线程触发
WaitForSingleObject()(位于
一些循环)在不同的线程中。能
你建议这是否可以接受
解决方案?

活动也可以,但这不是我冒险尝试的事情。然后你就可以让你的线程一直运行,而不是在有数据要处理时启动。
记住您还必须保持数据传输同步。如果您有某种队列,则必须确保写入和读取相继完成。

你能提出什么建议吗
更好的?有没有更快的
执行此操作的解决方案(“触发”
同一线程的另一个线程
应用程序)而不是事件对象?和
如何传递数据?

不知道有什么更快。但是要传递数据,要么在创建线程时执行,要么使用某种保持同步的队列。

希望这有帮助。

  • 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).

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.

  • 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?

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.

  • What would be your additional recommendations for implementing
    multiple-stream transfer through one
    port?

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.

When you say to "lock socket", you
mean serialize the access to send()
function? e.g. with Critical Section?

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.

Now that is the question - how to pass them.

Well , You can just pass it as a new thread.

DWORD WINAPI myVideoProcessor(LPVOID theData){
    StructForKeepingVideoData* data = dynamic_cast<StructForKeepingVideoData*>(theData);
    // process the data that is passed in theData
    ...
}
...
void ReceiveData(){
    while (true){
        ...
        char buffer[SIZE];
        recv(mySocket, buffer, SIZE, 0);
        StructForKeepingVideoData* data = new StructForKeepingVideoData(buffer);

        HANDLE mId = CreateThread(NULL, 0, myVideoProcessor, data, 0, NULL);
        // now, the video processing will be done somewhere else. let's continue receiving data!
    }
}

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.

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?

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.

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?

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.

眼眸里的快感 2024-11-22 05:07:55

可以从多个线程为同一个套接字调用 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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文