我正在编写一个模拟器来测试(取消)对象的序列化并通过TCP发送它们 . 我在C#中使用TcpClient,TcpListener和NetworkStreams进行通信 . 我想打开一个连接,发送多条消息,并在一段时间后关闭连接 . Build 连接并在两个方向上发送数据包都可以正常工作 . 但是,当我关闭客户端上的连接(调用 stream.Close()
和 tcpClient.Close()
)时,将一次又一次地调用服务器对BeginRead的回调 .
客户端代码:
private TcpClient client;
public void StartClient()
{
this.client = new TcpClient(this.serverAddress, this.port);
}
public void Send(byte[] data)
{
var stream = this.client.GetStream();
stream.Write(data, 0, data.Length);
}
public void StopClient()
{
this.client.GetStream().Close();
this.tcpClient.Close();
}
服务器代码:
private readonly AutoResetEvent autoResetEvent= new AutoResetEvent(false);
private TcpListener tcpListener;
public event Action<byte[]> MessageReceived;
public void StartListening()
{
this.tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1337);
this.tcpListener.Start();
this.tcpListener.BeginAcceptTcpClient(this.ClientConnecting, this.tcpListener);
}
private void ClientConnecting(IAsyncResult result)
{
var listener = (TcpListener)result.AsyncState;
this.tcpClient = listener.EndAcceptTcpClient(result);
this.Listen();
}
private void Listen()
{
var stream = this.tcpClient.GetStream();
var buffer = new byte[256];
while (stream.CanRead)
{
try
{
stream.BeginRead(buffer, 0, buffer.Length, this.DataReceiveCallback, new ReceiveState { Buffer = buffer, Stream = stream });
}
catch
{
this.StopListening();
break;
}
this.autoResetEvent.WaitOne();
}
}
public void StopListening()
{
var stream = this.tcpClient.GetStream();
stream.Close();
this.tcpClient.Close();
this.tcpClient = null;
}
private void DataReceiveCallback(IAsyncResult ar)
{
var state = (ReceiveState)ar.AsyncState;
var stream = state.Stream;
if (stream.CanRead)
{
var buffer = state.Buffer;
var numberOfBytesRead = stream.EndRead(ar);
if (this.MessageReceived != null)
{
if (!stream.DataAvailable)
{
this.MessageReceived(buffer.Take(numberOfBytesRead).ToArray());
}
else
{
var receivedBytes = new List<byte>(buffer);
while (stream.DataAvailable)
{
numberOfBytesRead = stream.Read(buffer, 0, buffer.Length);
receivedBytes.AddRange(buffer.Take(numberOfBytesRead));
}
this.MessageReceived(receivedBytes.ToArray());
}
}
}
this.autoResetEvent.Set();
}
当我在客户端上调用 StopClient()
时,正在调用服务器上的回调( DataReceiveCallback
) . stream.CanRead
返回true,但要读取0个字节 . stream.DataAvailable
也是假的 .
但是,除此之外,我认为在服务器端无法知道客户端是否关闭了连接 . 当连接仍然打开时,可以从客户端接收0个字节 .
现在作为af发生的是,在关闭客户端上的连接之后,一次又一次地调用 DataReceiveCallback
,始终使用0字节来读取 .
我想要做的是在客户端关闭连接时关闭服务器上的连接 . 我能做些什么来实现这个目标?
Note :我知道这个例子没有正确的错误处理,一旦你读到东西就不应该开始同步阅读,直到 stream.Read()
返回0 . 但是,由于我直接控制发送的所有数据,这适合我现在的需要 .
1 回答
您在循环中调用BeginRead . 如果流完成,则读取立即以0字节完成,循环继续 .
您使用异步IO没有任何意义,因为您正在阻塞等待IO完成的线程 . 毫无疑问,你已经从糟糕的MSDN样本中复制了它(不知道这是什么,真的) .
扔掉它并使用正常的循环同步读取 . 当读取调用返回0时,您将中断循环 .
另请注意,TCP现在无法以任何方式支持消息 . 删除
CanRead
和DataAvailable
的所有用法 . 只要你考虑这些属性,你很可能做错了 .