做io测试

使用 send / recv 做 IO 性能测试(TCP)

send/recv 是 Linux socket 原生接口,适合做用户态TCP IO吞吐、延迟测试 ,区别于 epoll/io_uring,这里给最简单的阻塞版本,再给性能测试思路。 原理:服务端 recv,客户端 send,传输固定大小数据,统计耗时、带宽。

一、完整C代码(阻塞TCP,send/recv 测试)

server.c(接收端,recv)

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>

#define PORT 8888
#define BUF_SIZE (1<<20) // 1MB buffer

int main() {
    int server_fd, new_socket;
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    char *buf = malloc(BUF_SIZE);

    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 1);
    printf("server listen on %d\n", PORT);

    new_socket = accept(server_fd, (struct sockaddr*)&addr, &addrlen);
    printf("client connected\n");

    long long total_recv = 0;
    clock_t start = clock();

    while (1) {
        int n = recv(new_socket, buf, BUF_SIZE, 0);
        if (n <= 0) break;
        total_recv += n;
    }

    clock_t end = clock();
    double sec = (double)(end - start)/CLOCKS_PER_SEC;
    printf("recv total: %lld bytes, time: %.2fs, bw: %.2f MB/s\n",
           total_recv, sec, total_recv / sec / (1<<20));

    close(new_socket);
    close(server_fd);
    free(buf);
    return 0;
}

client.c(发送端,send)

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

#define PORT 8888
#define BUF_SIZE (1<<20)
#define SEND_TOTAL (10LL << 30) // 10GB 测试流量

int main() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv_addr;
    char *buf = malloc(BUF_SIZE);
    memset(buf, 'A', BUF_SIZE);

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);

    connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    printf("connected to server\n");

    long long remain = SEND_TOTAL;
    clock_t start = clock();

    while (remain > 0) {
        int send_len = (remain > BUF_SIZE) ? BUF_SIZE : remain;
        int n = send(sock, buf, send_len, 0);
        if (n <= 0) {
            perror("send");
            break;
        }
        remain -= n;
    }

    clock_t end = clock();
    double sec = (double)(end - start)/CLOCKS_PER_SEC;
    printf("send total: %lld bytes, time: %.2fs, bw: %.2f MB/s\n",
           SEND_TOTAL, sec, SEND_TOTAL / sec / (1<<20));

    close(sock);
    free(buf);
    return 0;
}

二、编译 & 运行

复制代码
gcc server.c -o server -O2
gcc client.c -o client -O2

窗口1(服务端)

复制代码
./server

窗口2(客户端)

复制代码
./client

默认本地回环测试 10GB 流量,测 TCP 吞吐。 改 127.0.0.1 换成远端IP,测跨机器网络IO。

三、send/recv 关键坑(做IO测试必须注意)

  1. send 不代表数据已经送到对端 send 只是拷贝到内核 socket send buffer,成功返回仅代表拷贝完成;真正收完是对端 recv。

    所以吞吐统计一定要以 server recv 累计字节为准,client send 的计数会偏乐观。

  2. send/recv 返回值永远要检查 TCP是字节流,不保证一次send多少就一次recv多少 。 send() 返回可能小于传入长度(部分发送),上面代码已经处理了剩余数据。

  3. buffer 大小影响测试结果 调整 BUF_SIZE:4k/64k/1M/4M,用来测试不同应用层缓冲区对IO的影响。

  4. 阻塞 vs 非阻塞 上面是阻塞socket。 非阻塞:fcntl(sock, F_SETFL, O_NONBLOCK),此时 send/recv 可能返回 -1,errno=EAGAIN,需要循环重试。适合高并发IO测试。

  5. socket buffer 调优(提升上限)

    临时修改内核tcp缓冲区

    sysctl -w net.core.rmem_max=16777216
    sysctl -w net.core.wmem_max=16777216

代码里也可以用 setsockopt 设置 SO_RCVBUF / SO_SNDBUF。

参考链接 :0voice · GitHub

相关推荐
一条破秋裤1 小时前
Linux 网络编程基础:网络分类、分层模型与数据封装
linux·运维·网络
欣欣之王来了1 小时前
5G的行业应用:工业互联网、车联网、智慧医疗中的网络支撑
网络·学习·计算机网络·安全·教程
她说彩礼65万1 小时前
C语言 单引号与双引号
java·服务器·c语言
FACELESS VOID1 小时前
wsl下使用npm,错误识别到了windows下的npm
linux·windows·npm
anew___1 小时前
《从零手写操作系统 (19):Ext2文件系统实战——从内存到磁盘的跨越》
java·服务器·前端·数据库·算法
AOI小白新手上路1 小时前
视频去雨网络的模块缝合路线 · 实践笔记
网络·笔记·音视频
H.莓飛1 小时前
【数据结构】栈
linux·开发语言·数据结构·算法·centos
LRL_1 小时前
【云原生】Oracle Linux 9.6 离线(Air-Gapped)环境下安装 Longhorn 及其底层依赖(iSCSI/NFS)完全指南
linux·云原生·oracle
Brookty1 小时前
双向分流FIN标记、TCB服务逻辑与TCP断开连接流程介绍
网络·tcp