2

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

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

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

所以总结一下,我有三个问题:

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

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

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

= 更新 =

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

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

4

2 回答 2

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

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

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

如何编写类似于boost asios库的 BeginRecv 和 EndRecv(或切换到 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_ptrshared_ptr之类的结构-这将帮助您销毁数据。

我想到的一种方法是 Event Objects: SetEvent() 从接收线程触发不同线程中的 WaitForSingleObject() (在某个循环中)。你能建议这是否是可接受的解决方案吗?

事件也会起作用,但这不是我冒险的事情。然后,您将一直保持线程运行,而不是在有数据要处理时启动。 请记住,您还必须保持数据传输同步。如果您有某种队列,则必须确保写入和读取一个接一个地完成。

你能提出更好的建议吗?没有比事件对象更快的解决方案(“触发”同一应用程序的另一个线程)吗?以及如何传递数据?

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

希望这可以帮助。

于 2011-06-13T19:31:09.170 回答
0

可以从多个线程为同一个套接字调用 send() 吗?

当然,只要您锁定插座。如果我这样做,我会将所有传出协议单元排队到一个线程并用它发送,可能使用优先级队列来保证及时传递到需要它的那些流。

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

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

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

Rgds,马丁

于 2011-06-13T13:46:53.903 回答