套接字中readwrite和sendrecv函数
参考:《UNIX ⽹络编程 · 卷1 : 套接字联⽹API》
write 和 read 函数
⼀旦,我们建⽴好了 TCP 连接之后,我们就可以把得到的 fd 当作⽂件描述符来使⽤。由此⽹络程序⾥最基本的函数就是 read 和 write 函数了。其定义如下:
#include<unistd.h>
ssize_t read(int fd,void*buf, size_t count);
ssize_t write(int fd,const void*buf, size_t count);
write 函数将 buf 中的 count 字节内容写⼊⽂件描述符 fd。成功时返回写的字节数。失败时返回 -1。并设置 errno 变量。
在⽹络程序中,当我们向套接字⽂件描述符写时有两可能:
1. write 的返回值⼤于 0,表⽰写了部分或者是全部的数据。这样我们⽤⼀个 while 循环来不停的写⼊,
但是循环过程中的 buf 参数和
nbyte 参数得由我们来更新。也就是说,⽹络写函数是不负责将全部数据写完之后在返回的。
2. 返回的值⼩于 0,此时出现了错误。我们要根据错误类型来处理。如果错误为 EINTR 表⽰在写的时候出现了中断错误。如果为
EPIPE 表⽰⽹络连接出现了问题(对⽅已经关闭了连接)。
为了处理以上的情况,我们⾃⼰编写⼀个写函数来处理这⼏种情况。
int my_write(int fd,void*buffer,int length)
{
int bytes_left;
int written_bytes;
char*ptr;
ptr = buffer;
bytes_left = length;
while(bytes_left >0)
{
written_bytes =write(fd, ptr, bytes_left);
if(written_bytes <=0)
{
if(errno == EINTR)
written_bytes =0;
else
return(-1);
}
bytes_left -= written_bytes;
ptr += written_bytes;
}
return(0);
}
write的返回值read 函数是负责从 fd 中读取内容。当读成功时,read 返回实际所读的字节数。
如果返回的值是 0 表⽰已经读到⽂件的结束了;⼩于 0 表⽰出现了错误;如果错误为 EINTR 说明读是由中断引起的;如果是ECONNREST 表⽰⽹络连接出了问题。
和上⾯⼀样,也写⼀个⾃⼰的读函数。
int my_read(int fd,void*buffer,int length)
{
int bytes_left;
int bytes_read;
char*ptr;
bytes_left = length;
while(bytes_left >0)
{
bytes_read =read(fd, ptr, bytes_left);
if(bytes_read <0)
{
if(errno == EINTR)
bytes_read =0;
else
return(-1);
}
else if(bytes_read ==0)
break;
bytes_left -= bytes_read;
ptr += bytes_read;
}
return(length - bytes_left);
}
send 和 recv 函数
recv 和 send 函数提供了和 read 和 write 差不多的功能。不过它们提供了第四个参数来控制读写操作。recv 和 send 函数需要⼀个额外的参数,其定义如下:
#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd,void*buf, size_t len,int flags);
ssize_t send(int sockfd,const void*buf, size_t len,int flags);
前⾯的三个参数和 read、write ⼀样,第四个参数可以是 0 或者是以下的组合:
参数含义
MSG_PEEK查看数据,并不从系统缓冲区移⾛数据
MSG_OOB接受或者发送带外数据
MSG_WAITALL等待所有数据
MSG_DONTWAIT仅本操作⾮阻塞
MSG_DONTROUTE不查表
MSG_DONTROUTE:是 send 函数使⽤的标志。这个标志告诉 IP ⽬的主机在本地⽹络上⾯,没有必要查表,这个标志⼀般⽤⽹络诊断和路由程序⾥⾯。
MSG_OOB:表⽰可以接收和发送带外的数据。关于带外数据我们以后会解释的。
MSG_PEEK:是 recv 函数的使⽤标志,表⽰只是从系统缓冲区中读取内容,⽽不清除系统缓冲区的内容。这样下次读的时候,仍然是⼀样的内容。⼀般在有多个进程读写数据时可以使⽤这个标志。
MSG_WAITALL:是 recv 函数的使⽤标志,表⽰等到所有的信息到达时才返回。使⽤这个标志的时候 recv 会⼀直阻塞,直到指定的条件满⾜,或者是发⽣了错误:
1. 当读到了指定的字节时,函数正常返回。返回值等于 len
2. 当读到了⽂件的结尾时,函数正常返回。返回值⼩于 len
3. 当操作发⽣错误时,返回 -1,且设置错误为相应的错误号 (errno)
MSG_DONTWAIT:在⽆需打开相应套接字的⾮阻塞标志下,把单个 IO 操作临时指定为⾮阻塞,接着执⾏ I/O 操作,然后关闭⾮阻塞标志。
flags 参数在设计上存在⼀个基本问题:它是按值传递的,⽽不是⼀个值-结果参数。因此它只能⽤于从进程向内核传递标志。内核⽆法向进程传回。但对于 TCP/IP 协议不需要从测内核向进程传回标志。
但是之后的标准却指出了随输⼊操作向进程返送 MSG_EOR 标志的需求。其做出的决定是保持常⽤的输⼊函数 recv 和 recvfrom 参数不变,⽽改变 recvmsg 和 sendmsg 所⽤的 msghdr 结构,该结构按引⽤传递,内核返回时会修改其中的 msg_flags 成员标志,如果⼀个进程需要内核更新标志,就需要调⽤ recvmsg,⽽不是调⽤ recv 或 recvfrom。
套接字读缓冲区的数据量
有⼀种情况就是我们不想读取数据,但想知道⼀个套接字缓冲区中有多少数据可以读取。有以下单个⽅法可以⽤于获得排队的数据量:
1. 如果知道已排队数据量的⽬的在于避免读操作阻塞在内核中,那么可以使⽤⾮阻塞IO。
2. 如果即想查看数据,⼜想数据仍然留在接收队列中以供本进程其他部分稍后读取,可以使⽤ MSG_PEEK 标志。如果我们这么做,然
⽽不能肯定是否真有数据可读,那么可以结合⾮阻塞套接字使⽤该标志。对⼀个流式(TCP)套接字
⽽⾔,指定 MSG_PEEK 两次调⽤ recv,返回值可能不⼀样,因为这两次调⽤之间 TCP 可能⼜收到了⼀些数据。对于⼀个 UDP 套接字假设接收缓冲区中已有数据报,指定 MSG_PEEK 调⽤ recvfrom ⼀次,稍后不指定该标志再调⽤ recvfrom ⼀次,那么即使另有数据报在这两次调⽤之间加⼊该套接字的接收缓冲区,这两个调⽤的返回值完全相同。
3. ⼀些实现⽀持 ioctl 的 FIONREAD 命令。该命令的第三个 ioctl 参数是指向某个整数的⼀个指针,内核通过该整数返回的值就是套接
字接收缓冲区的当前字节数。该值是已排队字节的总和,对于 UDP 套接字⽽⾔包括所有已排队的数据报。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论