本章所有示例代码>>github
13.1 send & recv函数
1. Linux中的send & recv函数
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
-sockfd: 表示与数据传输对象连接的套接字文件描述符;
-buf: 保存待传输数据的缓冲地址值;
-nbytes: 待传输的字节数;
-flags: 传输数据时指定的可选项信息;
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);// 成功时返回接收的字节数(收到EOF时返回0),失败时返回-1
-sockfd: 表示与数据接收对象连接的套接字文件描述符;
-buf: 保存待接收数据的缓冲地址值;
-nbytes: 可接收的最大字节数;
-flags: 传输数据时指定的可选项信息;
send函数和recv函数的最后一个参数是收发数据时的可选项。该可选项可利用位或(bit OR)运算(| 运算符)同时传递多个信息。
2. MSG_OOB:发送紧急消息
MSG_OOB可选项用于创建特殊发送方法和通道以发送紧急消息。
收到MSG_OOB紧急消息时,操作系统将产生SIGURG信号,并调用注册的信号处理函数。
注意:通过MSG_OOB可选项传递数据时不会加快数据传输速度,而且通过信号处理函数urg_handler读取数据时也只能读1个字节。剩余数据只能通过未设置MSG_OOB可选项的普通输入函数读取。
真正意义上的Out-of-Band需要通过单独的通信路径高速传输数据,但TCP不另外提供,只利用TCP的紧急模式(Urgentmode)进行传输。
3. 紧急模式工作原理
MSG_OOB的真正意义在于督促数据接收对象尽快处理数据。这是紧急模式的全部内容,而且TCP“保持传输顺序”的传输特性依然成立。
如果将缓冲量最左端的位置视作偏移量为0,字符0保存于偏移量为2的位置。另外,字符0右侧偏移量为3的位置存有紧急指针(UrgentPointer)。紧急指针指向紧急消息的下一个位置(偏移量加1),同时向对方主机传递如下消息:
“紧急指针指向的偏移量为3之前的部分就是紧急消息!”
实际只用1个字节表示紧急消息。
“偏移量就是参照基准位置表示相对位置的量。”
4. 检查输入缓冲
同时设置MSG_PEEK选项和MSG_DONTWAIT选项,以验证输入缓冲中是否存在接收的数据。设置MSG_PEEK选项并调用recv函数时,即使读取了输入缓冲的数据也不会删除。该选项通常与MSG_DONTWAIT合作,用于调用以非阻塞方式验证待读数据存在与否的函数。
调用recv函数时传递MSG_DONTWAIT可选项,是为了保证即使不存在待读取数据也不会进入阻塞状态。
13.2 readv & writev函数
1. 使用readv & writev函数
readv& writev函数有助于提高数据通信效率。
通过writev函数可以将分散保存在多个缓冲中的数据一并发送,通过readv函数可以由多个缓冲分别接收。因此,适当使用这两个函数可以减少I/O函数的调用次数。
#include <sys/uio.h>
ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
-filedes:表示数据传输对象的套接字文件描述符,不限于套接字;
-iov:iovec结构体数组的地址值,结构体iovec中包含待发送数据的位置和大小信息;
-iovcnt:向第二个参数传递的数组长度;
struct iovec
{
void *iov_base; // 缓冲地址
size_t iov_len; // 缓冲大小
};
示例:
#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);
-filedes: 传递接收数据的文件(或套接字)描述符;
-iov: 包含数据保存位置和大小信息的iovec结构体数组的地址值;
-iovcnt: 第二个参数中数组的长度;
2. 合理使用readv & writev函数
- 需要传输的数据分别位于不同缓冲(数组)时,需要多次调用write函数,此时可以通过1次writev函数调用替代操作;
- 从C语言角度考虑,减少函数调用次数可以提高性能;但更大的意义在于减少数据包个数;