.NET 3.5 异步 System.Net.Sockets.Socket 停止数据流的原因是什么?
我有一个使用异步方法的 .NET 3.5 客户端/服务器套接字接口。客户端连接到服务器,并且连接应保持打开状态,直到应用程序终止。该协议由以下模式组成:
- 发送 stx
- 接收 ack
- 发送数据 1
- 接收 ack
- 发送数据 2(更多数据时重复 5-6 次)
- 接收 ack
- 发送 etx
因此,如上所述具有两个数据块的单个事务将包含来自客户端的 4 个发送。发送 etx 后,客户端只需等待更多数据发送出去,然后开始使用 stx 进行下一次传输。我不想断开各个交换之间或每个 stx/data/etx 有效负载之后的连接。
现在,连接后,客户端可以发送第一个 stx,并获得一个确认,但之后我无法将更多数据传输到网络上。双方都没有断开连接,插座仍然完好无损。客户端代码严格缩写如下 - 我遵循在线代码示例中常见的模式。
private void SendReceive(string data) {
// ...
SocketAsyncEventArgs completeArgs;
completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend);
clientSocket.SendAsync(completeArgs);
// two AutoResetEvents, one for send, one for receive
if ( !AutoResetEvent.WaitAll(autoSendReceiveEvents , -1) )
Log("failed");
else
Log("success");
// ...
}
private void OnSend( object sender , SocketAsyncEventArgs e ) {
// ...
Socket s = e.UserToken as Socket;
byte[] receiveBuffer = new byte[ 4096 ];
e.SetBuffer(receiveBuffer , 0 , receiveBuffer.Length);
e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
s.ReceiveAsync(e);
// ...
}
private void OnReceive( object sender , SocketAsyncEventArgs e ) {}
// ...
if ( e.BytesTransferred > 0 ) {
Int32 bytesTransferred = e.BytesTransferred;
String received = Encoding.ASCII.GetString(e.Buffer , e.Offset , bytesTransferred);
dataReceived += received;
}
autoSendReceiveEvents[ SendOperation ].Set(); // could be moved elsewhere
autoSendReceiveEvents[ ReceiveOperation ].Set(); // releases mutexes
}
服务器上的代码非常相似,除了它首先接收然后发送响应 - 服务器在发送响应后没有做任何事情(我可以告诉)来修改连接。问题是,当我第二次在客户端中点击 SendReceive 时,连接已经处于奇怪的状态。
我是否需要在客户端中执行某些操作来保留 SocketAsyncEventArgs,并在套接字/连接的生命周期内重复使用同一对象?我不确定在连接或给定交换的生命周期中哪个 eventargs 对象应该挂起。
我是否需要在服务器中执行某些操作以确保其继续接收数据?
服务器设置和响应处理如下所示:
void Start() {
// ...
listenSocket.Bind(...);
listenSocket.Listen(0);
StartAccept(null); // note accept as soon as we start. OK?
mutex.WaitOne();
}
void StartAccept(SocketAsyncEventArgs acceptEventArg) {
if ( acceptEventArg == null )
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
}
Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg);
if ( !willRaiseEvent )
ProcessAccept(acceptEventArg);
// ...
}
private void OnAcceptCompleted( object sender , SocketAsyncEventArgs e ) {
ProcessAccept(e);
}
private void ProcessAccept( SocketAsyncEventArgs e ) {
// ...
SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();
readEventArgs.SetBuffer(dataBuffer , 0 , Int16.MaxValue);
readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
readEventArgs.UserToken = e.AcceptSocket;
dataReceived = ""; // note server is degraded for single client/thread use
// As soon as the client is connected, post a receive to the connection.
Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
if ( !willRaiseEvent )
this.ProcessReceive(readEventArgs);
// Accept the next connection request.
this.StartAccept(e);
}
private void OnIOCompleted( object sender , SocketAsyncEventArgs e ) {
// switch ( e.LastOperation )
case SocketAsyncOperation.Receive:
ProcessReceive(e); // similar to client code
// operate on dataReceived here
case SocketAsyncOperation.Send:
ProcessSend(e); // similar to client code
}
// execute this when a data has been processed into a response (ack, etc)
private SendResponseToClient(string response) {
// create buffer with response
// currentEventArgs has class scope and is re-used
currentEventArgs.SetBuffer(sendBuffer , 0 , sendBuffer.Length);
Boolean willRaiseEvent = currentClient.SendAsync(currentEventArgs);
if ( !willRaiseEvent )
ProcessSend(currentEventArgs);
}
发送 ABC\r\n 时,.NET 跟踪显示以下内容:
Socket#7588182::SendAsync() Socket#7588182::SendAsync(True#1) Data from Socket#7588182::FinishOperation(SendAsync) 00000000 : 41 42 43 0D 0A Socket#7588182::ReceiveAsync() Exiting Socket#7588182::ReceiveAsync() -> True#1
然后它停止在那里。它看起来就像客户端的第一次发送,但服务器没有显示任何活动。
我认为目前信息可能过多,但我很乐意根据需要提供更多详细信息。
谢谢!
I have a .NET 3.5 client/server socket interface using the asynchronous methods. The client connects to the server and the connection should remain open until the app terminates. The protocol consists of the following pattern:
- send stx
- receive ack
- send data1
- receive ack
- send data2 (repeat 5-6 while more data)
- receive ack
- send etx
So a single transaction with two datablocks as above would consist of 4 sends from the client. After sending etx the client simply waits for more data to send out, then begins the next transmission with stx. I do not want to break the connection between individual exchanges or after each stx/data/etx payload.
Right now, after connection, the client can send the first stx, and get a single ack, but I can't put more data onto the wire after that. Neither side disconnects, the socket is still intact. The client code is seriously abbreviated as follows - I'm following the pattern commonly available in online code samples.
private void SendReceive(string data) {
// ...
SocketAsyncEventArgs completeArgs;
completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend);
clientSocket.SendAsync(completeArgs);
// two AutoResetEvents, one for send, one for receive
if ( !AutoResetEvent.WaitAll(autoSendReceiveEvents , -1) )
Log("failed");
else
Log("success");
// ...
}
private void OnSend( object sender , SocketAsyncEventArgs e ) {
// ...
Socket s = e.UserToken as Socket;
byte[] receiveBuffer = new byte[ 4096 ];
e.SetBuffer(receiveBuffer , 0 , receiveBuffer.Length);
e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
s.ReceiveAsync(e);
// ...
}
private void OnReceive( object sender , SocketAsyncEventArgs e ) {}
// ...
if ( e.BytesTransferred > 0 ) {
Int32 bytesTransferred = e.BytesTransferred;
String received = Encoding.ASCII.GetString(e.Buffer , e.Offset , bytesTransferred);
dataReceived += received;
}
autoSendReceiveEvents[ SendOperation ].Set(); // could be moved elsewhere
autoSendReceiveEvents[ ReceiveOperation ].Set(); // releases mutexes
}
The code on the server is very similar except that it receives first and then sends a response - the server is not doing anything (that I can tell) to modify the connection after it sends a response. The problem is that the second time I hit SendReceive in the client, the connection is already in a weird state.
Do I need to do something in the client to preserve the SocketAsyncEventArgs, and re-use the same object for the lifetime of the socket/connection? I'm not sure which eventargs object should hang around during the life of the connection or a given exchange.
Do I need to do something, or Not do something in the server to ensure it continues to Receive data?
The server setup and response processing looks like this:
void Start() {
// ...
listenSocket.Bind(...);
listenSocket.Listen(0);
StartAccept(null); // note accept as soon as we start. OK?
mutex.WaitOne();
}
void StartAccept(SocketAsyncEventArgs acceptEventArg) {
if ( acceptEventArg == null )
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted);
}
Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg);
if ( !willRaiseEvent )
ProcessAccept(acceptEventArg);
// ...
}
private void OnAcceptCompleted( object sender , SocketAsyncEventArgs e ) {
ProcessAccept(e);
}
private void ProcessAccept( SocketAsyncEventArgs e ) {
// ...
SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();
readEventArgs.SetBuffer(dataBuffer , 0 , Int16.MaxValue);
readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted);
readEventArgs.UserToken = e.AcceptSocket;
dataReceived = ""; // note server is degraded for single client/thread use
// As soon as the client is connected, post a receive to the connection.
Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
if ( !willRaiseEvent )
this.ProcessReceive(readEventArgs);
// Accept the next connection request.
this.StartAccept(e);
}
private void OnIOCompleted( object sender , SocketAsyncEventArgs e ) {
// switch ( e.LastOperation )
case SocketAsyncOperation.Receive:
ProcessReceive(e); // similar to client code
// operate on dataReceived here
case SocketAsyncOperation.Send:
ProcessSend(e); // similar to client code
}
// execute this when a data has been processed into a response (ack, etc)
private SendResponseToClient(string response) {
// create buffer with response
// currentEventArgs has class scope and is re-used
currentEventArgs.SetBuffer(sendBuffer , 0 , sendBuffer.Length);
Boolean willRaiseEvent = currentClient.SendAsync(currentEventArgs);
if ( !willRaiseEvent )
ProcessSend(currentEventArgs);
}
A .NET trace shows the following when sending ABC\r\n:
Socket#7588182::SendAsync() Socket#7588182::SendAsync(True#1) Data from Socket#7588182::FinishOperation(SendAsync) 00000000 : 41 42 43 0D 0A Socket#7588182::ReceiveAsync() Exiting Socket#7588182::ReceiveAsync() -> True#1
And it stops there. It looks just like the first send from the client but the server shows no activity.
I think that could be info overload for now but I'll be happy to provide more details as required.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,我相信你知道,但值得重复一遍; TCP 连接是字节流。每次读取可以返回 1 个字节到您使用的缓冲区大小之间的数据,具体取决于已到达的数据量。仅仅因为您用 3 次发送发送数据并不意味着您需要 3 个接收来读取它,您可以将其全部获取,或者每个接收可以返回一个字节。由您来处理消息框架,因为您是唯一知道这一情况的人。 TCP 是字节流。
另外你为什么要使用这些事件?如果你不想使用异步数据流,那么就不要使用异步函数,用同步套接字函数编写一些非常简单的东西,并消除使用异步 API 然后使用同步原语来阻碍它的所有无意义的复杂性。
Firstly, I'm sure you know, but it's worth repeating; a TCP connection is a stream of bytes. Each read can return between 1 byte and the size of the buffer that you used depending on the amount of data that has arrived. Just because you send the data with 3 sends doesn't mean that you'll need 3 recvs to read it, you could get it all in one, or each recv could return a single byte. It's up to YOU to deal with message framing as you're the only one that knows about it. TCP is a stream of bytes.
Also WHY are you using those events? If you don't want to use asynchronous data flow then don't use the async functions, write something very simple with the sync socket functions and remove all the pointless complexity of using an async API and then using synchronisation primitives to hobble it.