在之前我们Windows使用了很多次的send()和recv()来进行数据的收发,但我们并未向最后一个参数传递过 0 之外的参数。
另外还有许多I/O函数
多种I/O函数
在Linux中的send() & recv()
最后一个参数是为了指定可选项信息,在之前并未向其传递0之外的参数。
可传递参数包括以下但不止这些:
- MSG_OOB 传输带外数据(优先传输)
- MSG_PEEK 验证输入缓冲中是否存在接受的数据
- MSG_DONTROUTE 数据传输过程中不参照路由表(routing)而在本地(local)王志忠寻找目的地
- MSG_DONTWAIT 调用I/O函数时不阻塞,用于非阻塞函数
- MSG_WAITALL 防止函数返回,直至接受全部请求的字节数
MSG_OOB
客户端(Client)用于发送带外数据紧急消息:
//Client
write(sock,"123",strlen("123"));
send(sock,"4",strlen("4"),MSG_OOB);
write(sock,"567",strlen("567");
send(sock,"890",strlen("890"),MSG_OOB);
客户端按照顺序,理应是123 4 567 890 地接收,但因为紧急传输了4 890 接收顺序也因此改变。
注:紧急消息的意义在于督促消息处理而非金及传输形式受限的消息。
MSG_PEEL & MSG_DONTWAIT
MSG_PEEL & MSG_DONTWAIT 同时输入两个选项,已验证输入缓冲中是否存在接受的数据。
设置MSG_PEEK并调用recv(),即使读取了输入缓冲中的数据也不会删除(仅做检查),因此该选项常与MSG_DONTWAIT合作,用于调用一非阻塞方式验证待读数据是否存在。
//peek_recv
recv(recv_sock,buf,sizeof(buf-1), MSG_PEEK|MSG_DONTWAIT);
readv() & writev()
readv()& writev() 对数据进行整合传输及发送的函数。
通过writevI()可以将分散保存的多个缓冲区数据一并发送,通过readv由多个缓冲区分别接收数据。
也称:散布读(scatter read)和聚集写(gather write)。
//Linux
struct iovec
{
void * iov_base;
size_t iov_len;
}
int main()
{
//...
struct iovec vec[2];
char buf1[]="abcde";
char buf2[]="ghijk";
int strlen;
vec[0].iov_base=buf1;
vec[0].iov_len=sizeof(buf1);
vec[1].iov_base=buf2;
vec[1].iov_len=sizeof(buf2);
str_len=writev(1,vec,2);
//...
}
writev( filedes , ptr , 2 );
首参是文件描述符,ptr是待发送数据iovec数据信息的数组指针,2表示从ptr指向位置开始至浏览两个iovec结构体变量,并发送这些指针指向的缓冲数据。
readv()则将读入的数据按上述同样顺序散布到缓冲区中。readv总是先填满一个缓冲区,然后再填写下一个。readv返回读到的总字节数。如果遇到文件结尾,已无数据可读,则返回0。
合理使用readv() & writev()
(Linux)多次调用read和write的情况可以使用一次readv和writev代替,在C语言角度,减少函数调用次数能提高性能,但这不止减少数据包个数:在为了提高效率,服务器命令禁止Nagle算法情况下,writev函数在不采用Nagle算法时更有价值。
基于Windows的实现
在Windows中不存在Linux那样的信号处理机制!
回顾12章select监视3个对象:
- 是否存在套接字接收数据?
- 无需阻塞传输数据的套接字有哪些?
- 哪些套接字发生了异常?
其中“异常”指不同寻常的程序执行流,因此,Out_of_band数据也属异常。因此可以利用select()这一特性在Windows中接收Out_of_band数据,实际发送(send)代码与上面Linux无异(但首参的数据类型是SOCKET代表目标的套接字)。
不同的是接收紧急消息的代码:
代码:
关于Windows的readv() & writev()
windows并无函数与Linux的readv() & writev()直接对应,但可以通过 重叠I/O 得到相同的效果。