SocketAsyncEventArgs 实例上的 TCP 服务器重置 AcceptSocket.ReceiveAsync 操作
TCP Server Resetting AcceptSocket.ReceiveAsync operation on a SocketAsyncEventArgs instance
我目前有一个使用 C# SAEA 实现的 TCP 服务器。我想做的是在连接到服务器的 2 个 TCP 客户端之间转发一条消息(客户端 1 和 客户端 2)。
- 服务器使用 receiveSendEventArgs.AcceptSocket.ReceiveAsync 和
receiveSendEventArgs.AcceptSocket.SendAsync 命令发送和
从每个连接的客户端接收信息,无需
问题。
- 服务器处于 receiveSendEventArgs.AcceptSocket.ReceiveAsync 操作中 Client 1 和 Client 2.
- 客户端 1 发送一条消息,服务器接受该消息。服务器发现 Client 2 也已连接,因此需要获取 receiveSendEventArgs 对 Client 2[=53= 的引用]转发消息。
然而,Server 引用 Client 2 的 receiveSendEventArgs 并开始准备缓冲区 (SetBuffer) 以发送消息我相信,由于 Client 2 的 Socket 仍处于 "ReceiveSync" 状态,它会爆炸并显示以下消息:
"异步套接字操作已在使用此 SocketAsyncEventArgs 实例进行。"
有没有办法将服务器上的 Client 2 状态从 "ReceiveAsync" 切换到 "SendAsync" 以便在我尝试时不会出错将数据发送到 客户端 2?我知道 Completed 事件在发送或接收操作完成时触发,但是,直接调用我的 IO_Completed 方法不会改变操作。
在 for 循环中为 SocketAsyncEventArgs 的已完成事件设置事件处理程序:
eventArgObjectForPool.Completed += new EventHandler(IO_Completed);
无效IO_Completed(对象发送者,SocketAsyncEventArgs e){
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)e.UserToken;
//More business logic here ...
// determine which type of operation just completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("IO_Completed method in Receive, receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("IO_Completed method in Send, id " + receiveSendToken.TokenId);
}
ProcessSend(e);
break;
default:
//This exception will occur if you code the Completed event of some
//operation to come to this method, by mistake.
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
private void StartReceive(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken;
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartReceive(), receiveSendToken id " + receiveSendToken.TokenId);
}
switch (receiveSendToken.clientInfo.currentState)
{
case MyClient.ClientState.Connecting://This occurs when we get client to connect for first time. However, it will automatically disconnect
receiveSendToken.theMediator.HandleData(receiveSendToken.theDataHolder);
// Create a new DataHolder for next message.
receiveSendToken.CreateNewDataHolder();
//Reset the variables in the UserToken, to be ready for the
//next message that will be received on the socket in this
//SAEA object.
receiveSendToken.Reset(true);
receiveSendToken.theMediator.PrepareOutgoingData();
StartSend(receiveSendToken.theMediator.GiveBack());
//******************************************************************
break;
default:
//Set the buffer for the receive operation.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetReceive, this.socketListenerSettings.BufferSize);
// Post async receive operation on the socket.
bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.ReceiveAsync(receiveSendEventArgs);
//Socket.ReceiveAsync returns true if the I/O operation is pending. The
//SocketAsyncEventArgs.Completed event on the e parameter will be raised
//upon completion of the operation. So, true will cause the IO_Completed
//method to be called when the receive operation completes.
//That's because of the event handler we created when building
//the pool of SocketAsyncEventArgs objects that perform receive/send.
//It was the line that said
//eventArgObjectForPool.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
//Socket.ReceiveAsync returns false if I/O operation completed synchronously.
//In that case, the SocketAsyncEventArgs.Completed event on the e parameter
if (!willRaiseEvent)
{
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartReceive in if (!willRaiseEvent), receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessReceive(receiveSendEventArgs);
}
break;
}
}
private void StartSend(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken;
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartSend, id " + receiveSendToken.TokenId);
}
if (Program.watchThreads == true) //for testing
{
DealWithThreadsForTesting("StartSend()", receiveSendToken);
}
if (receiveSendToken.sendBytesRemainingCount <= this.socketListenerSettings.BufferSize)
{
Program.testWriter.WriteLine("blocking:?(" + receiveSendEventArgs.AcceptSocket.Blocking + ")");
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend, receiveSendToken.sendBytesRemainingCount);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend, receiveSendToken.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend, receiveSendToken.sendBytesRemainingCount);
}
else
{
//We cannot try to set the buffer any larger than its size.
//So since receiveSendToken.sendBytesRemainingCount > BufferSize, we just
//set it to the maximum size, to send the most data possible.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend, this.socketListenerSettings.BufferSize);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend, receiveSendToken.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend, this.socketListenerSettings.BufferSize);
//We'll change the value of sendUserToken.sendBytesRemainingCount
//in the ProcessSend method.
}
//post asynchronous send operation
bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.SendAsync(receiveSendEventArgs);
if (!willRaiseEvent)
{
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartSend in if (!willRaiseEvent), receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessSend(receiveSendEventArgs);
}
}
这不是我想要做的,但我可以调用以下内容:
receiveSendEventArgs.AcceptSocket.Write(strMyBuffer);
这让我可以写入同一个套接字,而且我没有任何错误。我想坚持使用异步命令,但屈服于同步写入,它没有影响我的服务器。也许还有其他解决方案。
我目前有一个使用 C# SAEA 实现的 TCP 服务器。我想做的是在连接到服务器的 2 个 TCP 客户端之间转发一条消息(客户端 1 和 客户端 2)。
- 服务器使用 receiveSendEventArgs.AcceptSocket.ReceiveAsync 和 receiveSendEventArgs.AcceptSocket.SendAsync 命令发送和 从每个连接的客户端接收信息,无需 问题。
- 服务器处于 receiveSendEventArgs.AcceptSocket.ReceiveAsync 操作中 Client 1 和 Client 2.
- 客户端 1 发送一条消息,服务器接受该消息。服务器发现 Client 2 也已连接,因此需要获取 receiveSendEventArgs 对 Client 2[=53= 的引用]转发消息。
然而,Server 引用 Client 2 的 receiveSendEventArgs 并开始准备缓冲区 (SetBuffer) 以发送消息我相信,由于 Client 2 的 Socket 仍处于 "ReceiveSync" 状态,它会爆炸并显示以下消息:
"异步套接字操作已在使用此 SocketAsyncEventArgs 实例进行。"
有没有办法将服务器上的 Client 2 状态从 "ReceiveAsync" 切换到 "SendAsync" 以便在我尝试时不会出错将数据发送到 客户端 2?我知道 Completed 事件在发送或接收操作完成时触发,但是,直接调用我的 IO_Completed 方法不会改变操作。
在 for 循环中为 SocketAsyncEventArgs 的已完成事件设置事件处理程序: eventArgObjectForPool.Completed += new EventHandler(IO_Completed);
无效IO_Completed(对象发送者,SocketAsyncEventArgs e){
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)e.UserToken;
//More business logic here ...
// determine which type of operation just completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("IO_Completed method in Receive, receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("IO_Completed method in Send, id " + receiveSendToken.TokenId);
}
ProcessSend(e);
break;
default:
//This exception will occur if you code the Completed event of some
//operation to come to this method, by mistake.
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
private void StartReceive(SocketAsyncEventArgs receiveSendEventArgs) {
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken;
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartReceive(), receiveSendToken id " + receiveSendToken.TokenId);
}
switch (receiveSendToken.clientInfo.currentState)
{
case MyClient.ClientState.Connecting://This occurs when we get client to connect for first time. However, it will automatically disconnect
receiveSendToken.theMediator.HandleData(receiveSendToken.theDataHolder);
// Create a new DataHolder for next message.
receiveSendToken.CreateNewDataHolder();
//Reset the variables in the UserToken, to be ready for the
//next message that will be received on the socket in this
//SAEA object.
receiveSendToken.Reset(true);
receiveSendToken.theMediator.PrepareOutgoingData();
StartSend(receiveSendToken.theMediator.GiveBack());
//******************************************************************
break;
default:
//Set the buffer for the receive operation.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetReceive, this.socketListenerSettings.BufferSize);
// Post async receive operation on the socket.
bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.ReceiveAsync(receiveSendEventArgs);
//Socket.ReceiveAsync returns true if the I/O operation is pending. The
//SocketAsyncEventArgs.Completed event on the e parameter will be raised
//upon completion of the operation. So, true will cause the IO_Completed
//method to be called when the receive operation completes.
//That's because of the event handler we created when building
//the pool of SocketAsyncEventArgs objects that perform receive/send.
//It was the line that said
//eventArgObjectForPool.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
//Socket.ReceiveAsync returns false if I/O operation completed synchronously.
//In that case, the SocketAsyncEventArgs.Completed event on the e parameter
if (!willRaiseEvent)
{
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartReceive in if (!willRaiseEvent), receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessReceive(receiveSendEventArgs);
}
break;
}
}
private void StartSend(SocketAsyncEventArgs receiveSendEventArgs) { DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken;
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartSend, id " + receiveSendToken.TokenId);
}
if (Program.watchThreads == true) //for testing
{
DealWithThreadsForTesting("StartSend()", receiveSendToken);
}
if (receiveSendToken.sendBytesRemainingCount <= this.socketListenerSettings.BufferSize)
{
Program.testWriter.WriteLine("blocking:?(" + receiveSendEventArgs.AcceptSocket.Blocking + ")");
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend, receiveSendToken.sendBytesRemainingCount);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend, receiveSendToken.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend, receiveSendToken.sendBytesRemainingCount);
}
else
{
//We cannot try to set the buffer any larger than its size.
//So since receiveSendToken.sendBytesRemainingCount > BufferSize, we just
//set it to the maximum size, to send the most data possible.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend, this.socketListenerSettings.BufferSize);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend, receiveSendToken.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend, this.socketListenerSettings.BufferSize);
//We'll change the value of sendUserToken.sendBytesRemainingCount
//in the ProcessSend method.
}
//post asynchronous send operation
bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.SendAsync(receiveSendEventArgs);
if (!willRaiseEvent)
{
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartSend in if (!willRaiseEvent), receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessSend(receiveSendEventArgs);
}
}
这不是我想要做的,但我可以调用以下内容:
receiveSendEventArgs.AcceptSocket.Write(strMyBuffer);
这让我可以写入同一个套接字,而且我没有任何错误。我想坚持使用异步命令,但屈服于同步写入,它没有影响我的服务器。也许还有其他解决方案。