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

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


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

server端代码

#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <string.h>

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;
}

client端代码

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

#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;
}

执行流程

先启动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字节数据的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_索伦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值