socket通信中的分散读和集中写
解释一
在 socket 通信中,分散读和集中写是两种常见的 I/O 模型。
分散读(scatter read):在分散读模型中,应用程序可以在一次 readv() 系统调用中从socke读取多个缓冲区的数据。这意味着可以一次性读取多个数据包,而不必为每个数据包执行单独的 read() 调用。这种方式可以减少系统调用的次数,提高读取效率。
集中写(gather write):在集中写模型中,应用程序可以在一次 writev() 系统调用中将多个缓冲区的数据写入socket。这意味着可以一次性发送多个数据包,而不必为每个数据包执行单独的 write() 调用。这种方式可以减少系统调用的次数,提高发送效率。
分散读和集中写可以通过使用 readv() 和 writev() 系统调用来实现。这两个系统调用允许应用程序指定一个向量(vector),其中包含多个缓冲区的指针和缓冲区的长度。这样可以一次性读取或写入多个缓冲区的数据。
以下是一个简单的示例代码,展示了如何在 socket 通信中使用分散读和集中写:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
void sendData(int socket, const char *data, size_t dataSize)
{
struct iovec iov[2];
char buffer[1024];
// 将数据分散到两个缓冲区
iov[0].iov_base = data;
iov[0].iov_len = dataSize / 2;
iov[1].iov_base = buffer;
iov[1].iov_len = dataSize - iov[0].iov_len;
// 使用 writev 进行集中写
ssize_t bytesWritten = writev(socket, iov, 2);
if (bytesWritten == -1)
{
perror("Failed to send data");
exit(EXIT_FAILURE);
}
printf("Sent %zu bytes of data\n", bytesWritten);
}
void receiveData(int socket)
{
struct iovec iov[2];
char buffer1[1024], buffer2[1024];
// 创建两个缓冲区来接收数据
iov[0].iov_base = buffer1;
iov[0].iov_len = sizeof(buffer1);
iov[1].iov_base = buffer2;
iov[1].iov_len = sizeof(buffer2);
// 使用 readv 进行分散读
ssize_t bytesRead = readv(socket, iov, 2);
if (bytesRead == -1)
{
perror("Failed to receive data");
exit(EXIT_FAILURE);
}
printf("Received %zu bytes of data\n", bytesRead);
// 输出接收到的数据
printf("Received data: %s\n", buffer1);
printf("Received data: %s\n", buffer2);
}
int main()
{
int serverSocket, clientSocket;
struct sockaddr_in serverAddress, clientAddress;
socklen_t addressLength = sizeof(clientAddress);
// 创建一个 TCP socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Failed to create socket");
exit(EXIT_FAILURE);
}
// 绑定 socket 到本地回环地址和端口 12345
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_LOOPBACK;
serverAddress.sin_port = htons(12345);
if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) == -1)
{
perror("Failed to bind socket");
exit(EXIT_FAILURE);
}
// 开始监听连接
if (listen(serverSocket, 1) == -1)
{
perror("Failed to listen on socket");
exit(EXIT_FAILURE);
}
printf("Waiting for client connection...\n");
// 接受客户端连接
if ((clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddress, &addressLength)) == -1)
{
perror("Failed to accept connection");
exit(EXIT_FAILURE);
}
printf("Connected with client\n");
// 发送数据给客户端
const char *data = "Hello, client!";
sendData(clientSocket, data, strlen(data));
// 接收客户端的数据
receiveData(clientSocket);
// 关闭连接
close(clientSocket);
close(serverSocket);
return 0;
}
在这个示例中,创建了一个TCP服务器和一个TCP客户端。通过在客户端和服务器之间建立连接,可以使用分散读和集中写来高效地传输数据。
解释二
在socket通信中,分散读(scattered reads)和集中写(gathered writes)是两种常用的高级I/O操作,它们通过使用sendmmsg()、recvmmsg()、writev()和readv()等系统调用来提高数据传输效率。这些操作允许你将数据分成多个片段,并且可以同时发送或接收这些片段,从而减少了系统调用的次数和上下文切换的开销。
分散读(Scattered Reads)
分散读是指从socket中读取数据时,将接收到的数据分散到多个缓冲区中。这通常在你想要同时读取多个数据片段,或者你想要减少接收数据时所需的系统调用次数时很有用。在Unix-like系统中,你可以使用recvmsg()配合CMSG_SPACE()和CMSG_FIRSTHDR()宏以及readv()系统调用来实现分散读。
例如,使用readv()函数:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/iov.h>
#include <unistd.h>
// 假设socket_fd是一个已经建立连接的socket文件描述符
int socket_fd;
// 定义两个缓冲区,用于分散读
char buffer1[1024];
char buffer2[512];
// 初始化iovec结构体数组
struct iovec iov[2];
iov[0].iov_base = buffer1;
iov[0].iov_len = sizeof(buffer1);
iov[1].iov_base = buffer2;
iov[1].iov_len = sizeof(buffer2);
// 读取数据到两个缓冲区中
ssize_t bytes_read = readv(socket_fd, iov, 2);
if (bytes_read == -1) {
perror("readv");
// 错误处理
}
// 现在buffer1和buffer2中包含了从socket读取的数据
集中写(Gathered Writes)
集中写是指将数据从多个缓冲区发送到一个socket中。这在你想要同时发送多个数据片段,或者你想要减少发送数据时所需的系统调用次数时非常有用。在Unix-like系统中,你可以使用sendmsg()配合CMSG_SPACE()和CMSG_FIRSTHDR()宏以及writev()系统调用来实现集中写。
例如,使用writev()函数:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/iov.h>
#include <unistd.h>
// 假设socket_fd是一个已经建立连接的socket文件描述符
int socket_fd;
// 定义两个缓冲区,用于集中写
char buffer1[1024];
char buffer2[512];
// 假设buffer1和buffer2已经被填充了数据
// 初始化iovec结构体数组
struct iovec iov[2];
iov[0].iov_base = buffer1;
iov[0].iov_len = sizeof(buffer1);
iov[1].iov_base = buffer2;
iov[1].iov_len = sizeof(buffer2);
// 将数据从两个缓冲区发送到socket中
ssize_t bytes_written = writev(socket_fd, iov, 2);
if (bytes_written == -1) {
perror("writev");
// 错误处理
}
// 现在数据已经从buffer1和buffer2发送到了socket中
使用分散读和集中写可以提高数据传输的效率,因为它们允许你在一个系统调用中处理多个缓冲区,减少了系统调用的开销。此外,这些操作还可以帮助你更好地控制数据传输,例如,你可以通过调整每个缓冲区的大小和数量来优化数据传输的性能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)