【问题标题】:C# Socket.Receive() SocketException: A request to send or receive data was disallowed because the socket is not connectedC# Socket.Receive() SocketException: 不允许发送或接收数据的请求,因为套接字未连接
【发布时间】:2015-07-29 22:55:41
【问题描述】:

我正在努力使用 C# 中的 Windows Socket 编程,在 Windows 8.1 上使用 .Net 4.5。

所以,我正在做一个非常简单的操作。我正在实现一个客户端-服务器类型的系统,所有这些系统都将在本地机器上运行,通过套接字进行通信。服务器是 Windows 服务,客户端是用户应用程序。

服务根据客户端的请求执行一些 OCR 计算,然后将结果返回给请求进程。该服务有一个监听套接字,它随时接受传入的请求,一个新的套接字被创建并用于响应每个请求。新响应套接字的端口号在请求中指定。这样做是为了让监听套接字能够处理并发请求。

服务的监听套接字工作正常。我能够从连接到侦听套接字的用户应用程序接收请求。当角色互换时会出现问题:服务需要连接到客户端拥有的 Socket,并通过客户端拥有的套接字发送 OCR 结果。让我们进入代码:

服务器端

this.LocalAddress = Dns.GetHostEntry("localhost").AddressList[0]; // localhost ip addr

...
//*************** Receive request
//*************** Perform computation
...

// respond to requester with results from OCR 
using (Socket responder = new Socket(this.LocalAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
{
    IPEndPoint requestEndpoint = new IPEndPoint(this.LocalAddress, request.portNum);
    responder.Connect(requestEndpoint); //same endpoint as client

    if(failure)
        responder.Send(Encoding.UTF8.GetBytes(this.OCRFailureMessage));
    else
        responder.Send(Encoding.UTF8.GetBytes(ocrResults.ToString()));
}

没有抛出异常,服务继续,好像一切都成功了。

客户端

//localhost address
IPAddress address = Dns.GetHostEntry("localhost").AddressList[0]; // localhost ip addr

// endpoint to listen on for response from StartOCRService
IPEndPoint myEndpoint = new IPEndPoint(address, 50002);

// create socket to receive response on and bind to listening endpoint
Socket receiver = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
receiver.Bind(myEndpoint);
receiver.Listen(10);

...
//************ Connect to service listening socket
//************ Send OCR request to service (myEndpoint port# included)
...

// accept connection from service
receiver.Accept();

// receive data from service
byte[] returnData = new byte[1024];
int numBytesReceived = receiver.Receive(returnData); //EXCEPTION THROWN

在客户端调用receiver.Receive() 时抛出异常。以下是异常详情:

类型:SocketException

消息:发送或接收数据的请求被禁止,因为套接字未连接并且(当使用 sendto 调用在数据报套接字上发送时)没有提供地址。

错误代码:10057(未连接套接字)

所以,我被挂断的地方是客户端的套接字未连接。 receiver.Accept() 行是一个阻塞调用,它等待服务器调用responder.Connect()。同时通过双方我看到客户端等待receiver.Accept(),直到服务器调用responder.Connect()。我认为这意味着客户端套接字已接受连接。任何人都可以发现客户端的套接字保持未连接并且抛出此异常的原因吗?

非常感谢所有帮助,非常感谢!

编辑:2015 年 7 月 29 日 5:07p

一些可能有用的附加细节。服务调用后responder.Connect()responder.Connected 为true,并且responder.RemoteEndpoint 是客户端正在侦听的端点(地址和端口号已确认)。

但是,对于客户却不能这样说。在receiver.Accept() 调用通过receiver.Connected 为false 后receiver.RemoteEndpoint 抛出了与上面相同的异常。

进一步挖掘接收者的成员后,我发现 receiver.DontFragment 抛出了 System.NotSupportedException 和消息

不支持此协议版本。

我使用的协议有问题吗?我正在全面使用 TCP。也许它与地址族有关?

【问题讨论】:

  • 您的客户端和服务器代码标记错误。另请注意,accept() 返回一个非常重要的值。
  • 嗨,马丁。你能详细说明错误标签吗?属于服务器/客户端的代码位于它们各自的粗体部分下方。也许你误解了我对我所包含的代码段的解释
  • 标记为“客户端”的代码是 TCP 服务器。标记为“服务器端”的代码是 TCP 客户端。
  • .. 换句话说,接受()调用的对等点是服务器。调用 connect() 的对等方是客户端。
  • 无论您的客户端服务器协议发生了什么,我都不知道在哪里可以忽略 accept() 调用的结果。没有这个结果,客户端服务器通信是不可能的。

标签: c# sockets windows-services socketexception


【解决方案1】:

TCP 服务器监听套接字用于接受连接,它不用于与 TCP 客户端对等方的后续数据交换。 accept() 为每个连接的对等客户端返回另一个唯一的客户端服务器套接字。

一种非常常见的模式是一台服务器向多个客户端提供请求/响应服务。 accept() 返回的客户端服务器套接字可以与专用于该客户端的线程或进程通信,该线程或进程读取、解析、处理其客户端请求并在同一客户端服务器套接字上发送回复。或者,select/epoll 异步设计可以在一个线程/进程中处理请求/响应。

每个客户端一个线程/进程需要一个堆栈,(不好),但允许简单的“内联”代码,(好)。

异步 ​​select/epoll 只需要一个堆栈,(好),但通常需要用户空间状态机来处理事件,(坏)。

选择你的毒药:)

在请求进入后关闭客户端服务器套接字然后尝试打开另一个方向的连接以提供回复的情况要少得多。这需要一个服务器和一个客户端,并且会变得非常混乱。

【讨论】:

  • 谢谢马丁!这正是我需要知道的。我会修改我的设计。经过更多研究后,我将前往 TcpListener/TcpClient 类来隐藏这些让我感到困惑的套接字细节。
【解决方案2】:

所以@Martin James 极大地帮助了我解决我的设计缺陷。在通过现有套接字连接发出请求后,没有理由建立新的套接字连接。抛出的异常与设计无关,无论它可能有多糟糕。此行在我的客户端代码中接受连接

// accept connection from service
receiver.Accept();

需要

// accept connection from service
Socket handler = receiver.Accept();

然后使用handler 接收消息。返回的socket对象连接正确。

再次感谢@Martin James

【讨论】:

    猜你喜欢
    • 2014-12-20
    • 2014-08-30
    • 2013-07-31
    • 2013-02-03
    • 2018-07-01
    • 2013-11-24
    • 2021-08-16
    • 2018-06-20
    • 2021-10-11
    相关资源
    最近更新 更多