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.youkuaiyun.com/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资源。
<think>嗯,用户遇到了TCP端口绑定失败的问题,错误信息是“通常只允许每个套接字地址协议/网络地址/端口使用一次”。我需要帮他们解决这个问题。首先,我得回忆一下端口绑定的相关知识。根据引用[1],当多个程序试图绑定到同一个IP地址端口时,就会发生冲突。比如,如果有程序已经绑定了127.0.0.1:11434,另一个程序再尝试绑定同样的地址就会失败。 接下来,我应该检查常见的可能原因。首先,可能是已经有程序在运行并占用了这个端口。用户需要确认是否有其他进程正在监听该端口。可以使用像netstat或lsof这样的工具来查找占用端口的进程。比如在Windows上用netstat -ano | findstr 11434,或者在Linux上用lsof -i :11434。找到进程后,终止它或者重启系统可能解决问题。 然后,考虑套接字选项的设置。引用[2]中提到套接字的创建过程,但这里可能需要设置SO_REUSEADDR选项。如果在绑定前设置了这个选项,允许地址重用,可能解决端口被占用的问题。不过,这个选项的具体效果可能因操作系统而异,需要正确应用。 另外,根据引用[1],如果程序绑定到0.0.0.0:11434,它不会接收发送到127.0.0.1:11434的消息,但如果有其他程序绑定了特定IP的端口,比如127.0.0.1:11434,那么冲突还是会发生。用户需要确保没有其他程序绑定了相同的地址端口组合。 还有,程序异常终止可能导致端口未被释放。比如,进程崩溃后,操作系统可能没有立即释放端口,处于TIME_WAIT状态。这时候可能需要等待一段时间,或者使用SO_REUSEADDR来快速重用端口。 用户可能需要检查代码中的套接字绑定逻辑,确保正确设置SO_REUSEADDR,并且正确关闭套接字。此外,确保没有多个实例的程序同时运行,或者配置文件中端口号被重复使用。 总结下来,解决步骤应该是:检查端口占用情况,终止占用进程;设置套接字选项允许地址重用;确保程序正确关闭套接字;检查代码逻辑是否有重复绑定。同时,引用相关的资料来支持这些步骤,比如引用[1]中的端口绑定规则和引用[2]中的套接字创建方法。</think>### 解决 TCP 端口绑定失败问题 #### 1. 检查端口占用情况 错误提示表明端口 `127.0.0.1:11434` 已被其他进程占用。需通过以下步骤确认: - **Windows** 使用命令: ```bash netstat -ano | findstr 11434 ``` 查看占用端口的进程 ID(PID),并通过任务管理器终止该进程[^1]。 - **Linux/MacOS** 使用命令: ```bash lsof -i :11434 ``` 或 ```bash netstat -tuln | grep 11434 ``` 终止进程使用: ```bash kill -9 <PID> ``` #### 2. 设置套接字地址重用(SO_REUSEADDR) 在绑定套接字前,通过设置 `SO_REUSEADDR` 选项允许地址复用,即使端口处于 `TIME_WAIT` 状态也能重新绑定[^2]: ```c int sockfd = socket(AF_INET, SOCK_STREAM, 0); int opt = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); ``` #### 3. 确保程序正确关闭套接字 程序异常终止可能导致套接字未关闭,需检查代码逻辑: - 调用 `close()` 或 `shutdown()` 函数显式关闭套接字。 - 服务器程序需正确处理 `SIGINT` 或 `SIGTERM` 信号,释放资源[^3]。 #### 4. 避免多个实例绑定同一端口 若程序允许多实例运行,需确保配置文件或启动参数中端口号不重复。 #### 5. 检查绑定 IP 地址的优先级 根据绑定规则[^1]: - 若已有程序绑定 `0.0.0.0:11434`,其他程序仍可绑定 `127.0.0.1:11434`。 - 若已有程序绑定 `127.0.0.1:11434`,其他程序无法再绑定该地址或 `0.0.0.0:11434`。 --- ### 操作流程图 ```plaintext 检查端口占用 → 终止占用进程 → 设置 SO_REUSEADDR → 重启程序 → 成功绑定 │ ↑ │ └─失败───────────┴─调整绑定逻辑或端口号 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值