什么是使用 .NET 3.5 异步 System.Net.Sockets.Socket 停止数据流?
Posted
技术标签:
【中文标题】什么是使用 .NET 3.5 异步 System.Net.Sockets.Socket 停止数据流?【英文标题】:What is stopping data flow with .NET 3.5 asynchronous System.Net.Sockets.Socket? 【发布时间】:2010-05-26 00:43:30 【问题描述】:我有一个使用异步方法的 .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:
Socket#7588182::SendAsync() 套接字#7588182::SendAsync(True#1) 来自 Socket#7588182::FinishOperation(SendAsync) 的数据 00000000 : 41 42 43 0D 0A 套接字#7588182::ReceiveAsync() 退出 Socket#7588182::ReceiveAsync() -> True#1
它停在那里。它看起来就像来自客户端的第一次发送,但服务器没有显示任何活动。
我认为这可能是信息过载,但我很乐意根据需要提供更多详细信息。
谢谢!
【问题讨论】:
【参考方案1】:首先,我确定您知道,但值得重复一遍; TCP 连接是一个字节流。每次读取可以返回 1 个字节和您使用的缓冲区大小,具体取决于已到达的数据量。仅仅因为您使用 3 次发送发送数据并不意味着您需要 3 个 recv 来读取它,您可以一次获得所有数据,或者每个 recv 可以返回一个字节。处理消息框架取决于您,因为您是唯一知道它的人。 TCP 是一个字节流。
还有你为什么要使用这些事件?如果您不想使用异步数据流,请不要使用异步函数,使用同步套接字函数编写一些非常简单的东西,并消除使用异步 API 并使用同步原语来阻碍它的所有无意义的复杂性。
【讨论】:
以上是关于什么是使用 .NET 3.5 异步 System.Net.Sockets.Socket 停止数据流?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 .NET 3.5 中使用 System.IdentityModel.Tokens.Jwt 5.2.1?
在 .Net 3.5 中使用 Ninject 时 System.Core 的 System.Io.FileNotFoundException
.NET 3.5 - System.DirectoryServices.AccountManagement - 组上的 AdvancedSearchFilter?
如何在 .NET 2.0/3.5 中将(字符串)消息从一个线程异步排队到另一个线程?
Windows 服务在从 3.5 迁移的 Windows Server 2008、x64、.NET 4 上引发 System.BadImageFormatException