我正在研究Linux网络编程,并看到一些关于读取,从/向套接字写入数据的功能。 recv,send,recvmsg,sendmesg。据我所知,recvmsg可用于传递文件描述符,增加从/向套接字读/写的超时。你能不能让我知道使用recvmsg,sendmsg而不是recv,send? 非常感谢你。
答案 0 :(得分:2)
recvmsg和sendmsg可以做的一些事情:
您可以执行分散/收集缓冲区。例如,让我们想要收到1MB的数据,但是你只有10个缓冲区,每个缓冲区都是100KB,那么你可以在一个recvmsg调用中填写每个缓冲区。
访问控制标志,辅助数据和IP数据包标头字段。例如,对于UDP,您可以通过枚举从recvmsg返回的控制数据(启用某些ioctl)来获取数据包被寻址的目标IP /端口地址。
答案 1 :(得分:0)
古老的问题,但是recvmsg的另一个有用的功能是用于在数据包中使用标头/数据格式的协议。您可以设置一个用于接收标头的缓冲区,以及另一个用于接收数据的缓冲区。
到目前为止,仅需将后者移入接收的数据即可,您将获得连续的数据,而没有任何(附加的)内存副本。效率更高。
想象一下您需要分批传输非常大的文件的情况。使用经典的recv调用,您需要创建一个这样的缓冲区(伪代码):
while(received < expectedFileSize)
{
char buffer[65536 - sizeof(Header)];
int s = recv(fd, buffer, sizeof(buffer), 0); // One syscall here
received += write(outfd, &buffer[sizeof(Header)], s); // One syscall per buffer, which cause a memcpy in the kernel
}
使用recvmsg,您可以跳过每个数据包和许多系统调用的内存副本:
char * file = mmap(...); // Get a writable buffer for this file
while(received < expectedFileSize)
{
char header[sizeof(Header)];
struct iovec v[2] = { { header, sizeof(Header) }, { file + received, expectedFileSize - received } };
msghdr msg = { NULL, 0, v, 2, 0, 0, 0 };
int s = recvmsg(fd, &msg, 0); // Only one syscall here
received += s - sizeof(Header);
}
不幸的是,这仅适用于固定大小的标头。
相反,假设您需要编写一个HTTP服务器。
此协议在发送内容之前先发送一堆头。使用sendmsg几乎是执行此任务的1:1方法,其中一个线程在编译头文件,而另一个线程在编译内容。
在这种情况下,无需添加锁定,因为每个线程在其自己的缓冲区上执行,和,内容编译的处理无需等待标头被完全编译(因为没有sendmsg
,您需要标头大小才能在标头后后附加内容。