我是靠谱客的博主 幽默鼠标,最近开发中收集的这篇文章主要介绍Linux下,writeread,recvsend, recvfromsendto的区别Linux下,write/read,recv/send, recvfrom/sendto的区别,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Linux下,write/read,recv/send, recvfrom/sendto的区别

先说概念和应用场景:read/wirte是通用的文件描述符操作;recv/send 通常应用于TCP;recvfrom/sendto通常应用于UDP。

read/write

ssize_t write(int fd, const void*buf,size_t nbytes);

ssize_t read(int fd,void *buf,size_t nbyte);

​ write函数是将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量.。

​ read函数是从fd中读取内容.当读成功 时,read返回实际所读的字节数,如果返回的值是0 表示已经读到文件的结束了,小于0表示出现了错误。 如果是ECONNREST表示网络连接出了问题。

	write在什么情况下会阻塞?当kernel的该socket的发送缓冲区已满时。对于每个socket,拥有自己的send buffer和receive buffer。从Linux 2.6开始,两个缓冲区大小都由系统来自动调节(autotuning),但一般在default和max之间浮动。关于socket内核相关参数查看设置,可参考 https://www.cnblogs.com/wuchanming/p/4028341.html。

linux的write和read接口描述有这么两句话:ERRORS
   EAGAIN The file descriptor fd refers to a file other than a  socket  and  has  been
          marked non-blocking (O_NONBLOCK), and the write would block.

​ 如果文件描述符不是socket的话,该函数是非阻塞的,否则该函数是阻塞的。 为了验证这个问题,进行如下实验,主要验证read函数的阻塞特性。

​ tcp已经发送到网络的数据依然需要暂存在send buffer中,只有收到对方的ack后,kernel才从buffer中清除这一部分数据,为后续发送数据腾出空间。接收端将收到的数据暂存在receive buffer中,自动进行确认。但如果socket所在的进程不及时将数据从receive buffer中取出,最终导致receive buffer填满,由于TCP的滑动窗口和拥塞控制,接收端会阻止发送端向其发送数据。这些控制皆发生在TCP/IP栈中,对应用程序是透明的,应用程序继续发送数据,最终导致send buffer填满,write调用阻塞。对于UDP来说就不会存在这种情况。

​ 一般来说,由于接收端进程从socket读数据的速度跟不上发送端进程向socket写数据的速度,最终导致发送端write调用阻塞。

recv/send

​ socket建立好了tcp连接之后,我们就可以把得到的fd当作文件描述符,此时可以使用read/write来操作。

​ 在网络程序中,当我们向套接字文件描述符读写时有两可能,需要我们自己处理:

​ write的返回值大于0,表示写了部分或者是全部的数据. 这样我们用一个while循环来不停的写入,但是循环过程中的buf参数和nbyte参数得由我们来更新。也就是说,网络写函数是不负责将全部数据写完之后在返回的(read同)。
​ write的返回值返回的值小于0,此时出现了错误.我们要根据错误类型来处理。(read同)如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接)。

​ recv和send函数提供了第四个参数来控制读写操作,依次解决上述问题。

int recv(int sockfd,void *buf,int len,int flags);
int send(int sockfd,void *buf,int len,int flags);

​ 前面的三个参数和read, write一样,第四个参数可以是0或者是以下的组合


​ | MSG_DONTROUTE | 不查找表 |
​ | MSG_OOB | 接受或者发送带外数据 |
​ | MSG_PEEK | 查看数据,并不从系统缓冲区移走数据 |
​ | MSG_WAITALL | 等待所有数据 |

​ MSG_DONTROUTE:是 send函数使用的标志.这个标志告诉IP.目的主机在本地网络上面,没有必要查找表.这个标志一般用网络诊断和路由程序里面。
MSG_OOB:表示可以接收和发送带外的数据.关于带外数据我们以后会解释的。

​ MSG_PEEK:是recv函数的使用标志, 表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志。

​ MSG_WAITALL 是recv函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或者是发生了错误. 1)当读到了指定的字节时,函数正常返回.返回值等于len 2)当读到了文件的结尾时,函数正常返回.返回值小于len 3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno)。

​ 这里只描述同步Socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的长度len和套接字的发送缓冲的长度,如果len大于发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于发送缓冲区的长度,那么send先检查协议 是否正在发送的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把发送缓冲中的数据发送完,如果len小于剩余 空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到发送缓冲区的剩余空间里)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如 果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执 行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)

​ 注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

​ 这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲 中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到 协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以 在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。

注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

// 设置一个文件描述符为nonblock
int set_nonblocking(int fd)
{
	int flags;
	if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
	flags = 0;
	return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

recvfrom/sendto

​ 利用数据报方式进行数据传输,在无连接的数据报socket方式下(UDP),由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址,

int recvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr *from, int *fromlen);
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

注:如果没有进行非阻塞设置的话,recvfrom和sendto默认都是阻塞的

文章参考链接:https://blog.csdn.net/sea_snow/article/details/112260750和https://blog.csdn.net/qq_31833457/article/details/78495354

最后

以上就是幽默鼠标为你收集整理的Linux下,writeread,recvsend, recvfromsendto的区别Linux下,write/read,recv/send, recvfrom/sendto的区别的全部内容,希望文章能够帮你解决Linux下,writeread,recvsend, recvfromsendto的区别Linux下,write/read,recv/send, recvfrom/sendto的区别所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部