C# Socket.Receive() SocketException: 发送或接收数据的请求被禁止,因为套接字未连接

C# Socket.Receive() SocketException: A request to send or receive data was disallowed because the socket is not connected

我正在为 Windows 在 C#、.Net 4.5 和 Windows 8.1 上的套接字编程而苦苦挣扎。

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

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

服务的侦听套接字工作正常。我能够从连接到侦听套接字的用户应用程序接收请求。当角色互换时会出现问题:服务需要连接到客户端拥有的套接字,并通过客户端拥有的套接字发送 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() 调用时抛出异常。以下是异常详情:

Type: SocketException

Message: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.

ErrorCode: 10057 (Socket is not connected)

因此,我挂断的地方是客户端的套接字未连接的方式。 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 消息

This protocol version is not supported.

我使用的协议有问题吗?我正在全面使用 TCP。可能跟地址族有关?

TCP 服务器侦听套接字用于接受连接,它不用于与 TCP 客户端对等点的后续数据交换。 accept() returns 另一个唯一的客户端<>服务器套接字,用于连接的每个对等客户端。

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

中处理 request/response

每个客户端一个 thread/process 需要每个客户端一个堆栈,(坏),但允许简单的 'in-line' 代码,(好)。

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

选择你的毒药:)

在收到请求后关闭客户端<>服务器套接字然后尝试打开另一个方向的连接以提供答复的情况要少得多。这需要两个对等点的服务器和客户端并且变​​得非常混乱。

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

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

需要

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

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

再次感谢@Martin James