socket基础
socket缓冲区
每个socket被创建后,无论使用的是TCP协议还是UDP协议,都会创建自己的接收缓冲区和发送缓冲区。
- socket缓冲区在每个套接字中单独存在;
- socket缓冲区在创建套接字时自动生成;
- 即使关闭套接字也会继续传送
发送
缓冲区中遗留的数据; - 关闭套接字将丢失
接收
缓冲区中的数据。
send的工作原理
ssize_t send(int socket, const void *buf, size_t len, int flags);
send()
函数只负责将数据提交给协议层。
当调用该函数时,send()
先比较待发送数据的长度和套接字的发送缓冲区的长度:
- 如果待拷贝数据的长度大于发送缓冲区的长度时,该函数返回SOCKET_ERROR;
- 如果拷贝数据的长度小于或等于发送缓冲区的长度时,那么send先检查协议是否正在发送发送套接字的发送缓冲区中的数据:
- 如果是,就等待协议把数据发送完,再进行拷贝;
- 如果协议还没有开始发送套接字的发送缓冲区中的数据或者该发送缓冲区中没有数据,那么send就比较该发送缓冲区中的剩余空间和待拷贝数据的长度:
- 如果待拷贝数据的长度大于剩余空间的大小,send就一直等待协议把该发送缓冲区中的数据发完;
- 如果待拷贝数据的长度小于剩余空间大小,send就仅仅把buf中的数据拷贝到剩余空间中。 (注意:并不是send把该套接字的发送缓冲区中数据传到连接的另一端,而是协议传的,send仅仅是把数据拷贝到该发送缓冲区的剩余空间里面。)
send函数返回值:
- 如果send函数拷贝成功,就返回实际拷贝的字节数;
- 如果拷贝的过程中出现错误,send就返回SOCKET_ERROR;
- 如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。
注意:send函数把buffer中的数据成功拷贝到套接字的发送缓冲区中的剩余空间里面后,它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。
recv的工作原理
ssize_t recv(int socket, void *buf, size_t len, int flags)
参数一:指定接收端套接字描述符;
参数二:指向一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
参数三:指明buf的长度;
参数四:一般置为0;
返回值:失败时,返回值小于0;超时或对端主动关闭,返回值等于0;成功时,返回值是返回接收数据的长度。
recv函数仅仅是拷贝数据,真正的接收数据是协议来完成的。
recv先检查套接字的接收缓冲区,如果该接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。
当协议把数据接收完毕,recv函数就把套接字的接收缓冲区中的数据拷贝到用户层的buffer中,
recv函数返回值:
- recv函数返回其
实际拷贝的字节数
。如果recv在拷贝时出错,那么就返回SOCKET_ERROR; - 如果recv函数在等待协议接收数据时网络中断了,那么它返回0。对方优雅的关闭socket并不影响本地recv的正常接收数据;
- 如果协议缓冲区内没有数据,recv返回0,指示对方关闭;
- 如果协议缓冲区有数据,则返回对应数据(可能需要多次recv),在最后一次recv时,返回0,指示对方关闭。
注意:协议接收到的数据可能大于buffer的长度,所以在这种情况下,要调用几次recv函数才能把套接字接收缓冲区中的数据拷贝完。