我有一个使用异步方法的 .NET 3.5 客户端/服务器套接字接口。客户端连接到服务器,并且连接应保持打开状态,直到应用程序终止。该协议由以下模式组成:
- 发送 stx
- 接收确认
- 发送数据1
- 接收确认
- 发送数据2(重复5-6,同时有更多数据)
- 接收确认
- 发送 etx
因此,具有上述两个数据块的单个事务将包含来自客户端的 4 次发送。发送 etx 后,客户端只需等待更多数据发送出去,然后使用 stx 开始下一次传输。我不想破坏各个交易所之间或每个 stx/data/etx 有效负载之后的连接。
现在,连接后,客户端可以发送第一个 stx,并获得一个 ack,但之后我无法将更多数据放到网络上。双方都没有断开连接,套接字仍然完好无损。客户端代码严重缩写如下 - 我遵循在线代码示例中常见的模式。
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 时,.NET 跟踪显示以下内容\r\n:
套接字#7588182::SendAsync() 套接字#7588182::SendAsync(True#1) 来自 Socket#7588182::FinishOperation(SendAsync) 的数据 00000000 : 41 42 43 0D 0A 套接字#7588182::ReceiveAsync() 退出 Socket#7588182::ReceiveAsync() -> True#1
它停在那里。它看起来就像来自客户端的第一次发送,但服务器没有显示任何活动。
我认为现在可能是信息过载,但我很乐意根据需要提供更多详细信息。
谢谢!