关于Socket的一些细节

前记

  最近几天在排除一个软件的BUG,一段看似简单且没有问题的代码却带来了灾难性的错误。由此想到总结一下关于socket操作的一些细节。要知道,事情总是败在细节上,尽管这段代码只有两行,经过修改后也不超过10行,但是让两个人忙活了至少两天的时间,这还不包括后续的重新发布管理的所消耗的人力和时间。下面论述的一些代码是针对C#语言的。

 

问题总结

1. TCP连接中,最后释放连接要经过四次握手,每次握手后,不论都客户端还是服务都会处于一个特定的状态,这几个状态是什么?

答案:看以下的交互图:

         Client                  消息                   Server

         close()

                            ------ FIN ------->

        FIN_WAIT1                                 CLOSE_WAIT

                            <----- ACK -------

        FIN_WAIT2 

                                                     close()

                            <------ FIN ------  

        TIME_WAIT                               LAST_ACK      

                           ------ ACK ------->  

                                                    CLOSED

         CLOSED

可知,客户端会依次处于FIN_WAIT1, FIN-WAIT2, TIME_WAIT, CLOSED四个状态;服务器端会依次处于CLOSE_WAIT, LAST_ACK, CLOSED三个状态。

 

2. 使用socket进行阻塞的接收操作,是调用Receive函数来实现。如果一方在等待接收,而另一方主动关闭了连接,阻塞会立即停止,那么会发生以下几种操作中哪一种呢?

返回0

返回负数

抛出异常

正在等待接收,表示连接是正常的。另一方主动关闭连接后,接收方会返回0.注意在阻塞的交互模式下,Receive函数不会因为接收时的网络问题而抛出异常。

 

3. Receive这个函数,又有返回值,又会抛出异常。而返回值又不单单表示接收的数量,还会表示其它的意思,设计者为什么要这么做呢?为什么不统一到抛出异常或通过返回值来表达正确还是错误呢?

抛出异常只是对参数进行验证时,或调用其它函数时抛出的异常,对于接收的错误,只是返回0或负数。返回0表示socket已关闭,返回负数表示操作失败。

 

4. 发送方通过阻塞方式地发送数据,调用的是Send函数,当在发送数据的时候,接收方关闭了连接,或者网络中断了?Send函数会发生哪种行为?

返回0

返回负数

返回已经发送的数据的长度

抛出异常

还是返回0

 

5. Poll函数是干什么用的?在操作过程中必需用吗?如果用,有什么好处,如果不用有什么坏处?

其行为非常像Mutex,用来等待socket某个状态的有效。比如说,如果正在操作的socket的状态是读取,而现在又要写入,则需要等读取完了之后再写入,用Poll可以使当着线程阻塞一直等到socket可写。同样,如果要读的时候也是这样。用Poll得保证了安全地读和写。

 

6. 同样是关闭,却有ShutdownClose两个函数,它们有什么区别呢?在实际应用中,应该怎么正确地使用它?

Shutdown关闭网络,它带有三个参数:只关闭读通道、只关闭写通道、关闭读写通道。通过Reflector反编译查看,可以看到,在Close中调用了Dispose函数,而在Dispose函数中主要做了两件事,关闭发送通道(写通道)和销毁socketHandle

在实际使用中,如果单独使用Shutdown来关闭发送通道,则无法销毁Handle,如果只使用Close来关闭,则又没有关闭网络的接收通道,可能造成数据丢失。因此,最好的是使用Shutdown来关闭网络通道,再用Close来做完全的清理工作。

 

7. 有些服务器程序,在运行一段时间后,就出现了大量的CLOSE_WAIT连接(用netstat -na可以看到),分析为什么会出现这样的情况?

CLOSE_WAIT一般是由于一端调用了Close而另外一端没有调用Close造成的。当主动关闭的一方发送FIN到被动关闭这边后,被动关闭这边的TCP马上回应一个ACK过去,同时向上面应用程序提交一个ERROR,导致上面的SOCKETsend或者recv返回SOCKET_ERROR,正常情况下,如果上面在返回SOCKET_ERROR后调用了 closesocket,那么被动关闭的者一方的TCP就会发送一个FIN过去,自己的状态就变迁到LAST_ACK. (部分答案来自网络)

 

 

原文地址:https://www.cnblogs.com/qkhh/p/1807245.html