手把手写c++服务器(35):手撕代码——高并发高qps技术基石之非阻塞send万字长文(代码片段)

沉迷单车的追风少年 沉迷单车的追风少年     2023-01-06     693

关键词:

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 

 前言:创建socket是默认阻塞的。但是在高并发多QPS的场景中,阻塞模式会极大程度上影响并发性,使之并发名存实亡。而send函数本质上并不是在网络上发送数据,而是将发送缓冲区的数据拷贝到数据内核中;recv函数的本质是将内核缓冲区中的数据拷贝到应用程序的缓冲区中。因此,当缓冲区满了的时候,阻塞/阻塞会影响send/recv的调用,我们这一篇文章重点讨论这一种情况;后面几讲会继续讨论非阻塞connect及与IO复用结合。

目录

预备知识

1、send/recv的本质

2、文件描述符控制函数:fcntl()

参数详解

3、设置文件描述符为非阻塞模式

4、服务端、客户端响应/请求一般框架

服务端

客户端

正式开始

1、客户端

2、服务端

3、实验结果

服务端:

客户端:

tcpdump抓包:

参考:


预备知识

1、send/recv的本质

send和recv并不是直接收发数据,send函数本质上并不是在网络上发送数据,而是将发送缓冲区的数据拷贝到数据内核中;recv函数的本质是将内核缓冲区中的数据拷贝到应用程序的缓冲区中。

什么时候拷贝、什么时候正式发送,需要看是否启用了nagel算法。如果禁用了nagel算法,存放到内核缓冲区的数据会被立即发出去;有兴趣的同学可以去看一下nagel算法,这也是面试中经常考察的知识点。

如果数据缓冲区满了,阻塞模式和非阻塞模式的表现是不一样的

  • 当socket是阻塞模式时,程序会阻塞在send/recv处。
  • 当socket是非阻塞模式时,send/recv不会阻塞当前程序执行流,会立即返回错误:EWOULDBLOCK或EAGAIN。后面我们会用实验验证这一点。

2、文件描述符控制函数:fcntl()

不太清楚的小伙伴请看往期:手把手写C++服务器(27):五大文件描述符零拷贝、fcntl控制总结_沉迷单车的追风少年-CSDN博客

file control,文件描述符控制。与之类似的系统调用是ioctl。

#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd ,struct flock* lock);

参数详解

3、设置文件描述符为非阻塞模式

#include <fcntl.h>

// 将文件描述符设置为非阻塞模式
int setnoblocking(int fd) 
    // 获取文件描述符旧的状态标志
    int old_option = fcntl(fd, F_GETFL);
    // 设置非阻塞标志
    int new_option = old_option | O_NONBLOCK;
    // 设置非阻塞模式
    fcntl(fd, F_SETFL, new_option);
    // 返回文件描述符旧的状态标志,以便日后恢复改状态标志
    return old_option;

4、服务端、客户端响应/请求一般框架

不太清楚的小伙伴请看往期:https://xduwq.blog.csdn.net/article/details/119482107

服务端

// 设置非阻塞模式
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
 
 
int main(int argc, char* argv[]) 
    if (argc <= 1) 
        printf("error! please input port!\\n");
        return 1;
    
    //创建监听socket
    int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    assert(serv_sock);
    int port = atoi(argv[1]);
 
    //将套接字和IP、端口绑定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    serv_addr.sin_port = htons(port);  //端口
    if (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) 
        std::cout << "bind listen socket is error" << std::endl;
        close(listenfd);
        return -1;
    
 
    //进入监听状态,等待用户发起请求
    if (listen(listenfd, 20) == -1) 
        std::cout << "listen error" << std::endl;
        close(listenfd);
        return -1;
    
 
    //接收客户端请求
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);
    int connfd = 0;
    for ( ; ; ) 
        if ((connfd = accept(listenfd, (struct sockaddr*)&clnt_addr, &clnt_addr_size)) < 0) 
            printf("accept error: %s\\n",strerror(errno));
            return 1;
        
    
    close(listenfd);
    return 0;

客户端

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
 
using namespace std;
 
int main(int argc, char* argv[])
    
    if (argc <= 1) 
        printf("error! please input port!\\n");
        return 1;
    
 
    //创建socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1) 
        std::cout << "create client error" << std::endl;
        return -1;
    
 
    int port = atoi(argv[1]);
 
    //向服务器(特定的IP和端口)发起请求
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    serv_addr.sin_port = htons(port);  //端口
    
    int connectfd = connect(clientfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if (connectfd == -1) 
        std::cout << "connect error" << std::endl;
        close(clientfd);
        return -1;
    
   
    //读取服务器传回的数据
    char buffer[40];
    read(clientfd, buffer, sizeof(buffer)-1);
   
    printf("Message form server: %s\\n", buffer);
   
    //关闭套接字
    close(clientfd);
 
    return 0;

正式开始

1、客户端

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
#include <fcntl.h>

// #define SEND_DATA "NO BLOCKING MODEL"
#define SEND_DATA "hello"

using namespace std;

// 将文件描述符设置为非阻塞模式
int setnoblocking(int fd) 
    // 获取文件描述符旧的状态标志
    int old_option = fcntl(fd, F_GETFL);
    // 设置非阻塞标志
    int new_option = old_option | O_NONBLOCK;
    // 设置非阻塞模式
    if (fcntl(fd, F_SETFL, new_option) == -1) 
        // std::cout << "set no blocking model is error" << std::endl;
        return -1;
    
    // 返回文件描述符旧的状态标志,以便日后恢复改状态标志
    return old_option;


int main(int argc, char* argv[]) 

    if (argc <= 1) 
        printf("error! please input port!\\n");
        return 1;
    

    //创建socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1) 
        std::cout << "create client error" << std::endl;
        return -1;
    
    // 第一个入参是端口
    int port = atoi(argv[1]);

    //向服务器(特定的IP和端口)发起请求
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    serv_addr.sin_port = htons(port);  //端口

    int connectfd = connect(clientfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if (connectfd == -1) 
        std::cout << "connect error" << std::endl;
        close(clientfd);
        return -1;
    

    std::cout << "old clientfd option is " << fcntl(clientfd, F_GETFL, 0) << std::endl;

    // 连接成功后再将clientfd设置为非阻塞模式
    int oldconnectfd = setnoblocking(clientfd);
    if (oldconnectfd == -1) 
        std::cout << "set no block model is error" << std::endl;
        close(clientfd);
        return -1;
    

    // // 获取文件描述符旧的状态标志
    // int old_option = fcntl(clientfd, F_GETFL, 0);
    // // 设置新的非阻塞标志
    // int new_option = old_option | O_NONBLOCK;
    // // 设置非阻塞模式
    // if (fcntl(clientfd, F_SETFL, new_option) == -1) 
    //     std::cout << "set no blocking model is error" << std::endl;
    //     return -1;
    // 

    std::cout << "new clientfd option is " << fcntl(clientfd, F_GETFL) << std::endl;
    std::cout << "new connectfd option is " << fcntl(connectfd, F_GETFL) << std::endl;
    // return 0;

    // 不停地向服务器发送数据,直到出错退出
    int count = 0;
    while (true) 
        // std::cout << "count = " << count << std::endl;
        int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);
        if (ret == -1) 
            // 非阻塞模式下,send由于TCP窗口太小发送不出去数据,错误码是EWOULDBLOCK
            if (errno == EWOULDBLOCK) 
                std::cout << "send data error because TCP windows is too small!" << std::endl;
                continue;
             else if (errno == EINTR) 
                std::cout << "sending is interupted by signal" << std::endl;
                continue;
             else 
                std::cout << "send data error" << std::endl;
                break;
            
         else if (ret == 0) 
            // 对方关闭了连接
            std::cout << "send data error" << std::endl;
            close(clientfd);
            break;
         else 
            count++;
            std::cout << "send data successfully, count = " << count << std::endl; 
        
    
   
    //关闭套接字
    close(clientfd);
 
    return 0;

2、服务端

// 设置非阻塞模式
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <iostream>

int main(int argc, char* argv[]) 

    if (argc <= 1) 
        printf("error! please input port!\\n");
        return 1;
    

    // 创建监听socket
    int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    assert(listenfd);
    int port = atoi(argv[1]);
 
    // 将套接字和IP、端口绑定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
    serv_addr.sin_port = htons(port);  //端口
    if (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) 
        std::cout << "bind listen socket is error" << std::endl;
        close(listenfd);
        return -1;
    
 
    // 进入监听状态,等待用户发起请求
    if (listen(listenfd, SOMAXCONN) == -1) 
        std::cout << "listen error" << std::endl;
        close(listenfd);
        return -1;
    
 
    // 接收客户端请求
    while (true) 
        struct sockaddr_in clientaddr;
        socklen_t clientaddrlen = sizeof(clientaddr);
        int clientfd = accept(listenfd, (struct sockaddr *)& clientaddr, &clientaddrlen);
        if (clientfd != -1) 
            std::cout << "accept a client connection" << std::endl;
         else 
            std::cout << "accept connection error!" << std::endl;
        
    

    close(listenfd);
    return 0;

3、实验结果

服务端:

./server 1234
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection
accept a client connection

客户端:

send data error because TCP windows is too small!
send data error because TCP windows is too small!
send data error because TCP windows is too small!
send data error because TCP windows is too small!
send data error because TCP windows is too small!

tcpdump抓包:

tcpdump -i any -nn -S 'tcp port 1234'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
11:32:11.355877 IP 127.0.0.1.50750 > 127.0.0.1.1234: Flags [.], ack 2208883600, win 512, options [nop,nop,TS val 3367396090 ecr 3367340791], length 0
11:32:11.355888 IP 127.0.0.1.1234 > 127.0.0.1.50750: Flags [.], ack 1270908893, win 0, options [nop,nop,TS val 3367396090 ecr 3367285075], length 0
11:32:13.399881 IP 127.0.0.1.39942 > 127.0.0.1.1234: Flags [.], ack 3715653001, win 512, options [nop,nop,TS val 3367398134 ecr 3367291639], length 0
11:32:13.399895 IP 127.0.0.1.1234 > 127.0.0.1.39942: Flags [.], ack 1174986492, win 0, options [nop,nop,TS val 3367398134 ecr 3367184099], length 0
11:33:04.599897 IP 127.0.0.1.44602 > 127.0.0.1.1234: Flags [.], ack 89987839, win 512, options [nop,nop,TS val 3367449334 ecr 3367338743], length 0
11:33:04.599939 IP 127.0.0.1.1234 > 127.0.0.1.44602: Flags [.], ack 1065986402, win 0, options [nop,nop,TS val 3367449334 ecr 3367227655], length 0
11:33:59.895885 IP 127.0.0.1.50750 > 127.0.0.1.1234: Flags [.], ack 2208883600, win 512, options [nop,nop,TS val 3367504630 ecr 3367396090], length 0
11:33:59.895911 IP 127.0.0.1.1234 > 127.0.0.1.50750: Flags [.], ack 1270908893, win 0, options [nop,nop,TS val 3367504630 ecr 3367285075], length 0
11:34:49.409784 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [S], seq 170000750, win 65495, options [mss 65495,sackOK,TS val 3367554143 ecr 0,nop,wscale 7], length 0
11:34:49.409796 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [S.], seq 2701108373, ack 170000751, win 65483, options [mss 65495,sackOK,TS val 3367554143 ecr 3367554143,nop,wscale 7], length 0
11:34:49.409804 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], ack 2701108374, win 512, options [nop,nop,TS val 3367554143 ecr 3367554143], length 0
11:34:49.409822 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000751:170000756, ack 2701108374, win 512, options [nop,nop,TS val 3367554143 ecr 3367554143], length 5
11:34:49.409861 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000756:170000761, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554143], length 5
11:34:49.409881 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000766:170000771, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 5
11:34:49.409896 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000776:170000781, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 5
11:34:49.409911 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000786:170000791, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 5
11:34:49.409926 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000796:170000801, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 5
11:34:49.409935 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170000806, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 0
11:34:49.409948 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000811:170000816, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 5
11:34:49.409963 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000821:170000826, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 5
11:34:49.409970 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170000826:170000831, ack 2701108374, win 512, options [nop,nop,TS val 3367554144 ecr 3367554144], length 5
11:34:49.429146 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], seq 170000836:170033604, ack 2701108374, win 512, options [nop,nop,TS val 3367554163 ecr 3367554144], length 32768
11:34:49.447874 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], seq 170000836:170033604, ack 2701108374, win 512, options [nop,nop,TS val 3367554182 ecr 3367554144], length 32768
11:34:49.447940 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170033604, win 375, options [nop,nop,TS val 3367554182 ecr 3367554144,nop,nop,sack 1 170000836:170033604], length 0
11:34:49.447953 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], seq 170033604:170066372, ack 2701108374, win 512, options [nop,nop,TS val 3367554182 ecr 3367554182], length 32768
11:34:49.491875 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170066372, win 119, options [nop,nop,TS val 3367554226 ecr 3367554182], length 0
11:34:49.707879 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [P.], seq 170066372:170081604, ack 2701108374, win 512, options [nop,nop,TS val 3367554442 ecr 3367554226], length 15232
11:34:49.707893 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170081604, win 0, options [nop,nop,TS val 3367554442 ecr 3367554442], length 0
11:34:49.923893 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], ack 2701108374, win 512, options [nop,nop,TS val 3367554658 ecr 3367554442], length 0
11:34:49.923923 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170081604, win 0, options [nop,nop,TS val 3367554658 ecr 3367554442], length 0
11:34:50.359883 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], ack 2701108374, win 512, options [nop,nop,TS val 3367555094 ecr 3367554658], length 0
11:34:51.223886 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], ack 2701108374, win 512, options [nop,nop,TS val 3367555958 ecr 3367554658], length 0
11:34:51.223897 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170081604, win 0, options [nop,nop,TS val 3367555958 ecr 3367554442], length 0
11:34:52.955881 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], ack 2701108374, win 512, options [nop,nop,TS val 3367557690 ecr 3367555958], length 0
11:34:52.955893 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170081604, win 0, options [nop,nop,TS val 3367557690 ecr 3367554442], length 0
11:34:53.310633 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [S], seq 4126399068, win 65495, options [mss 65495,sackOK,TS val 3367558044 ecr 0,nop,wscale 7], length 0
11:34:53.310651 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [S.], seq 2899400818, ack 4126399069, win 65483, options [mss 65495,sackOK,TS val 3367558044 ecr 3367558044,nop,wscale 7], length 0
11:34:53.310662 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 0
11:34:53.310737 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399069:4126399074, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310742 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126399074, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 0
11:34:53.310786 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399074:4126399079, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310790 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126399079, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 0
11:34:53.310797 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399079:4126399084, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310799 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126399084, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 0
11:34:53.310805 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399084:4126399089, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310812 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399089:4126399094, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310820 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399094:4126399099, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310828 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399099:4126399104, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310844 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126399114, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 0
11:34:53.310858 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399119:4126399124, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310865 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399124:4126399129, ack 2899400819, win 512, options [nop,nop,TS val 3367558044 ecr 3367558044], length 5
11:34:53.310880 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399134:4126399139, ack 2899400819, win 512, options [nop,nop,TS val 3367558045 ecr 3367558045], length 5
11:34:53.310888 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399139:4126399144, ack 2899400819, win 512, options [nop,nop,TS val 3367558045 ecr 3367558045], length 5
11:34:53.310895 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399144:4126399149, ack 2899400819, win 512, options [nop,nop,TS val 3367558045 ecr 3367558045], length 5
11:34:53.310902 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126399149:4126399154, ack 2899400819, win 512, options [nop,nop,TS val 3367558045 ecr 3367558045], length 5
11:34:53.317094 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], seq 4126399154:4126431922, ack 2899400819, win 512, options [nop,nop,TS val 3367558051 ecr 3367558045], length 32768
11:34:53.331887 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], seq 4126399154:4126431922, ack 2899400819, win 512, options [nop,nop,TS val 3367558066 ecr 3367558045], length 32768
11:34:53.331902 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126431922, win 375, options [nop,nop,TS val 3367558066 ecr 3367558045,nop,nop,sack 1 4126399154:4126431922], length 0
11:34:53.331918 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], seq 4126431922:4126464690, ack 2899400819, win 512, options [nop,nop,TS val 3367558066 ecr 3367558066], length 32768
11:34:53.379882 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126464690, win 119, options [nop,nop,TS val 3367558113 ecr 3367558066], length 0
11:34:53.595879 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [P.], seq 4126464690:4126479922, ack 2899400819, win 512, options [nop,nop,TS val 3367558329 ecr 3367558113], length 15232
11:34:53.595894 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126479922, win 0, options [nop,nop,TS val 3367558330 ecr 3367558329], length 0
11:34:53.807899 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], ack 2899400819, win 512, options [nop,nop,TS val 3367558542 ecr 3367558330], length 0
11:34:53.807936 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126479922, win 0, options [nop,nop,TS val 3367558542 ecr 3367558329], length 0
11:34:54.235890 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], ack 2899400819, win 512, options [nop,nop,TS val 3367558969 ecr 3367558542], length 0
11:34:55.095891 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], ack 2899400819, win 512, options [nop,nop,TS val 3367559829 ecr 3367558542], length 0
11:34:55.095915 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126479922, win 0, options [nop,nop,TS val 3367559830 ecr 3367558329], length 0
11:34:56.471880 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], ack 2701108374, win 512, options [nop,nop,TS val 3367561205 ecr 3367557690], length 0
11:34:56.471900 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170081604, win 0, options [nop,nop,TS val 3367561206 ecr 3367554442], length 0
11:34:56.791884 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], ack 2899400819, win 512, options [nop,nop,TS val 3367561525 ecr 3367559830], length 0
11:34:56.791905 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126479922, win 0, options [nop,nop,TS val 3367561526 ecr 3367558329], length 0
11:35:00.311890 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], ack 2899400819, win 512, options [nop,nop,TS val 3367565045 ecr 3367561526], length 0
11:35:00.311917 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126479922, win 0, options [nop,nop,TS val 3367565046 ecr 3367558329], length 0
11:35:03.383878 IP 127.0.0.1.51346 > 127.0.0.1.1234: Flags [.], ack 2701108374, win 512, options [nop,nop,TS val 3367568117 ecr 3367561206], length 0
11:35:03.383893 IP 127.0.0.1.1234 > 127.0.0.1.51346: Flags [.], ack 170081604, win 0, options [nop,nop,TS val 3367568117 ecr 3367554442], length 0
11:35:07.223885 IP 127.0.0.1.51768 > 127.0.0.1.1234: Flags [.], ack 2899400819, win 512, options [nop,nop,TS val 3367571957 ecr 3367565046], length 0
11:35:07.223906 IP 127.0.0.1.1234 > 127.0.0.1.51768: Flags [.], ack 4126479922, win 0, options [nop,nop,TS val 3367571957 ecr 3367558329], length 0
11:35:09.105206 IP 127.0.0.1.53466 > 127.0.0.1.1234: Flags [S], seq 1554713212, win 65495, options [mss 65495,sackOK,TS val 3367573839 ecr 0,nop,wscale 7], length 0
11:35:09.105220 IP 127.0.0.1.1234 > 127.0.0.1.53466: Flags [S.], seq 2146429495, ack 1554713213, win 65483, options [mss 65495,sackOK,TS val 3367573839 ecr 3367573839,nop,wscale 7], length 0

参考:

手把手写c++服务器(37):手撕代码——高并发多线程技术基石之异步connect万字长文(代码片段)

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:connect创建的时候是默认阻塞模式的,但是现实情况里可能会因为网络差、中间代理服务器、网关等因素造成连接速度慢。此... 查看详情

手把手写c++服务器(34):高并发高吞吐io秘密武器——epoll池化技术两万字长文(代码片段)

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文手把手写C++服务器(31):服务器性能提升关键——IO复用技术【两万字长文】介绍了IO复用技术,其中重点比较了... 查看详情

手把手写c++服务器(30):手撕代码——基于tcp/ip的抛弃服务discard(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前面两讲讲了echo服务器和CGI网关服务器《手把手写C++服务器(29):手撕echo回射服务器代码》《手把手写C++服务器(2... 查看详情

手把手写c++服务器(29):手撕echo回射服务器代码(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:上一讲《手把手写C++服务器(28):手撕CGI通用网关接口服务器代码》讲解了如何利用复制文件描述符dup重定位标准输出&#x... 查看详情

手把手写c++服务器(28):手撕cgi通用网关接口服务器代码(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》《手把手写C++服务器(27):五大文件描述符零拷... 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这几个一次只能监听... 查看详情

手把手写c++服务器(16):服务端多线程并发编程入门精讲(代码片段)

...c;C++的多线程编程显得臃肿、困难。但是在C++服务器编程当中,多线程是一道绕不开门槛,是提高应用程序响应和性能的重要利器,能够隐藏诸如I/O这样耗时的操作延迟。特别是C++11引入了std::thread之... 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这... 查看详情

手把手写c++服务器(14):基于udp测量两台机器之间的网络延迟(代码片段)

前言:网络延迟是网络编程中不可避免的话题,特别是直播、聊天等实时性要求高的应用场景,对网络延迟特别敏感。常用的测量网络延迟工具有基于TCP的qperf等,但是手写一个测量网络延迟的工具可以更好地帮... 查看详情

手把手写c++服务器(25):万物皆可文件之socketfd(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:大家一定听说过在Linux当中,万物皆是文件,任何客观的存在都是以文件形式呈现。前面讲socket编程的时候(手把手写C+&... 查看详情

手把手写c++服务器(27):五大文件描述符零拷贝控制总结(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》、《手把手写C++服务器(25):万物皆可文件之sock... 查看详情

c++从零实现一个高并发内存池(代码片段)

从零实现一个高并发内存池🎄项目介绍◎项目的内容介绍◎要求的知识储备◎内存池的介绍1、池化技术2、内存池3、内存池主要解决的问题4、malloc解析🎄设计思路◎第一阶段--设计一个定长的内存池适应平台的指针方案... 查看详情

手把手写c++服务器(32):三大事件之信号详解(代码片段)

本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:信号实际上是一种软中断,信号机制实际上是进程间通信的一种方式。状态改变、系统异常、系统状态的变化等等,这些是... 查看详情

c++高并发队列的实现(代码片段)

JAVA如何进行CAS讲到java的队列时,讲到java中的CAS操作回顾下java中的cas,主要采用compareAndSet方法,如AtomicReference中所使用的:AtomicRefrence.java/***Atomicallysetsthevaluetothegivenupdatedvalue*ifthecurrentva 查看详情

手把手教你实现分布式锁|redis|高并发|c/c++|linux服务器开发

手把手教你实现分布式锁|redis|高并发|c/c++|linux服务器开发专注于服务器后台开发,包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体 查看详情

手把手写c++服务器(21):linuxsocket网络编程入门基础(代码片段)

本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】前言:刚开始写C++服务器的时候,我们进行网络编程肯定是使用socketAPI,等熟练之后,会根据我们自己的需要,封装... 查看详情

手把手写c++服务器:专栏文章-汇总导航更新中

手把手写C++服务器(1):网络编程常见误区手把手写C++服务器(2):C/C++编译链接模型、函数重载隐患、头文件使用规范手把手写C++服务器(3):C++编译常见问题、编译优化方法、C++库发... 查看详情

高并发内存池项目(c++实战项目)(代码片段)

文章目录🎄项目介绍◎项目来源▶项目源码◎内存池相关知识1、池化技术2、内存池3、内存池主要解决的问题4、malloc🎄设计思路◎第一阶段–设计一个定长的内存池适应平台的指针方案◎第二阶段–高并发内存池整体... 查看详情