今天在自己写的C/S的Server端重启监听时遇到的问题,原因应该是正在Accept状态的listenSocket未能关闭,二次分配相同的端口时引发了异常。网上查看了多人的观点,随手记一下。
大致的处理办法有两类:
一是想办法把端口关掉;二是使用端口复用忽略掉这种异常。
第一类办法有两种解决方法:
A.自定义一个消息,想关闭端口时直接把这个消息传给监听的端口,而监听端也要在收到消息后,针对这个特定的消息编写类似listenSocket.Close()的代码;
B.Socket想办法设置成线程外部可访问类型,比如在线程外部定义,作为参数传递进线程;或是定义为全局变量。要关闭端口时直接在外部使用listenSocket.Close(),从而引发异常,使程序从监听阻塞的Accept()状态跳出来,当然listenSocket.Accept()要用Try...Catch来屏蔽掉这种异常。
第二类办法就是使用端口复用,做端口绑定前,使用listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);将绑定的端口设置为可复用,直接屏蔽掉这种异常。
本人偷懒,直接用了端口复用,代码少啊。虽然对这种技术也不是很了解,但感觉上应该会有部分资源没有释放掉,如果频繁使用恐怕会影响Server的性能【端口收到消息后需要判断到底发给哪个Socket,虽然是自动判断的,但总归会影响一些效率吧】
用特定消息结束端口占用也是我第一感觉上想用的办法,后来想了想,如果被别人知道我用的消息结构,我的Server监听岂不是随时可以被别人关闭!好吧,我承认关闭也没什么损失...
所以,比较正规的办法应该是使用线程外可访问的listenSocket。只是要重新调整代码结构,稍复杂一些。这里有个链接:http://blog.csdn.net/kingfox/article/details/7233350
顺手把内容也Copy过来,以下是原文代码:
刚刚学习C#,在编写一个网络通讯的程序的时候,遇到了点麻烦。监听代码是放在一个线程中,当在线程中调用Socket.Accept()函数时,倘若这时需要中止该线程,C#似乎没有提供现成的办法,使用了Thread.Abort()和Thread.Interrupt()函数,都没有用。有人说用异步Accept方法避免阻塞,可是用这种方法就得在线程中不停地轮询Socket的状态,会导致CPU负荷增加。还有人提出可以现在程序内部创建一个对侦听Socket的连接,然后发送特定的推出数据序列,当监听程序收到这个特殊序列后就主动结束线程。这个方法虽然可以解决问题,但是未免复杂了些。
想来想去,突然想到如果将监听socket关闭掉,引发socket异常,然后在监听线程中捕获这个异常不就可以中止监听线程了吗,试验了一下,果然可以。监听线程的代码如下:
创建线程的代码如下:
中止线程的代码如下:
调用线程类的stop函数之后,会将处于监听远程连接的listenSocket关闭掉,这时会导致引发System.Net.Sockets.SocketException,在线程代码中捕获并处理这个异常就行了。这种方法实现简单,也不会产生额外的CPU资源。