send/recv与socket

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/swty3356667/article/details/84201178

send函数

不论是客户端还是服务器端应用程序都用send函数来向TCP连接的另一端发送数据。客户端程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

当调用send函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区,如果len大于发送缓冲区的长度,该函数返回错误SOCKET_ERROR;如果len <= s的发送缓冲区,那么send先检查协议是否正在发送s的发送缓冲区中的数据,如果是就等待协议把数据发送完毕,如果协议还没有开始发送s的发送缓冲区中的数据或者s的发送缓冲区中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len > 剩余空间 send就一直等待协议把s的发送缓冲区中的数据发送完,如果len < 剩余空间 send就仅仅把buf中的数据copy到剩余空间里(send仅仅是把buf中的数据copy到s的发送缓冲区中,至于把数据传送到另一端是协议传的不是send)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误或者在等待协议传送数据时网络断开,那么send函数也返回SOCKET_ERROR。

要注意send函数把buf中的数据成功copy到s的发送缓冲区的剩余空间里,send函数就返回了,但是此时这些数据并不一定马上被传送到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个socket函数就会返回SOCKET_ERROR。但是注意的是:每一个除send以外的socket函数在执行的最开始总要等待套接字的发送缓冲区中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该socket函数就返回SOCKET_ERROR。

注意:在Unix系统之下,如果send在等待协议传送数据时网络断开,调用send的进程会接受到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

错误代码:

EBADF 参数s 非合法的socket处理代码。
EFAULT 参数中有一指针指向无法存取的内存空间
ENOTSOCK 参数s为一文件描述词,非socket。
EINTR 被信号所中断。
EAGAIN 此操作会令进程阻断,但参数s的socket为不可阻断。
ENOBUFS 系统的缓冲内存不足
ENOMEM 核心内存不足
EINVAL 传给系统调用的参数不正确。

recv函数

当调用recv函数时,recv函数先等待s的发送缓冲区中的数据都被协议传送完毕,如果协议在传送数据时出现了网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲区中没有数据或者协议成功发送完毕后,recv函数先检查s的接受缓冲区,如果s接收缓冲区中没有数据或者正在接收数据,那么recv函数久一直等待直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲区中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲区中的数据copy完毕。recv函数仅仅是拷贝数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv函数在copy时出错,返回SOCKET_ERROR,如果recv函数在等待协议接收数据时网络中断了,那么recv函数返回0。

注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

返回值说明

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

返回说明:   

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

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

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

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

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

EBADF:sock不是有效的描述词

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

EFAULT:内存空间访问出错

EINTR:操作被信号中断

EINVAL:参数无效

ENOMEM:内存不足

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

ENOTSOCK:sock索引的不是套接字

socket缓冲区

默认情况下socket是阻塞的。

send函数并不是直接将数据传输到网络中,而是负责将数据写入输出缓冲区,数据从输出缓冲区发送到目标主机是由TCP协议完成的。数据写入到输出缓冲区之后,send函数就可以返回了,数据是否发送出去,是否发送成功,何时到达目标主机,都不由它负责了,而是由协议负责。

recv函数也是一样的,它并不是直接从网络中获取数据,而是从输入缓冲区中读取数据。

输入输出缓冲区,系统会为每个socket都单独分配,并且是在socket创建的时候自动生成的。一般来说,默认的输入输出缓冲区大小为8K。套接字关闭的时候,输出缓冲区的数据不会丢失,会由协议发送到另一方;而输入缓冲区的数据则会丢失。

Socket数据发送与接收问题

数据的发送和接收是独立的,并不是发送方执行一次send,接收方就执行以此recv。recv函数不管发送几次,都会从输入缓冲区尽可能多的获取数据。如果发送方发送了多次信息,接收方没来得及进行recv,则数据堆积在输入缓冲区中,取数据的时候会都取出来。换句话说,recv并不能判断数据包的结束位置。

send函数:
在数据进行发送的时候,需要先检查输出缓冲区的可用空间大小,如果可用空间大小小于要发送的数据长度,则send会被阻塞,直到缓冲区中的数据被发送到目标主机,有了足够的空间之后,send函数才会将数据写入输出缓冲区。

TCP协议正在将数据发送到网络上的时候,输出缓冲区会被锁定(生产者消费者问题),不允许写入,send函数会被阻塞,直到数据发送完,输出缓冲区解锁,此时send才能将数据写入到输出缓冲区

要写入的数据大于输出缓冲区的最大长度的时候,要分多次写入,直到所有数据都被写到缓冲区之后,send函数才会返回。

recv函数:
函数先检查输入缓冲区,如果输入缓冲区中有数据,读取出缓冲区中的数据,否则的话,recv函数会被阻塞,等待网络上传来数据。如果读取的数据长度小于输出缓冲区中的数据长度,没法一次性将所有数据读出来,需要多次执行recv函数,才能将数据读取完毕。

参考:

https://blog.csdn.net/u010270148/article/details/53605339

https://www.cnblogs.com/sunziying/p/6501045.html

https://www.cnblogs.com/Berryxiong/p/6547510.html

https://www.cnblogs.com/msb-/articles/6042413.html

https://blog.csdn.net/u010871058/article/details/76147082

猜你喜欢

转载自blog.csdn.net/swty3356667/article/details/84201178