今天继续socket编程的学习,最近晚上睡觉都没有发热,没有暖气的日子还是种煎熬,快乐的十一也已经走来,幸福有暖气的日子也快啦,好了,回到正题~
![](https://i-blog.csdnimg.cn/blog_migrate/edfe500070adff04a5ff299f59de2498.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9c086d0d18a829daf5b6e3202a8af7fc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9d3114f5faaaa17cf4f34d4f8a079912.png)
![](https://i-blog.csdnimg.cn/blog_migrate/19e907831827796b64c02851b4382032.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2bfe9f012fc4a8e6eae6bc6a8974cb1e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1e73bc570a34e88ac440cb87abc46822.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fac2a1bcec298dd827e72443773451ee.png)
对于上面提到的,要想让client只终止写的一端,可以用shutdown设置成SHUT_WR,也就是how=1:
![](https://i-blog.csdnimg.cn/blog_migrate/2673a88d740f6d38ab6203798e4ec694.png)
从上面的解释中来看:"close不能保证,直到套接字引用计数减为0时才发送。也就是说直到所有的进程都关闭了套接字",并不是只要调用了close就会立马向对方发送FIN tcp段,而是需要引用计数减为0时才发送,回想一下原来用fork()实现服务端的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/4e49b28c408ee78f41a746d68f1ad14c.png)
而如果改用shutdown:
![](https://i-blog.csdnimg.cn/blog_migrate/b85d40e694687b5ff1a9948e2aaedb65.png)
说了一系列理论过后,下面用实验来进一步加深对shutdown的理解,也就是用shutdown来改写客户端。
![](https://i-blog.csdnimg.cn/blog_migrate/6fa1a85e73c045491ff25e5419842688.png)
目前我们的回射客户/服务端程序都已经改用select函数来实现了,下面我们来做一个这样的实验:
![](https://i-blog.csdnimg.cn/blog_migrate/650b0ff366140efe606daf88535b725e.png)
为了进行以上实验流程,需要改造一下实验环境,于是乎需小调整一下代码:
echocli.c:
![](https://i-blog.csdnimg.cn/blog_migrate/db715550be4fffec92fb1d5e02c05ebf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8f5b9dca3ef940e2c7a8cac4a0c65bc2.png)
echosrv.c:
![](https://i-blog.csdnimg.cn/blog_migrate/cf15d3d6e80c6f3fe5ab1f6df462ecdd.png)
![](https://i-blog.csdnimg.cn/blog_migrate/11407e87f8929a4da5ca7c74f753324d.png)
下面来看下效果:
![](https://i-blog.csdnimg.cn/blog_migrate/321615d74703210ae6fab9c98ab0107e.gif)
可见,并没回将数据回射回客户端,并且客户端这边报了一个错:
![](https://i-blog.csdnimg.cn/blog_migrate/f7dd4c85693d2bf3ff498191216ace99.png)
这是哪打印出来的呢?分析一下代码流程:
![](https://i-blog.csdnimg.cn/blog_migrate/8c3115336be0f215666132d1b22f2ecb.png)
而且客户端的这个错误,还会导致服务端也崩溃掉了。
那如果这样处理是否可以避免这个报错呢?
![](https://i-blog.csdnimg.cn/blog_migrate/dcb91d8b65dfa2fcd4d7c43db55496f8.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/20da011e2814137dfb388d784575e9bb.gif)
可以看出,客户端并没有报错了,但是服务端还是崩溃了,这是为什么呢?
![](https://i-blog.csdnimg.cn/blog_migrate/9873dcb43d74d90cf9e184f34d04b745.png)
所以,解决服务端崩溃很简单,处理如下:
![](https://i-blog.csdnimg.cn/blog_migrate/d2556b48e78aa3a3b523d4fe27d7fc5d.png)
再次编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/ccc0a86b94a444684c4ae89e69d2dc63.gif)
这时,可以看到服务端打印了"client close",而且没有崩溃了,这是由于执行到了这个流程:
![](https://i-blog.csdnimg.cn/blog_migrate/5815d58baf00bfd20b0fa1117d338511.png)
但是目前残留在管道当中的数据都无法回显给客户端,所以接下来用shutdown来进行改进,让它在客户端关闭的情况下还能回射回来:
![](https://i-blog.csdnimg.cn/blog_migrate/a0894e36ff16c0cac016720e2efc339c.png)
编译如下:
![](https://i-blog.csdnimg.cn/blog_migrate/02c39fe4e3e53ac22e3eb56d2c86084f.gif)
从上面可以看到,客户端成功回显了,但是,发现有个小问题,就是当客户端回显数据之后,服务端没有把客户端给关闭掉,所以,程序应该有个bug,检查一下服务端的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/9c54cfeb67010b6a3298b15aac0588c3.png)
所以,修改如下:
![](https://i-blog.csdnimg.cn/blog_migrate/dd82e37e1e0f81a56348a3941a8080d7.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/57c9eb8f6e39f9e68fc13a16287103c8.gif)
这时问题就成功解决,此时的客户端就相对要完善一些了,实际上在编写TCP程序时是需要考虑到这一点的,实际上客户端程序还有一个地方是需要注意的:
![](https://i-blog.csdnimg.cn/blog_migrate/0d2869285323c42167f21abfdb6e86a5.png)
所以可以加入一个flag进行处理:
![](https://i-blog.csdnimg.cn/blog_migrate/e1e3b2b10ffee6f401a10d79faa91762.png)
当然程序运行效果是一样的,只是程序更严谨一些,好了,下次学习继续~