非阻塞模式下 SEND 和 RECV 函数的返回值总结

 send 和 recv 函数的各种返回值意义:

返回值 n返回值含义
大于 0成功发送 n 个字节
0对端关闭连接
小于 0( -1)出错或者被信号中断或者对端 TCP 窗口太小数据发不出去(send)或者当前网卡缓冲区已无数据可收(recv)

我们来逐一介绍下这三种情况:

  • 返回值大于 0

    对于 send 和 recv 函数返回值大于 0,表示发送或接收多少字节,需要注意的是,在这种情形下,我们一定要判断下 send 函数的返回值是不是我们期望发送的缓冲区长度,而不是简单判断其返回值大于 0。举个例子:

    1
    2
    3
    4
    5
    
    int n = send(socket, buf, buf_length, 0);
    if (n > 0)
    {
        printf("send data successfully\n");
    }
    

    很多新手会写出上述代码,虽然返回值 n 大于 0,但是实际情形下,由于对端的 TCP 窗口可能因为缺少一部分字节就满了,所以返回值 n 的值可能在 (0, buf_length] 之间,当 0 < n < buf_length 时,虽然此时 send 函数是调用成功了,但是业务上并不算正确,因为有部分数据并没发出去。你可能在一次测试中测不出 n 不等于 buf_length 的情况,但是不代表实际中不存在。所以,建议要么认为返回值 n 等于 buf_length 才认为正确,要么在一个循环中调用 send 函数,如果数据一次性发不完,记录偏移量,下一次从偏移量处接着发,直到全部发送完为止。

    1
    2
    3
    4
    5
    6
    
    //不推荐的方式一
    int n = send(socket, buf, buf_length, 0);
    if (n == buf_length)
    {
        printf("send data successfully\n");
    }
    
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
//推荐的方式二:在一个循环里面根据偏移量发送数据
bool SendData(const char* buf, int buf_length)
{
    //已发送的字节数目
    int sent_bytes = 0;
    int ret = 0;
    while (true)
    {
        ret = send(m_hSocket, buf + sent_bytes, buf_length - sent_bytes, 0);
        if (ret == -1)
        {
            if (errno == EWOULDBLOCK)
            {
                //严谨的做法,这里如果发不出去,应该缓存尚未发出去的数据,后面介绍
                break;
            }
            else if (errno == EINTR)
                continue;
            else
                return false;
        }
        else if (ret == 0)
        {
            return false;
        }

        sent_bytes += ret;
        if (sent_bytes == buf_length)
            break;

        //稍稍降低 CPU 的使用率
        usleep(1);
    }

    return true;
}

  • 返回值等于 0

    通常情况下,如果 send 或者 recv 函数返回 0,我们就认为对端关闭了连接,我们这端也关闭连接即可,这是实际开发时最常见的处理逻辑。send 函数主动发送 0 字节时也会返回 0,这是一种特例,我们下一节会介绍。

  • 返回值小于 0

    对于 send 或者 recv 函数返回值小于 0 的情况(即返回 -1),根据前文的讨论,此时并不表示 send 或者 recv 函数一定调用出错。这里列一个表格说明:

     返回值和错误码send 函数recv 函数操作系统说明
    1返回 -1,错误码 EWOUDBLOCK 或 EAGAINTCP 窗口太小,数据暂时发不出去当前内核缓冲区中无可读数据Linux
    2返回 -1,错误码 EINTR被信号中断,需要重试被信号中断,需要重试Linux
    3返回 -1,错误码不是上述 1 和 2出错出错Linux
    4返回 -1,错误码 WSAEWOUDBLOCKTCP 窗口太小,数据暂时发不出去当前内核缓冲区中无可读数据Windows
    5返回 -1,错误码不是上述 4出错出错Windows

注意:这里是针对非阻塞模式下 socket 的 send 和 recv 返回值,如果是阻塞模式下 socket,如果返回值是 -1(Windows 上即 SOCKET_ERROR),则一定表示出错。

阻塞与非阻塞的 SOCKET 的各自适用场景

阻塞的 socket 函数在调用 sendrecvconnectaccept 等函数时,如果特定的条件不满足,就会阻塞其调用线程直至超时,非阻塞的 socket 恰恰相反。这并不意味着非阻塞模式的 socket 模式比阻塞模式的 socket 模式好,二者各有优缺点。

非阻塞模式的 socket,一般用于需要支持高并发多 QPS 的场景下(如服务器程序),但是正如前文所述,这种模式让程序执行流和控制逻辑变复杂;相反,阻塞模式逻辑简单,程序结构简单明了,常用于一些特殊的场景。这里举两个应用的场景:

示例一

程序需要临时发送一个文件,文件分段发送,每段对端都会的给与一个应答,程序可以单独开一个任务线程,在这个任务线程函数里面,使用先 send 后 recv 再 send 再 recv 的模式,每次 send 和 recv 都是阻塞式的。

示例二

A 端与 B 端之间只有问答模式,即 A 发送给 B 一个请求,B 必定会应答给 A 一个响应,除此以外,B 不会给 A 推送任何数据,也可以采取阻塞模式,每次 send 完请求后,就可以直接使用阻塞式的 recv 去接受一定要有的应答包。

  • 13
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值