C#Socket的ErrorCode的说明

本文详细介绍了 Winsock 错误代码及其含义,包括内存不足、参数错误、连接失败等多种常见错误情况,并解释了这些错误发生的原因及可能的解决办法。
部署运行你感兴趣的模型镜像

sckOutOfMemory   7   内存不足  
sckInvalidPropertyValue   380   属性值无效。  
sckGetNotSupported   394   属性不可读。  
sckSetNotSupported   383   属性是只读的。  
sckBadState   40006   所请求的事务或请求本身的错误协议或者错误连接状态。  
sckInvalidArg 40014 传递给函数的参数格式不确定,或者不在指定范围内。
sckSuccess   40017   成功。  
sckUnsupported   40018   不受支持的变量类型。  
sckInvalidOp   40020   在当前状态下的无效操作  
sckOutOfRange   40021   参数越界。  
sckWrongProtocol   40026   所请求的事务或请求本身的错误协议  
sckOpCanceled   1004   取消操作。  
sckInvalidArgument   10014   所请求的地址是广播地址,但未设置标记。  
sckWouldBlock   10035   套接字不成块,而指定操作将使之成块。  
sckInProgress 10036 制造块的 Winsock 操作在进行之中。
sckAlreadyComplete   10037   完成操作。未进行制造块的操作。  
sckNotSocket   10038   描述符不是套接字。  
sckMsgTooBig   10040   数据报太大,不适于缓冲区的要求,因而被截断。  
sckPortNotSupported   10043   不支持指定的端口。  
sckAddressInUse   10048   地址在使用中。  
sckAddressNotAvailable   10049   来自本地机器的不可用地址。  
sckNetworkSubsystemFailed   10050   网络子系统失败。  
sckNetworkUnreachable   10051   此时不能从主机到达网络。  
sckNetReset   10052   在设置   SO_KEEPALIVE   时连接超时。  
sckConnectAborted   11053   由于超时或者其它失败而中止连接。  
sckConnectionReset   10054   通过远端重新设置连接。  
sckNoBufferSpace   10055   没有可用的缓冲空间。  
sckAlreadyConnected   10056   已连接套接字。  
sckNotConnected   10057   未连接套接字。  
sckSocketShutdown   10058   已关闭套接字。  
sckTimedout   10060   已关闭套接字。  
sckConnectionRefused   10061   强行拒绝连接。  

WSAESERVERDOWN (10064) 服务器暂时连接不上

WSAESERVERUNREACH (10065)无法连接服务器可能是你的网络有问题 
sckNotInitialized   10093   应首先调用   WinsockInit。  
sckHostNotFound   11001   授权应答:未找到主机。  
sckHostNotFoundTryAgain   11002   非授权应答:未找到主机。  
sckNonRecoverableError   11003   不可恢复的错误。  
sckNoData   11004   无效名,对所请求的类型无数据记录。

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<think> 我们正在处理一个关于C# Socket编程中出现的“一个封锁操作被对 WSACancelBlockingCall 的调用中断”异常的问题。根据提供的引用,我们知道这个错误通常发生在网络操作(如接收或发送数据)被强制取消时,特别是在阻塞调用被WSACancelBlockingCall函数中断的情况下。我们将结合引用内容,分析原因并提供解决方案。 引用[1]和[2]指出,当我们在进行阻塞的网络操作(如UDPClient循环监听端口)时,如果调用了WSACancelBlockingCall来取消这个阻塞操作,就会抛出这个异常。引用[3]进一步说明,在关闭套接字时,如果有一个阻塞调用正在进行,关闭操作可能会触发WSACancelBlockingCall,导致错误码WSAEINTR(对应ErrorCode=10004)。 引用[4]提到了一种解决方案:使用非阻塞方式检查数据可用性(通过Socket.Poll方法)再读取数据,避免在阻塞操作中被中断。引用[5]则展示了一种在后台线程中执行耗时操作以避免界面卡死的方法,这间接避免了在主线程中阻塞操作被意外打断的情况。 综合以上信息,我们可以总结出以下解决方法: 1. **避免在阻塞操作时关闭Socket**:确保在关闭Socket之前,没有阻塞的网络操作(如Receive)正在进行。我们可以通过设置一个标志来通知线程退出,然后优雅地关闭。 2. **使用非阻塞方式**:如引用[4]所示,使用Poll方法检查数据是否可读,然后再进行读取操作,这样就不会长时间阻塞,从而减少被中断的机会。 3. **使用后台线程**:将耗时的网络操作放在后台线程中执行,如引用[5]所示,这样主线程(如UI线程)就不会被阻塞,同时可以安全地控制后台线程的终止。 4. **使用异步方法**:使用BeginReceive/EndReceive或.NET中更现代的异步API(如async/await)来避免阻塞操作。 下面我们针对这几种方法给出具体实现的建议: ### 方法1:优雅地关闭线程(使用标志位) 在循环监听中设置一个退出标志,当需要停止时,设置该标志,然后关闭Socket(这将导致阻塞的Receive操作抛出异常),在catch中捕获异常并判断是否为预期的退出。 示例代码: ```csharp private bool _isReceiving = true; private UdpClient _udpClient; // 接收线程 private void ReceiveThread() { try { while (_isReceiving) { IPEndPoint remoteEP = null; byte[] data = _udpClient.Receive(ref remoteEP); // 阻塞操作 // 处理数据... } } catch (SocketException ex) { // 当_isReceiving为false时,我们预期会退出,所以忽略特定的异常 if (_isReceiving) { // 非预期异常,需要处理 } } } // 停止接收 public void Stop() { _isReceiving = false; _udpClient.Close(); // 关闭UdpClient,导致Receive抛出异常 } ``` ### 方法2:使用非阻塞方式(Poll) 如引用[4]所示,使用Socket.Poll来检查是否有数据可读。注意,Poll方法的第一个参数是超时时间(微秒),-1表示无限等待,但这样还是阻塞的。所以我们可以设置一个较小的超时时间(比如100毫秒)来定期检查退出标志。 示例代码(使用Socket类,因为UdpClient底层也是使用Socket): ```csharp private bool _isReceiving = true; private Socket _socket; // 假定已经初始化 private void ReceiveLoop() { byte[] buffer = new byte[1024]; while (_isReceiving) { // 等待100毫秒,检查是否有数据可读 if (_socket.Poll(100000, SelectMode.SelectRead)) // 100000微秒=100毫秒 { int dataLength = _socket.Receive(buffer); if (dataLength > 0) { // 处理数据 } } } } ``` ### 方法3:使用异步接收 使用异步操作可以避免阻塞线程,并且更容易取消操作。 示例代码(使用异步方法): ```csharp private bool _isReceiving = true; private Socket _socket; private async Task ReceiveAsync() { byte[] buffer = new byte[1024]; while (_isReceiving) { try { var result = await _socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None); if (result > 0) { // 处理数据 } } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) { // 操作被取消,正常退出 break; } } } // 停止接收 public void Stop() { _isReceiving = false; _socket.Close(); // 关闭Socket,会取消异步操作 } ``` ### 总结 出现“一个封锁操作被对 WSACancelBlockingCall 的调用中断”异常,主要是因为我们试图在阻塞操作进行时取消它(比如关闭Socket)。解决方法的核心是:避免阻塞操作被强制中断,改为使用可控的方式退出阻塞操作(标志位+关闭Socket触发异常)或使用非阻塞/异步操作。 根据引用[4]和[5],我们推荐使用异步方法或非阻塞轮询方式,这样可以更优雅地处理关闭过程。同时,在关闭Socket之前,确保通知接收线程退出,然后关闭Socket。 [^1]: Socket网络编程中UDPClient断开时捕获的异常 [^2]: Winsock中WSACancelBlockingCall函数的作用和影响 [^3]: closesocket函数可能返回的错误代码 [^4]: 使用Poll方法避免阻塞操作被中断 [^5]: 在后台线程中执行耗时操作以避免界面卡死 §§相关问题§§ 1. 在C#中,如何安全地停止一个正在阻塞接收数据的线程? 2. Socket.Poll方法的工作原理是什么?它如何避免阻塞操作被中断? 3. C#中异步Socket编程有哪些最佳实践? </think> ### C# Socket 受 `WSACancelBlockingCall` 影响的解决方案 #### 问题原因分析 当 Socket 进行阻塞操作(如 `Receive` 或 `Send`)时,若外部强制中断(如关闭 Socket 或调用 `WSACancelBlockingCall`),会触发错误代码 `10004`(对应 `WSAEINTR`)[^1][^2][^3]。常见场景包括: 1. **多线程冲突**:后台线程阻塞接收数据时,主线程关闭 Socket。 2. **异步终止**:阻塞操作未正常退出,被强制取消。 3. **资源释放顺序错误**:先中断操作再关闭 Socket。 --- ### 解决方案 #### 方法 1:安全终止阻塞操作(推荐) 使用标志位 + `Close()` 触发可控中断,避免强制取消阻塞调用。 ```csharp private volatile bool _isRunning = true; // 接收线程 void ReceiveThread() { byte[] buffer = new byte[1024]; while (_isRunning) { try { int bytesRead = socket.Receive(buffer); // 阻塞操作 // 处理数据... } catch (SocketException ex) { if (ex.ErrorCode == 10004 && !_isRunning) { // 预期退出,不处理异常 } else { // 其他异常处理 } } } } // 安全停止方法 void Stop() { _isRunning = false; socket.Close(); // 触发阻塞操作的异常 } ``` #### 方法 2:非阻塞轮询(替代 `Receive`) 改用 `Poll()` 检查数据可用性,避免线程阻塞[^4]: ```csharp while (_isRunning) { if (socket.Poll(100000, SelectMode.SelectRead)) // 超时100ms { int bytesRead = socket.Receive(buffer); if (bytesRead > 0) { // 处理数据 } } Thread.Sleep(10); // 减少CPU占用 } ``` #### 方法 3:异步编程模式 使用 `BeginReceive`/`EndReceive` 异步 API: ```csharp private void StartReceive() { byte[] buffer = new byte[1024]; socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), buffer); } private void ReceiveCallback(IAsyncResult result) { if (!_isRunning) return; try { int bytesRead = socket.EndReceive(result); // 处理数据 StartReceive(); // 继续下一次接收 } catch (SocketException ex) { if (ex.ErrorCode != 10004) // 处理异常 } } ``` #### 方法 4:后台线程隔离(UI 应用专用) 在独立线程中运行阻塞操作,避免干扰 UI 线程[^5]: ```csharp // 启动线程 Thread receiveThread = new Thread(ReceiveThread); receiveThread.IsBackground = true; receiveThread.Start(); // 停止时 _isRunning = false; socket.Close(); // 安全中断 receiveThread.Join(500); // 等待线程退出 ``` --- ### 关键注意事项 1. **线程同步**:共享资源(如 `_isRunning`)需使用 `volatile` 或锁保护。 2. **关闭顺序**: - 先设置停止标志 `_isRunning = false` - 再调用 `socket.Close()` 3. **异常处理**:捕获 `SocketException` 后需过滤 `ErrorCode == 10004`(预期中断)。 4. **套接字复用**:参考 TCP/UDP 端口复用方案[^4],避免地址占用冲突。 > 示例引用场景: > - 后台线程阻塞接收数据时,主线程关闭 Socket 触发 `WSACancelBlockingCall`[^1] > - 未处理异步操作终止导致的传输连接中断[^2] > - 阻塞调用中执行 `closesocket()` 引发 `WSAEINTR` 错误[^3] [^1]: UDP 接收循环中关闭 Socket 引发的阻塞中断 [^2]: TCP Socket 传输连接读取失败问题分析 [^3]: `closesocket` 函数触发 `WSAEINTR` 的条件说明 [^4]: 非阻塞轮询 (`Poll`) 避免操作中断的方案 [^5]: 后台线程隔离阻塞操作的实现方法
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值