要通过TCP连接为我的应用程序发送数据,我使用一段简单的代码:
public void Send(byte[] message)
{
if (socket != null)
{
if (stream != null)
{
stream.Write(message, 0, message.Length);
if (receiveThread == null)
{
StartReceiver();
}
}
}
}
套接字是TcpClient
类的实例,流是关联的流实例。 StartReceiver()
启动一个Thread,正如方法所暗示的那样,它接收发送给应用程序的数据。
要接收数据,我使用:
private void ReceiveLoop()
{
DataReceivedStruct drs;
try
{
for (; ; )
{
if (stream != null)
{
drs = new DataReceivedStruct();
drs.stream = stream;
drs.waitHandle = are;
stream.BeginRead(readBuffer, 0, readBuffer.Length, DataReceived, drs);
Console.WriteLine("Waiting to be allowed to continue");
are.WaitOne();
Console.WriteLine("Allowed, continuing loop");
}
else
{
Thread.Sleep(5);
}
}
}
catch (SocketException e)
{
DispatchRaiseException(e);
}
catch (Exception e)
{
DispatchRaiseException(e);
}
}
同样,使用的流是TcpClient
类对象的上述流实例。 readBuffer对象是byte[1024]
。给BeginRead
的回调如下所示:
private void DataReceived(IAsyncResult result)
{
DataReceivedStruct drs = (DataReceivedStruct)result.AsyncState;
NetworkStream used = drs.stream;
AutoResetEvent handle = drs.waitHandle;
used.EndRead(result);
DispatchRaiseReceived(readBuffer);
Console.WriteLine("Signalling allowance of continue for loop");
handle.Set();
}
它结束对流的读取操作并传递readBuffer中的数据集。
原则上这是有效的。我可以从应用程序发送和接收数据。应用程序的接收端只有一个问题。当消息发送到应用程序时,将调用BeginRead
函数,之后回调将触发并以EndRead
结束读取操作,并传递数据以进行进一步处理。这适用于一次一条消息。但是在第一条消息触发BeginRead
后直接发送另一条消息时,它会变得更有趣。然后会发生的是第一条消息的EndRead
尚未发生,因此第一条消息的数据被第二条消息写入,导致数据不正确。
我应该停止使用BeginRead
/ EndRead
并使用阻止Read
操作来接收数据吗?或者是否可以使用BeginRead
/ EndRead
锁定流,以便在处理完第一条消息之前不会收到第二条消息?
答案 0 :(得分:1)
IMO,这里的问题是考虑循环,因此需要一个继续标志。这不会扩展,因为它需要每个连接一个线程。
应该做的是:
在回调中:
如果您乐意替换几个不同的缓冲区(我使用一个小型微池),您可以交换“进程”,以便您可以继续并行处理。但是你绝对需要每次读取不同的缓冲区,以防止覆盖数据。这通常只适用于异常高读取方案。
如果它有帮助,我正在编写一个用于编写简单的TCP客户端/服务器方案的库,而不必担心所有蹩脚的实现细节,我计划一旦稳定发布为开源;具有广泛的对象池/重用,完全异步使用(使用绑定到完成端口的3.5 API)等。