• 网络编程:发送0字节数据的效果


    网络编程:发送0字节数据的效果


    通过一个例子来看看发送一个长度为0的数据,send函数返回值是什么,对端是否会接收到0字节数据。

    server端代码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(void)
    {
        // 1.创建一个监听socket
        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (listenfd == -1)
        {
            std::cout << "create listen socket error." << std::endl;
            return -1;
        }
    
        // 2.初始化服务器地址
        struct sockaddr_in bindaddr;
        bindaddr.sin_family = AF_INET;
        bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        bindaddr.sin_port = htons(3000);
        if (bind(listenfd, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) == -1)
        {
            std::cout << "bind listen socket error." << std::endl;
            close(listenfd);
            return -1;
        }
    
        // 3.启动监听
        if (listen(listenfd, SOMAXCONN) == -1)
        {
            std::cout << "listen error." << std::endl;
            close(listenfd);
            return -1;
        }
    
        int clientfd;
        struct sockaddr_in clientaddr;
        socklen_t clientaddrlen = sizeof(clientaddr);
    
        // 4.接受客户端连接
        clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);
        if (clientfd != -1)
        {
            while (true)
            {
                char recvBuf[64] = {0};
                // 5.从客户端接收数据,客户端没u有数据过来时,会在recv函数处阻塞
                int ret = recv(clientfd, recvBuf, 64, 0);
                if (ret > 0)
                {
                    std::cout << "recv data from client, data: " << recvBuf << std::endl;
                }
                else if (ret == 0)
                {
                    // 假设recv返回值为0时意味着收到了0字节数据
                    std::cout << "recv 0 byte data." << std::endl;
                    continue;
                }
                else
                {
                    // 出错
                    std::cout << "recv data error." << std::endl;
                    break;
                }
            }
        }
    
        // 6.关闭客户端socket
        close(clientfd);
        // 7.关闭监听socket
        close(listenfd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    client端代码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define SERVER_ADDRESS "127.0.0.1"
    #define SERVER_PORT     3000
    #define SEND_DATA       ""
    
    int main(void)
    {
        // 1.创建一个socket
        int clientfd = socket(AF_INET, SOCK_STREAM, 0);
        if (clientfd == -1)
        {
            std::cout << "create client socket error." << std::endl;
            return -1;
        }
    
        // 2.连接服务器
        struct sockaddr_in serveraddr;
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
        serveraddr.sin_port = htons(SERVER_PORT);
        if (connect(clientfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) == -1)
        {
            std::cout << "connect socket error." << std::endl;
            close(clientfd);
            return -1;
        }
    
        // 连接成功后,我们再将clientfd设置为非阻塞模式
        // 不能再创建时就设置,这样会影响到connect的行为
        int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);
        int newSocketFlag = oldSocketFlag | O_NONBLOCK;
        if (fcntl(clientfd, F_SETFL, newSocketFlag) == -1)
        {
            close(clientfd);
            std::cout << "set socket to nonblock error." << std::endl;
            return -1;
        }
    
        // 3. 不断向服务器发送数据,或者出错退出
        int count = 0;
        while (true)
        {
            // 发送0字节数据
            int ret = send(clientfd, SEND_DATA, 0, 0);
            if (ret == -1)
            {
                // 在非阻塞模式下,send函数由于TCP窗口太小,
                // 发不出去数据,错误码是EWOULDBLOCK
                if (errno == EWOULDBLOCK)
                {
                    std::cout << "send data error as TCP Window size is too small." << std::endl;
                    continue;
                }
                else if (errno == EINTR)
                {
                    // 信号被中断 继续重试
                    std::cout << "sending data interrupted by singal." << std::endl;
                    continue;
                }
                else
                {
                    std::cout << "send data error." << std::endl;
                    break;
                }
            }
            else if (ret == 0)
            {
                // 发送了0字节数据
                std::cout << "send 0 byte data." << std::endl;
            }
            else
            {
                count++;
                std::cout << "send data successfully, count = " << count << std::endl;
            }
    
            sleep(1);
        }
    
        // 5.关闭socket
        close(clientfd);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    执行流程

    先启动server端,再使用tcpdump抓取经过3000端口的数据包。

    在这里插入图片描述

    在这里插入图片描述

    在启动client端,输出结果如下:每隔一秒发送一次数据。

    在这里插入图片描述

    此时使用lsof -i Pn命令查看连接状态:发现连接正常

    在这里插入图片描述

    在tcpdump抓包结果中,除了建立连接的三次握手数据包,再无其它数据,也就是说,send函数发送0字节数据,此时send函数返回0,但client端的操作系统协议栈并不会把这些数据发送出去:

    在这里插入图片描述

    因此,server端也会一直没有输出:
    在这里插入图片描述

    使用gdb调试,此时中断会发现,server端没有数据,一直阻塞在recv函数调用处:

    在这里插入图片描述

    在这里插入图片描述

    结论

    通过测试,可以知道存在以下两种情况让send函数的返回值为0:

    1. 对端关闭连接时,正好尝试调用send函数发送数据;
    2. 本端尝试调用send函数发送0字节数据。

    而recv函数只有在对端关闭连接时才会返回0,对端发送0字节数据,本端的recv函数是不会收到0字节数据的。

  • 相关阅读:
    P8 服务拆分-服务远程调用
    每天一个知识点-如何保证缓存一致性
    樱花(筛素数+约数)
    【定制项目】【M15 消防安全宣传】主要模块:视频 + 音频 + 图标 + 问答游戏
    【《C Primer Plus》读书笔记】第11章:字符串和字符串函数
    Python求解不等式优化问题
    GPU服务器安装驱动、cuda和cudnn和tensorflow
    【Unity100个实用小技巧】如何修改UI上材质的Shader
    【面经】被虐了之后,我翻烂了equals源码,总结如下
    PHP面试 linux基础
  • 原文地址:https://blog.csdn.net/m0_56257585/article/details/126590380