美文网首页
TCP缓冲区测试

TCP缓冲区测试

作者: 牛奶言兼 | 来源:发表于2022-09-06 23:56 被阅读0次

    接着上篇的问题,tcp在收包时是怎样的?关于tcp的发包,根据常识可以判断,必然是按用户的消息包“原子”拼接的,即: UserDataPackage1:UserDataPackage2:UserDataPackage3 的顺序进行拼接的,在实际发送时,可以产生UserDataPackage1:UserDataPackage2-partialUserDataPackage2-partial:UserDataPackage3两个传输包。所以,发送层面用户关注内容不是特别多,而接收处理层面则比较复杂。

    示例

    服务端代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <stdarg.h>
    
    #include "unistd.h"
    #include "netinet/in.h" // sockaddr_in
    #include "arpa/inet.h" // inet_pton
    
    #define MAXLINE 4096
    #define LISTENQ 1024
    
    #define LOG(format, ...) \
        printf("file:%s, line:%d  ", __FILE__, __LINE__); printf(format, ##__VA_ARGS__); printf("\n")
    
    int main(int argc, char **argv) {
        LOG("==== server start ====");
        int listenfd, connfd;
        struct sockaddr_in servaddr;
        int *buff = (int *)malloc(1024 * 1024 * 1024); // 1024M * 1024KB * 1024Byte
        time_t ticks;
    
        buff[0] = 4 * 1024 * 1024 / sizeof(int);
    
        for (int i = 0; i < (1024 * 1024 * 1024 / sizeof(int) - 1); ++i) {
            buff[i + 1] = i;
        }
    
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(1024);
    
        if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) {
            LOG("bind port success.");
        } else {
            LOG("bind port fail.");
            exit(-1);
        }
    
        listen(listenfd, LISTENQ);
        LOG("wait recv connect request.");
    
        for(;;) {
            LOG("block net i/o.");
            connfd = accept(listenfd, (struct sockaddr *)NULL, NULL);
            LOG("accept connect request.");
            ticks = time(NULL);
            // snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
            write(connfd, buff, 4 * 1024 * 1024); // 发生4MB数据,必然分包传输
            close(connfd);
            break; // 如果不想结束服务,注掉这里
        }
        LOG("==== server end ====");
        exit(0);
    }
    

    客户端代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "unistd.h"
    #include "netinet/in.h" // sockaddr_in
    #include "arpa/inet.h" // inet_pton
    
    #define MAXLINE 4 * 1024 * 1024
    
    #define LOG(format, ...) \
        printf("file:%s, line:%d  ", __FILE__, __LINE__); printf(format, ##__VA_ARGS__); printf("\n")
    
    int main(int argc, char **argv) {
        LOG("==== client start ====");
        int sockfd, n;
        char *recvline = (char *)malloc(1024 * 1024 * 1024);; //[MAXLINE + 1];
        struct sockaddr_in servaddr;
    
        if (argc != 2) {
            LOG("usage: a.out <IPaddress>");
            exit(1);
        }
    
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            LOG("socket error");
            exit(1);
        }
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(1024);
        if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
            LOG("inet_pon error for %s", argv[1]);
            exit(1);
        }
    
        if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { // sock.h connect
            LOG("connect error");
            exit(1);
        }
    
        while ((n = read(sockfd, recvline, MAXLINE)) > 0) {
            LOG("read bytes: %d", n); // 将接收到的字节数打印出来
            recvline[n] = 0;
            LOG("recv content:");
            LOG("%d", *(int*)recvline);
            // if (fputs(recvline, stdout) == EOF) {
            //     LOG("fputs error.");
            //     exit(1);
            // }
        }
    
        if (n < 0) {
            LOG("read error");
            exit(1);
        }
    
        LOG("==== client end ====");
        exit(0);
    }
    

    执行过程

    ./server 
    file:timeserv.c, line:18  ==== server start ====
    file:timeserv.c, line:37  bind port success.
    file:timeserv.c, line:44  wait recv connect request.
    file:timeserv.c, line:47  block net i/o.
    file:timeserv.c, line:49  accept connect request.
    file:timeserv.c, line:56  ==== server end ====
    
    ./client 127.0.0.1
    file:time.c, line:15  ==== client start ====
    file:time.c, line:44  read bytes: 65536
    file:time.c, line:46  recv content:
    file:time.c, line:47  1048576
    file:time.c, line:44  read bytes: 65536
    file:time.c, line:46  recv content:
    file:time.c, line:47  16383
    file:time.c, line:44  read bytes: 2783743
    file:time.c, line:46  recv content:
    file:time.c, line:47  32767
    file:time.c, line:44  read bytes: 1279489
    file:time.c, line:46  recv content:
    file:time.c, line:47  186547968
    file:time.c, line:59  ==== client end ====
    

    从客户端的读取来看,缓冲区的数据是循环使用的,也就是说在读取的过程中,如果有新的数据接收到,而此时还在读取过程中的话,内核会将新收到的数据仍纳入到本次用户数据请求处理中,直到读取完毕或达到用户空间缓冲区大小结束(??感觉是,但不知道是不是,需要看看内核源码,但似乎又不是??)。

    这就是tcp无界的表现,它不管发送方是如何组织的消息分段,在tcp这一层,它就是一串字节流,且流是连续的,可以“无限读”。

    基于这样的特征,应用层可以设计特定分界符或分段消息头来进行处理,完成分界。

    衍生出一个问题
    如果接受缓冲区被打满了,会发生什么?

    相关文章

      网友评论

          本文标题:TCP缓冲区测试

          本文链接:https://www.haomeiwen.com/subject/qkunnrtx.html