如何将单个端口用于多个逻辑数据流(Winsock)?

时间:2011-06-13 13:21:52

标签: multithreading winapi visual-c++ winsock recv

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

所以我试图实现单端口连接(TCP),只有一个套接字用于传输不同的流。各个数据包将在标题中包含信息,表示它所属的流,预期的总消息大小等。说我有5个不同的流。我打算用5个线程来调用同一个套接字的 send()。这样安全吗?我知道来自不同流的数据包会混合到一起,但如果我在每个发送的数据包中包含必要的元信息,它可以在另一端正确地重建,对吗?

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

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

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

P.S。:Afaik,没有办法通过多个套接字使用一个端口,对吗?

它是Windows平台,Winsock2,Visual C ++(如果你能提供非常棒的平台特定提示)。

= UPDATE =

  • 当您说“锁定套接字”时,您的意思是序列化对 send()功能的访问?例如与Critical Section

  • 至于接收端......我想我会在 recv()时收集消息(我称之为“消息”逻辑完整数据结构,例如视频帧或声音样本等) - 将它们循环(缓冲来自各个流的消息到单独的缓冲区),然后我只是将汇编的消息(当一个将被完全接收时)传递给分叉线程。 现在问题是 - 如何传递它们。 我想到的一种方法是Event Objects SetEvent()从接收器线程到不同线程中的 WaitForSingleObject()(在某个循环中)。如果这是可接受的解决方案,你能告诉我吗?你能提出更好的建议吗?没有比事件对象更快的解决方案(“触发”同一应用程序的另一个线程)吗?以及如何传递数据?

2 个答案:

答案 0 :(得分:3)

  
      
  • 从多个线程调用相同套接字的send()是否可以?   (假设数据包中有信息   关于哪个发送者线程的标题(即   子系统)它属于。)。
  •   

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

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

如何将BeginRecv和EndRecv写成类似于boost asios库(或切换到Boost Asio。它是平台独立的!)。当您收到完整的消息后,您可以将其发送出去。我想你想把它放在其他地方继续接收网络?您可以使用CreateThread并通过lpParameter参数发送您的数据,这将是除了recv之外的问题。在下载数据之前不会有太大的分析因为你仍然需要在下一个数据库下载之前下载完整的包。

  
      
  • 您的其他实施建议是什么?   通过一个多流传输   端口?
  •   

对此不太了解,但我之前已经写过关于Winsock的an answer,其中我描述了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_ptrshared_ptr之类的结构 - 这将有助于你破坏数据。

  

我想到的一种方式是   事件对象:来自的SetEvent()   接收器线程触发   WaitForSingleObject()(在   一些循环)在不同的线程中。能够   如果这是可以接受的,你建议   溶液

事件也会起作用,但这不是我冒险的事情。然后你只需要保持你的线程一直运行,而不是在你有数据要处理时开始。 请记住,您还必须保持数据传输同步。如果您对队列有一些排序,则必须确保写入和读取是在彼此之后完成的。

  

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

不会更快地知道任何事情。但要传递数据,要么在创建线程时使用,要么使用某种保持同步的队列。

希望这有帮助。

答案 1 :(得分:0)

从多个线程调用相同套接字的send()是否可以?

当然,提供你锁定套接字。如果我这样做,我会将所有传出协议单元排队到一个线程并发送,可能使用优先级队列来保证快速传递到需要它的那些流。

如何将收到的不同逻辑流的数据包“分叉”到不同的工作线程?

您的协议必须这样做。从传入的字节流中组装视频/音频/任何协议单元,并将PDU排队到适当的处理程序线程。

Windows - 如果您需要高性能,服务器上的IO完成端口,如果您尝试流式传输视频,则会。

RGDS, 马丁