【发布时间】:2017-12-05 03:48:37
【问题描述】:
我写了一个简单的异步 TcpClient。这里是代码的相关部分:
public class AsyncTcpClient : IDisposable
{
private TcpClient tcpClient;
private Stream stream;
private int bufferSize = 8192;
bool IsReceiving = false;
public event EventHandler<string> OnDataReceived;
public event EventHandler OnDisconnected;
public event EventHandler OnConnected;
public event EventHandler<Exception> OnError;
public bool IsConnected
{
get
{
return tcpClient != null && tcpClient.Connected;
}
}
public AsyncTcpClient() { }
public async Task ConnectAsync(string host, int port, CancellationToken token = default(CancellationToken))
{
try
{
if (IsConnected) Close();
tcpClient = new TcpClient();
if (!tcpClient.ConnectAsync(host, port).Wait(250))
{
throw new TimeoutException();
}
stream = tcpClient.GetStream();
OnConnected?.Invoke(this, EventArgs.Empty);
await Receive();
}
catch (Exception)
{
OnDisconnected?.Invoke(this, EventArgs.Empty);
}
}
public async Task Receive(CancellationToken token = default(CancellationToken))
{
try
{
if (!IsConnected || IsReceiving) throw new InvalidOperationException();
IsReceiving = true;
byte[] buffer = new byte[bufferSize];
while (IsConnected)
{
token.ThrowIfCancellationRequested();
// First time it reads the incoming data, then it hangs here forever
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token);
if (bytesRead > 0)
{
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, bytesRead);
OnDataReceived?.Invoke(this, Encoding.ASCII.GetString(data));
}
buffer = new byte[bufferSize];
}
}
catch (ObjectDisposedException) { }
catch (IOException)
{
throw;
}
finally
{
IsReceiving = false;
}
}
}
在另一个应用程序上,我有一个等待连接的 TcpListener。 连接成功后,服务器向客户端发送一些数据。从 ReadAsync 正确接收数据。然后,如果我尝试从服务器发送更多数据,客户端会在第二次调用 ReadAsync 时永远等待。
我很确定服务器正在工作,因为我收到了发送正确字节的 SendCallback。
我是否错误地使用了 ReadAsync?
更新
我在这里添加我的服务器的完整代码:
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 4096;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
public class TcpServerAsync
{
public readonly ConcurrentQueue<String> queue = new ConcurrentQueue<String>();
public ManualResetEvent allDone = new ManualResetEvent(false);
private Boolean _isRunning = true;
public event EventHandler Connected;
public TcpServerAsync(Int32 port)
{
}
public void Start()
{
Thread t = new Thread(Run);
t.Start();
}
public void Run()
{
IPHostEntry ipHostInfo = Dns.GetHostEntry("localhost");
IPAddress ipAddress = ipHostInfo.AddressList[1];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 5000);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(1);
while (_isRunning)
{
allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
public void AcceptCallback(IAsyncResult ar)
{
allDone.Set();
Connected.Invoke(this, new EventArgs());
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
StateObject state = new StateObject
{
workSocket = handler
};
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
while (handler.Connected)
{
if (queue.TryDequeue(out String data))
{
try
{
SendData(handler, data);
}
catch (Exception ex)
{
throw;
}
}
Thread.Sleep(0);
}
}
public void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
content = state.sb.ToString();
Debug.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content);
}
}
public void SendData(Socket handler, String data)
{
byte[] byteData = Encoding.ASCII.GetBytes(data);
handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
}
private void SendCallback(IAsyncResult ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent = handler.EndSend(ar);
Debug.WriteLine("Sent {0} bytes to client.", bytesSent);
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
}
【问题讨论】:
-
我刚刚测试了代码,它按预期工作。你确定服务器没有问题?
-
“我写了一个简单的异步 TcpClient” -- 为什么?为什么不直接从
TcpClient获取NetworkStream并使用异步方法呢?为什么你要按照你的方式编写代码,使得ConnectAsync()方法在连接关闭之前不会返回?这是一个非常令人困惑的设计。更不用说您的Receive()方法最好遵循约定(即名称中包含...Async),并且不将 EAP 模型与 TPL 模型混合。但大多数情况下,如果您需要代码方面的帮助,您需要提供一个很好的 minimal reproducible example 来重现问题。 -
@PeterDuniho 我是这样写的,因为我还在学习,我基于微软的例子:msdn.microsoft.com/it-it/library/bew39x2a(v=vs.110).aspx。我不知道什么是 EAP 和 TPL 模型。我要去谷歌搜索他们。感谢您的提示。
-
@Rainman 用服务器代码更新了问题。
标签: c#