socket:通常每个套接字地址(协议/网络地址/端口)只允许使用一次

今天在自己写的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异常,然后在监听线程中捕获这个异常不就可以中止监听线程了吗,试验了一下,果然可以。监听线程的代码如下:

[csharp]  view plain  copy
  1. using System;  
  2. using System.IO;  
  3. using System.Net.Sockets;  
  4. using System.Net;  
  5.   
  6. public class ListenThread  
  7. {  
  8.    public void run()  
  9.    {  
  10.       Console.Write("creating listen socket ...");  
  11.       listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
  12.       listenSocket.Bind(new IPEndPoint(IPAddress.Any, 65365));  
  13.       listenSocket.Listen(0);  
  14.       Console.Write("    done.\n");  
  15.   
  16.       try  
  17.       {  
  18.          Console.Write("listening ...");  
  19.          ioSocket = listenSocket.Accept();  
  20.          Console.Write("    accepted.\n");  
  21.   
  22.          Console.Write("creating I/O thread ...");  
  23.          // new Thread(new ThreadStart(this.networkIOThreadProc)).Start();  
  24.          Console.Write("    done.\n");  
  25.       }  
  26.       catch (Exception e)  
  27.       {  
  28.          Console.WriteLine("Thread aborted.");  
  29.       }  
  30.       finally  
  31.       {  
  32.          Console.WriteLine("Thread resource released.");  
  33.       }  
  34.    }  
  35.   
  36.    public void stop()  
  37.    {  
  38.       if (listenSocket != null)  
  39.       {  
  40.          listenSocket.Close();  
  41.       }  
  42.    }  
  43.   
  44.    private Socket listenSocket = null;  
  45.    private Socket ioSocket = null;  
  46.   
  47. }  

创建线程的代码如下:

[csharp]  view plain  copy
  1. ListenThread listener = new ListenThread();  
  2. Thread listenThread = new Thread(new ThreadStart(listener.run));  
  3. listenThread.Start();  

中止线程的代码如下:

[csharp]  view plain  copy
  1. listener.stop();  

调用线程类的stop函数之后,会将处于监听远程连接的listenSocket关闭掉,这时会导致引发System.Net.Sockets.SocketException,在线程代码中捕获并处理这个异常就行了。这种方法实现简单,也不会产生额外的CPU资源

猜你喜欢

转载自blog.csdn.net/liufeifeinanfeng/article/details/79713495