以下内容引述自《Linux/Unix系统编程手册》
流式套接字上的部分读和部分写
如果套接字上可用的数据比在read()调用中请求的数据要少,这种情况下,read()直接返回可用的字节数。
如果没有足够的缓冲区空间来传输所有请求的字节,并且满足了如下的几条中的其中一条时,可能会出现部分写的现象。
- 在write() 调用中传输了部分请求的字节后被信号处理例程中断;
- 套接字工作在非阻塞模式下(O_NONBLOCK),可能当前只能传输一部分请求的字节;
- 在部分请求的字节已经完成传输后出现了一个异步错误。对于这里的异步错误,我们指的是应用程序使用的套接字API调用中出现了一个异步错误。异步错误是可能会发生的
在所有上述情况中,假设缓冲区空间至少能传输1字节数据,write()调用成功,并返回传输到输出缓冲区中的字节数。
如果出现了部分I/O现象——例如,如果read()返回的字节数少于请求的数量,又或者是阻塞式的write()调用在完成了部分数据传输后被信号处理例程中断——那么有时候需要重新调用系统调用来完成全部数据的传输。
针对以上情况,我们使用循环调用系统函数来重新启用这些系统调用,因此确保了请求的字节数总是能够全部得到传输
shutdown()系统调用
在套接字上调用close() 会将双向通信通道的两端都关闭。有时候,只关闭连接的一端也是有用处的,这样数据只能在一个方向上通过套接字传输。系统提供了shutdown()提供了这种功能。
#include <sys/socket.h>
int shutdown(int sockfd, int how);
系统调用shutdown()可以根据参数how的值选择关闭套接字通道的一端还是两端。参数how可以选择以下几种:
SHUT_RD
关闭连接的读端。之后的读取操作将返回文件结尾(0)。数据仍然可以写入到套接字上。在UNIX域流式套接字上执行了SHUT_RD操作后,对端应用程序将接收到一个SIGPIPE信号,如果继续尝试在对端套接字上做写操作的话,将产生EPIPE错误。
SHUT_WR
关闭连接的写端。一旦对端的应用程序已经将所有剩余的数据读取完毕,它就会检测到文件结尾。后续对本地套接字的写操作将产生SIGPIPE信号以及EPIPE错误。而由对端写入的数据仍然可以在套接字上读取。
SHUT_RDWR
将连接的读端和写端都关闭。这等同于先执行SHUT_RD,跟着再执行SHUT_WR操作
shudown() 同close() 之间的另一个重要区别是:无论该套接字上是否还关联着其他的文件描述符,shutdown()都会关闭套接字通道。但是shutdown()并不会关闭文件描述符,就算参数how指定为SHUT_RDWR时也是如此。要关闭文件描述符,必须另外调用close()
专用于套接字的I/O系统调用:recv和send
recv() 和 send() 系统调用可在已连接的套接字上执行I/O操作。提供了专属于套接字的功能,而这些功能在传统的read() 和 write() 系统调用中是没有的。
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buffer, size_t length, int flags);
ssize_t send(int sockfd, void *buffer, size_t length, int flags);
recv() 和 send() 的返回值以及前3个参数同read() 和 write() 一样。最后一个参数flags 是一个位掩码,用来修改 I/O 操作的行为。
对于recv() 来说,该参数可以为下列值相或的结果
MSG_DONTWAIT
让recv() 以非阻塞方式执行。如果没有数据可用,那么recv()不会阻塞而是立刻返回,伴随的错误码为EAGAIN。我们可以通过fcntl()把套接字设为非阻塞模式(O_NONBLOCK) 从而达到相通的效果。区别在于MSG_DONTWAIT允许我们在每次调用中控制非阻塞行为
MSG_OOB
在套接字上接收带外数据
MSG_PEEK
从套接字缓冲区获取一份请求字节的副本,但不会请求的字节从缓冲区中实际移除,这份数据稍后可以由其他的recv()或read()调用重新读取
MSG_WAITALL
通常,recv() 调