一个实验证明 read、write 操作的是 scoket 缓冲

摘要

​ 本文主要是通过实验模拟一下 scoket 缓冲区的发送和接收过程,以证明 write,read 操作的是 socket 缓冲区而不是等对端处理完才返回,本质上这也是利用缓存提高性能的一种思路。

基本概念
发送缓冲区

​ 当 TCP 三次握手成功,TCP 连接成功建立后,操作系统内核会为每一个连接发送缓冲区。当我们调用 write 时数据会从应用程序缓冲区拷贝到内核 socket 缓冲。

接收缓冲区

​ 当服务端调用 read 函数进行读取数据时候其实读取的是 socket 缓冲里面的数据

在这里插入图片描述

实验
服务端
int main(int argc, char **argv)
{
    int                 listenfd, connfd;
    socklen_t           clilen;
    struct sockaddr_in  cliaddr, servaddr;
 
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
 
    bzero(&servaddr, sizeof(servaddr));
  	// IPV4
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(12345);
 
    /* bind 到本地地址,端口为 12345 */ 
    bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    /* listen 的 backlog 为 1024 */
    listen(listenfd, 1024);
 
    /* 循环处理用户请求 */
    for ( ; ; ) {
        clilen = sizeof(cliaddr);
        connfd = accept(listenfd, (SA *) &cliaddr, &clilen);
        read_data(connfd);   /* 读取数据 */
        close(connfd);          /* 关闭连接套接字,注意不是监听套接字 */
    }
}
 

void read_data(int sockfd)
{
    ssize_t  n;
    char buf[1024];
 
    int time = 0;
    for ( ; ; ) {
        fprintf(stdout, "block in read\n");
      	// 等于0说明对端发送了 EOF 包,数据读取结束
        if ( (n = Readn(sockfd, buf, 1024)) == 0)
            return;     /* connection closed by other end */
 
        time ++;
        fprintf(stdout, "1K read for %d \n", time);
      	// sleep 模拟程序处理时间
        usleep(1000);
    }
}
客户端
int main(int argc, char **argv)
{
    int                 sockfd;
    struct sockaddr_in  servaddr;
 
    if (argc != 2)
        err_quit("usage: tcpclient <IPaddress>");
 
  	// 创建了 socket 套接字,调用 connect 向对应服务器端发起连接请求。
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
  	// 建立链接,发起三次握手
    connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
  	// 发送数据
    send_data(stdin, sockfd);
    exit(0);
}
 
# define MESSAGE_SIZE 10240000
void send_data(FILE *fp, int sockfd)
{
    char * query;
    query = malloc(MESSAGE_SIZE+1);
    for(int i=0; i< MESSAGE_SIZE; i++){
        query[i] = 'a';
    }
    query[MESSAGE_SIZE] = '\0';
 
    const char *cp;
    cp = query;
    remaining = strlen(query);
    while (remaining) {
      n_written = send(sockfd, cp, remaining, 0);
      fprintf(stdout, "send into buffer %ld \n", n_written);
      if (n_written <= 0) {
        perror("send");
        return;
      }
      remaining -= n_written;
      cp += n_written;
    }
 
    return;
}
执行以上代码

实验一

​ 启动服务端,调用客户端进行数据发送,我们可以观察到客户端发送完成,服务端立马输出了所有数据

在这里插入图片描述

实验二:

​ 把服务端 sleep 时间调大,然后重复实验一。我们会发现客户端已经结束了,服务端还在不断的输出数据,这进一步证明了客户端发送到 socket 缓冲之后就结束了,服务端还在不断的从 socket 缓冲读取数据。

结束语

​ 本文的代码是极客时间网络编程作者的代码,地址:yolanda,可以去这个仓库找到对应的源码并进行实验。计算机的很多的概念我们一听就会,一做就废。软件工程本质上是一门实践科学,我们要多动手,多做实验,才能对事物本身有更深的理解,祝大家变的更强。

Reference
  1. https://dlonng.com/posts/tcp_udp
  2. https://time.geekbang.org/column/intro/100032701
  3. https://github.com/froghui/yolanda
  4. https://zhuanlan.zhihu.com/p/372798949
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值