如何避免使用函数调用 WSAsend 将多个缓冲区组合成一个 UDP 数据包?
Posted
技术标签:
【中文标题】如何避免使用函数调用 WSAsend 将多个缓冲区组合成一个 UDP 数据包?【英文标题】:How to avoid combining multiple buffers into one UDP packet with function call WSAsend? 【发布时间】:2021-12-12 05:22:46 【问题描述】:我正在编写 WIN32 代码以在 UDP 套接字上发送数据。为了获得更好的性能,使用多个缓冲区调用 WSASend()。但结果是这些缓冲区中的所有数据都合并为一个 UDP 数据包。我需要为每个缓冲区中的数据提供单独的 UDP 数据包。有没有办法做到这一点? WIN32 不支持 Linux 中的 sendmmsg() ,我相信它可以满足我的需要。谢谢!
'''
#define MAX_UDP_PKT_IN_MSG 5
CHAR userData[MAX_UDP_PKT_IN_MSG][10];
WSABUF buf[MAX_UDP_PKT_IN_MSG];
DWORD pktSent = 0;
/* init socket for UDP */
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// calls bind(), connect()
...
for (int i = 0; i < MAX_UDP_PKT_IN_MSG; i ++)
buf[i].len = 10;
buf[i].buf = &userData[i][0];
rc = WSASend(sock, buf, MAX_UDP_PKT_IN_MSG, &pktSent, 0, 0, 0);
...
'''
【问题讨论】:
您要求的内容每次发送需要 1 个缓冲区。WSASend()
只执行 1 次发送,因此合并缓冲区,而不是 N 次发送 N 个缓冲区。文档中的这条语句暗示了这一点:“对于面向消息的套接字[即UDP],不要超过底层提供者的最大消息大小,可以获取通过获取套接字选项SO_MAX_MSG_SIZE
的值。如果数据太长而无法以原子方式通过底层协议,则返回错误WSAEMSGSIZE
,并且不传输任何数据。" 这意味着所有提交的缓冲区被视为 1 条消息
@RemyLebeau 你是对的!有什么方法可以在一个呼叫中发送多个数据包。
我认为您的意思是消息,而不是数据包。但无论如何,不,我不知道有任何类似于sendmmsg()
用于 Windows 套接字的函数。
【参考方案1】:
使用TransmitPackets 函数。
已记录从多个缓冲区传输。在TRANSMIT_PACKETS_ELEMENT
中有一个标志TP_ELEMENT_EOP
以避免与下一个元素组合,大概你会希望将它与TP_ELEMENT_MEMORY
一起用于每个元素。
TRANSMIT_PACKETS_ELEMENT
上的文档提到:
TP_ELEMENT_EOP
指定此元素不应与从套接字层到传输层的单个发送请求中的下一个元素组合。此标志用于对数据报或面向消息的套接字上的每条消息的内容进行精细控制。
所以TransmitPackets
函数支持这个用例。
使用TransmitPackets
而不是多次调用WSASend
,每次对于每个数据包都是只有一个OS调用,因此性能更有效。好吧,只有其他一切都有效,才能使操作系统调用成为明显的瓶颈。
【讨论】:
这正是我所需要的。谢谢亚历克斯。以上是关于如何避免使用函数调用 WSAsend 将多个缓冲区组合成一个 UDP 数据包?的主要内容,如果未能解决你的问题,请参考以下文章
WSASend 是不是将所有 WSABUF 缓冲区作为单个数据包发送?
io 完成端口问题,每个 GetQueuedCompletionStatus 调用多个 wsarecv 或 wsasend