一道高频腾讯面试题tcp数据发送问题
问题引出
好几个读者私信说在腾讯面试过程中,被面试官问到了一个问题:一个tcp服务端和一个tcp客户端,客户端和服务端建立连接后,服务端一直sleep,然后客户端一直发送数据会是什么现象。
要回答这个问题,需要我们清楚tcp协议的特点和tcp发送数据的大体过程。
tcp发送数据过程
恐怕接触过网络的同学都知道tcp是面向连接的可靠传输协议,意味着客户端发送的数据服务端是一定能够收到的,那么对于上面的问题就不可能存在数据的丢弃。下面我们分析一下tcp的传输过程。
如图所示,tcp数据包的传输过程主要有如下几个步骤:
1。应用程序调用write系列函数发送数据,数据首先由应用程序缓冲区复制到发送端的内核中的套接字发送缓冲区,然后write成功返回;需要特别注意的是write成功返回只是说明数据成功的由应用进程缓冲区复制到了套接字发送缓冲区,并不代表数据发送到了对端主机。2。内核协议栈将套接字发送缓冲区中的数据发送到对端主机,这个过程不受应用程序控制,而是发送端内核协议栈完成;3。数据到达接收端主机的套接字接收缓冲区,注意这个接收过程也不受应用程序控制,而是由接收端内核协议栈完成;4。数据由套接字接收缓冲区复制到接收端应用程序缓冲区,注意这个过程是由类似read等函数来完成。
清楚了tcp的传输过程,现在我们分情况来讨论上面的问题。
相关视频推荐
从websocket协议到tcp自定义协议,tcp分包与粘包
90分钟搞定tcpip协议栈
TCPIP协议栈,一次课开启你的网络之门
LinuxC后台服务器开发架构师免费学习地址:CCLinux鏈嶅姟鍣紑鍙鍚庡彴鏋舵瀯甯堛愰浂澹版暀鑲层瀛範瑙嗛鏁欑鑵捐璇惧爞
【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!(需要视频资料后台私信1自取)
阻塞方式的情况
write系列函数的工作方式默认是阻塞方式:调用write函数时,内核从应用进程的缓冲区到套接字的发送缓冲区复制数据。如果其发送缓冲区中没有空间,进程将进入睡眠,直到有空间为止。
因此,阻塞方式下,如果服务端一直sleep不接收数据,而客户端一直write,也就是只能执行上述过程中的前三步,这样最后接收端的套接字接收缓冲区和发送端套接字发送缓冲区都被填满,这样write就无法继续将数据从应用程序复制到发送端的套接字发送缓冲区了,从而发送端进程进入睡眠。可以用下面的程序验证。
tcpClient。c是客户端代码用来发送数据,客户端每次write成功一次,将计数器count加1,同时输出本次write成功的字节数。count保存客户端write成功的次数。
includestdio。hincludestring。hincludeunistd。hincludesystypes。hincludesyssocket。hincludestdlib。hincludememory。hincludeincludenetinetin。hdefinePORT8888defineBuflen1024intmain(intargc,charargv〔〕){structsockaddrinserveraddr;intn,count0;intsockfd;charsendline〔Buflen〕;sockfdsocket(AFINET,SOCKSTREAM,0);memset(serveraddr,0,sizeof(serveraddr));serveraddr。sinfamilyAFINET;serveraddr。sinporthtons(PORT);serveraddr。sinaddr。saddrhtonl(INADDRANY);serveraddr。sinaddr。saddrinetaddr(argv〔1〕);connect(sockfd,(structsockaddr)serveraddr,sizeof(serveraddr));与服务器端进行通信memset(sendline,x,sizeof(Buflen));while((nwrite(sockfd,sendline,Buflen))0){count;printf(alreadywritedbytesd,n,count);}if(n0)perror(writeerror);close(sockfd);}
下面的tcpServer。c是服务端程序,服务端并不接收数据。
includestdio。hincludestdlib。hincludestrings。hincludesystypes。hincludesyssocket。hincludememory。hincludeunistd。hincludenetinetin。hincludeincludestring。hdefinePORT8888定义通信端口defineBACKLOG5定义侦听队列长度definebuflen1024intlistenfd,connfd;intmain(intargc,charargv〔〕){structsockaddrinserveraddr;存储服务器端socket地址结构structsockaddrinclientaddr;存储客户端socket地址结构pidtpid;listenfdsocket(AFINET,SOCKSTREAM,0);memset(serveraddr,0,sizeof(serveraddr));serveraddr。sinfamilyAFINET;协议族serveraddr。sinaddr。saddrhtonl(INADDRANY);本地地址serveraddr。sinporthtons(PORT);bind(listenfd,(structsockaddr)serveraddr,sizeof(serveraddr));listen(listenfd,BACKLOG);for(;;){socklentaddrlensizeof(clientaddr);connfdaccept(listenfd,(structsockaddr)clientaddr,addrlen);if(connfd0)perror(accepterror);printf(receiveconnection);if((pidfork())0){close(listenfd);sleep(1000);子进程不接收数据,sleep1000秒exit(0);}else{close(connfd);}}}
首先编译运行服务端,然后启动客户端,运行结果如下:
可以看到客户端write成功377次后就陷入了阻塞,注意这个时候不能说明发送端的套接字发送缓冲区一定是满的,只能说明套接字发送缓冲区的可用空间小于write请求写的自己数1024。
非阻塞方式的情况
下面看一下非阻塞套接字情况下,write的工作方式:对于一个非阻塞的TCP套接字,如果发送缓冲区中根本没用空间,输出函数将立即返回一个EWOULDBLOCK错误。如果发送缓冲区中有一些空间,返回值将是内核能够复制到该缓冲区的字节数。这个字节数也成为不足计数。
这样就可以知道非阻塞情况下服务端一直sleep,客户端一直write数据的效果了:开始客户端write成功,随着客户端write,接收端的套接字接收缓冲区和发送端的套接字发送缓冲区会被填满。当发送端的套接字发送缓冲区的可用空间小于write请求写的字节数时,write立即返回1,并将errno置为EWOULDBLOCK。
可以用下面的程序验证,其中,服务端程序代码和上面例子一样,我们只看客户端非阻塞模式代码:
includestdio。hincludestring。hincludeunistd。hincludesystypes。hincludesyssocket。hincludestdlib。hincludememory。hincludeincludenetinetin。hincludefcntl。hincludeerrno。hdefinePORT8888defineBuflen1024intmain(intargc,charargv〔〕){structsockaddrinserveraddr;intn,flags,count0;intsockfd;charsendline〔Buflen〕;sockfdsocket(AFINET,SOCKSTREAM,0);memset(serveraddr,0,sizeof(serveraddr));serveraddr。sinfamilyAFINET;serveraddr。sinporthtons(PORT);serveraddr。sinaddr。saddrhtonl(INADDRANY);serveraddr。sinaddr。saddrinetaddr(argv〔1〕);connect(sockfd,(structsockaddr)serveraddr,sizeof(serveraddr));flagsfcntl(sockfd,FGETFL,0);将已连接的套接字设置为非阻塞模式fcntl(sockfd,FSETFL,flagsONONBLOCK);memset(sendline,a,sizeof(Buflen));while((nwrite(sockfd,sendline,Buflen))0){count;printf(alreadywritedbytesd,n,count);}if(n0){if(errno!EWOULDBLOCK)perror(writeerror);elseprintf(EWOULDBLOCKERROR);}close(sockfd);}
首先编译运行服务端,然后启动客户端,运行结果如下图所示。
编辑
可以看到客户端成功write185次后就发生套接字发送缓冲区空间不足,从而返回EWOULDBLOCK错误。我们注意到每次write同样的字节数(1024)阻塞模式下能write成功377次,为什么非阻塞情况下要少呢?
这是因为阻塞模式下一直write到接收端的套接字接收缓冲区和发送端的套接字发送缓冲区都满的情况才会阻塞。而非阻塞模式情况下有可能是发送端发送过程的第二步较慢,造成发送端的套接字发送缓冲区很快写满,而接收端的套接字接收缓冲区还没有满,这样write就会仅仅因为发送端的套接字发送缓冲区满而返回错误。
原文地址:https:mp。weixin。qq。comsrpNTjTUt19Bbyx6IWm2ig
人胎盘片能促怀孕吗?人胎盘片治不孕人胎盘片是一款非常适合女性吃的药,对于怀孕很有帮助,下面5号网的小编为你们介绍人胎盘片能促怀孕吗?人胎盘片治不孕。人胎盘片能促怀孕吗人胎盘片吃了对怀孕有一定的作用,人胎盘……
人胎盘片能长内膜吗?长内膜药物推荐很多女性吃长内膜的药,觉得药效不好,下面5号网的小编为你们介绍人胎盘片能长内膜吗?长内膜药物推荐。人胎盘片能长内膜吗胎盘片可以补身体,延缓衰老,但不是专门治疗子宫内膜薄的……
央行连续三个月超额续作MLF,当前降息可能性有多大?记者王玉中国人民银行周三进行了4990亿元一年期中期借贷便利(MLF)操作,超额对冲本月到期的3000亿元MLF,连续3个月加量续作,利率维持在2。75不变。分析师……
飞蚊症可以戴隐形眼镜吗?飞蚊症戴隐形眼镜好吗?飞蚊症是一种眼部的疾病,常常伴有视力模糊的症状,今天5号网小编就要为大家解答一下,飞蚊症可以戴隐形眼镜吗?飞蚊症戴隐形眼镜好吗?飞蚊症可以戴隐形眼镜吗最好是不要戴,……
三七可以泡酒吗?三七泡酒的功效与作用三七是非常好的一味中药,今天5号网小编就要为大家介绍一下,三七可以泡酒吗?三七泡酒的功效与作用是什么?三七可以泡酒吗三七可以泡酒,并且可以搭配不同的材料有着不一样的功效!……
孕妇如厕时生产婴儿被卡蹲便器网友打趣接生员是消防员上个厕所就把宝宝生下来了?宝宝还被卡在蹲便器中。网友称不知是先心疼宝宝还是先羡慕孕妈这么顺利的生下婴儿。2月12日,广西玉林,大塘镇卫生院一临产孕妇上厕所时,突然提前生产……
点菜的技巧有哪些?怎样点一桌好菜?其实吃饭时一门艺术,菜品搭配可以体现一个人的品味和修养。菜品讲究荤素汤品的合理搭配,这样大家才可以吃得开心舒适。点菜的技巧有哪些?怎样点一桌好菜?点菜的技巧有哪些先……
宫寒怎么调理比较快教你5个暖宫小方法说到宫寒相信很多女生都有这个毛病,宫寒是指妇女肾阳不足,平时生活中应该多注意保暖,少吃生冷的食物,通过一些暖宫的食物再加上运动来调理。宫寒怎么调理比较快造成宫寒的原因很多……
沈腾摊上事了,春晚总导演被迫启动应急方案沈腾春晚现场改词,艾伦呆住。距春晚已经过去了三天,咱就说沈腾差点摊上事儿了,因为现场临场发挥,导致艾伦当场愣住了,原本14分钟的小品仅用了12分钟完成。总导演启动应……
最全的国企主题基金大盘点整体来看,国内的机构投资还是非常看重业绩增长的。不过,在实际的投资中,对于短期的业绩会给更高的权重,也就是非常看重行业的景气度,如果是有政策支持的新兴行业,且有着很大的想象空间……
达克宁要用几个疗程?达克宁多少钱一支?达克宁是比较知名的脚气药品,很多人不清楚这个用多久好,下面5号网的小编为你们介绍达克宁要用几个疗程?达克宁多少钱一支?达克宁要用几个疗程1、皮肤感染:外用,涂搽于洗净的患……
金戈一个月可以吃几次?金戈一天可服用2次吗?金戈是治疗ed的有效药物,但是也不能多吃,下面5号网的小编为你们介绍金戈一个月可以吃几次?金戈一天可服用2次吗?金戈一个月可以吃几次金戈一个月吃2次左右为好,吃多了有害。……