linux socket编程中的recv和send的返回值介绍及其含义。

<1>recv方法:

模型:

 #include <sys/types.h>

 #include <sys/socket.h>

 ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数:

sockfd创建的文件描述符fd,buf接收数据的缓冲区,len接收数据的长度,flags表示信息,默认设置为0

当应用程序调用recv接收数据的时候,recv函数会等待sockfd中发送数据的缓冲区的协议发送完数据,如果在等待过程中出现网络错误,则会返回SOCKET_ERROR。如果sockfd中的缓冲区中没有数据或者协议已经发送完数据,则recv会检查sockfd的接受缓冲区,如果该缓冲区正在接受数据,则recv会一直等待,知道缓冲区接受数据完毕,之后recv将数据从缓冲区拷贝一份值buf中,数据通过协议转发的,recv只是将数据从缓冲区拷贝过来。注,如果recv在拷贝数据时出现错误,则返回SOCKET_ERROT,如果在协议传输数据中出现网络错误,则返回0。

阻塞与非阻塞recv返回值没有区别,都是:

       <0 出错    

       =0 对方调用了close API关闭连接

       >0 接收到数据大小,

特别地:返回值<0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的,继续接收。

但是如下特点:

只是阻塞模式下recv会一直阻塞直到接收到数据,非阻塞模式下如果没有数据就会返回,不会阻塞着读,因此需要循环读取)。

返回说明:   

1)成功执行时,返回接收到的字节数。

2)若另一端已关闭连接则返回0,这种关闭是对方主动且正常的关闭

3)失败返回-1errno被设为以下的某个值   

EAGAIN:套接字已标记为非阻塞,而接收操作被阻塞或者接收超时

EBADFsock不是有效的描述词

ECONNREFUSE:远程主机阻绝网络连接

EFAULT:内存空间访问出错

EINTR:操作被信号中断

EINVAL:参数无效

ENOMEM:内存不足

ENOTCONN:与面向连接关联的套接字尚未被连接上

ENOTSOCKsock索引的不是套接字

<2>send方法:

模型:

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数:

sockfd:创建的sockfd文件描述符,buf发送数据所在的数据区,len发送数据的长度,flags标志位默认设置为0

   当程序使用send方法的时候,send会首先检查协议sockfd中的发送缓冲区中是否有数据发送,send会比较发送数据的buf长度和sockfd发送数据的缓冲区长度,如果len大于sockfd的发送长度,则send返回SOCKET_ERROR,如果发送缓冲区的大小足够,则将数据buf中的数据发送至发送缓冲区中,确认send函数将数据拷贝至发送换区中,另外,如果send检测发送缓冲区有数据但是还未发送,就比较该缓冲区的剩余空间和和len的大小,如果len大于剩余空间,就一直等待,直到缓冲区中的数据发送玩为止,如果len<缓冲区剩余的大小,就将发送的数据拷贝至该缓冲区中,如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。(send函数只是将数据拷贝至发送缓冲中,就返回,此刻数据不一定发送至接收端)。

总结,不管是send还是recv方法,都是数据的缓冲区和发送的缓冲区的拷贝操作过程,真正发送数据的是协议功能,注意三种返回值的可能性,>0表示成功,返回实际发送或接受的字节数,=0表示超时,对方主动关闭了连接过程,<0出错,此种情况可能出现过重情况,如上所示。其中errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN这三种是特殊情况,实际使用中表示继续正常接受数据即可。

如图所示通信过程:


<3>send()方法的行为

    对于send方法,将需要发送的数据拷贝至发送缓冲区,否则进入阻塞或者进入超时等待。如果改变这种状态,将发送缓冲区大小设置为0,这样,当send方法返回是,所发送的数据就都到达目的机器。但是,只是到达目标服务器的接受缓冲区,并不保证数据以被应用层所接收。另外, 在发送数据时,协议根据滑动窗口和MSS值来确定tcp报文段的数据字段大小,这样就能保证接收缓冲区不会溢出。如果接收方的滑动窗口为0,但是发送方还有数据尚未发送完成,就是用探测机制,一方面检测对方方的滑动窗口的大小变化(探测机制是通过每次发送一个字节来进行检测,由先前的30s到之后的1分钟,最终达到2分钟间隔)),另一方面检测对方的连接是否异常。

     push标志指示接收端应尽快将数据提交给应用层。如果send函数提交的待发送数据量较小,例如小于1460B(参照MSS值确定),那么协议层会将该报文中的TCP头部的push字段置为1;如果待发送的数据量较大,需要拆成多个数据段发送时,协议层只会将最后一个分段报文的TCP头部的push字段置1。

<4>recv()方法的行为

    对recv方法来说,将接收缓冲区中的数据拷贝至应用层的缓冲区中,当应用缓冲区满或者接受缓冲区数据接收完,就会返回。如果将接受缓冲区大小设置为0,那么该方法会直接从协议中的滑动窗口中获取数据。要么缓冲区接收满为止。要么当push标志位1的时候 ,recv返回实际接收的数据大小。

协议层收到TCP数据包后(保存在滑动窗口区),本方的滑动窗口合拢(窗口值减小);当协议层将数据拷贝到接收缓冲区(滑动窗口区—>接收缓冲区),或者应用层调用recv接收数据(接收缓冲区—>应用层缓冲区,滑动窗口区—>应用层缓冲区)后,本方的滑动窗口张开(窗口值增大)。收到数据更新window后,协议层向对方发送ACK确认。




猜你喜欢

转载自blog.csdn.net/qq_26105397/article/details/80988429