TcpListener 和 TcpClient 的异步函数问题,函数不等待 await 关键字
Problems with asynchronous functions with TcpListener and TcpClient, function not waiting on await keyword
我是异步套接字编程的新手,我的异步函数有问题。
我正在尝试创建一个聊天程序,该程序使用 Windows 表单作为客户端,并使用控制台应用程序作为服务器。
这是在我的服务器上处理连接的代码:
public async void StartServer()
{
TcpListener listener = new TcpListener(_ip, _port);
listener.Start();
Console.WriteLine("Server is running on IP: {0} Port: {1}", _ip.ToString(), _port);
while (true)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync();
HandleConnections(client);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
private async void HandleConnections(TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[256];
string message = null;
int x;
while(stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
message = message.Replace('[=10=]', ' ');
message = message.Trim();
Console.WriteLine("Message Recieved: " + message);
byte[] bytes = Encoding.ASCII.GetBytes(message);
await stream.WriteAsync(bytes, 0, bytes.Length);
stream.Close();
}
这里是客户端程序连接服务器的代码:
private async void ConnectButton_Click(object sender, EventArgs e)
{
IPAddress address = IPAddress.Parse(IPInput.Text);
client = new TcpClient();
await client.ConnectAsync(address, 12345);
NetworkStream stream = client.GetStream();
string message = UsernameInput.Text + " Connected!";
Task<int> sendTask = SendMessage(stream, message);
int sendComp = await sendTask;
Task<string> recieveTask = RecieveMessage(stream);
string recieved = await recieveTask;
stream.Close();
ChatText.AppendText(recieved);
}
private async Task<int> SendMessage(NetworkStream stream, string message)
{
byte[] bytes = Encoding.ASCII.GetBytes(message + "\r\n");
await stream.WriteAsync(bytes, 0, bytes.Length);
return 1;
}
private async Task<string> RecieveMessage(NetworkStream stream)
{
byte[] buffer = new byte[256];
string message = null;
int x;
while (stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
return message;
}
我遇到的第一个问题是当我 运行 客户端程序并单击 ConnectButton 时,消息被发送到输出 Message Recieved: user Connected!
的服务器程序,但随后客户端程序遇到ChatText.AppendText(recieved);
行的空引用异常表明 recieved
变量为空。 string recieved = await recieveTask;
行好像没有等待任务执行完毕,直接跳转到下一行,没有给recieved
赋值。如果我在 private async Task<string> RecieveMessage(NetworkStream stream)
函数的顶部放置一个断点并单步执行它,那么 recieved
变量将获取它的值并且代码将成功完成,但如果没有断点,我将得到空引用异常。
我遇到的下一个问题是,如果我离开服务器 运行ning 并再次打开客户端并尝试连接,服务器会在行 message = message.Replace('[=19=]', ' ');
上收到空引用异常。第一次用客户端运行,服务端成功接收到消息,但是第二次,没有从流中获取任何数据,将变量留空,导致空引用异常。
如果我的代码是垃圾,我深表歉意,我已经阅读了数小时的 MSDN 文档,但无法提出解决方案,我觉得我这样做是完全错误的。所以我的问题如下:
是什么导致了我遇到的这些错误?我是否以正确的方式解决这个问题?
你的两个问题都与异步函数无关,实际上这两个问题都是因为同一个问题:
while (stream.DataAvailable)
{
// read stream here
}
如果数据还 无法从流中读取 - 您的 ReceiveMessage 和 HandleConnections 函数都完全跳过读取流。你应该做的(在你的情况下)是:
do
{
// read your stream here
} while (stream.DataAvailable);
然后第一个 Read(或 ReadAsync)将等待直到第一个数据块到达,并且只有在第一个数据块到达之后才会检查是否有更多数据可用。
另请注意,您使用大缓冲区(256 字节),而 client\server 发送短消息(如 "Client received: xxx"),这意味着大部分缓冲区是空的,当您将其转换为字符串时Encoding.ASCII.GetString - 末尾有很多空格 ("Client received: xxx ... ")。
它看起来不像是 async
/await
的问题,而是您的 TCP 流的问题。
您似乎并没有真正在等待回复。 SendMessage
写入服务器,然后 RecieveMessage
期望响应已经在流中。
如果第一次点击 while
循环时 stream.DataAvailable
为假,message
将保持 null
。
您需要某种方式来等待流中有数据,然后再尝试从中读取数据。
我是异步套接字编程的新手,我的异步函数有问题。
我正在尝试创建一个聊天程序,该程序使用 Windows 表单作为客户端,并使用控制台应用程序作为服务器。
这是在我的服务器上处理连接的代码:
public async void StartServer()
{
TcpListener listener = new TcpListener(_ip, _port);
listener.Start();
Console.WriteLine("Server is running on IP: {0} Port: {1}", _ip.ToString(), _port);
while (true)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync();
HandleConnections(client);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
private async void HandleConnections(TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[256];
string message = null;
int x;
while(stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
message = message.Replace('[=10=]', ' ');
message = message.Trim();
Console.WriteLine("Message Recieved: " + message);
byte[] bytes = Encoding.ASCII.GetBytes(message);
await stream.WriteAsync(bytes, 0, bytes.Length);
stream.Close();
}
这里是客户端程序连接服务器的代码:
private async void ConnectButton_Click(object sender, EventArgs e)
{
IPAddress address = IPAddress.Parse(IPInput.Text);
client = new TcpClient();
await client.ConnectAsync(address, 12345);
NetworkStream stream = client.GetStream();
string message = UsernameInput.Text + " Connected!";
Task<int> sendTask = SendMessage(stream, message);
int sendComp = await sendTask;
Task<string> recieveTask = RecieveMessage(stream);
string recieved = await recieveTask;
stream.Close();
ChatText.AppendText(recieved);
}
private async Task<int> SendMessage(NetworkStream stream, string message)
{
byte[] bytes = Encoding.ASCII.GetBytes(message + "\r\n");
await stream.WriteAsync(bytes, 0, bytes.Length);
return 1;
}
private async Task<string> RecieveMessage(NetworkStream stream)
{
byte[] buffer = new byte[256];
string message = null;
int x;
while (stream.DataAvailable)
{
x = await stream.ReadAsync(buffer, 0, buffer.Length);
message += Encoding.ASCII.GetString(buffer);
}
return message;
}
我遇到的第一个问题是当我 运行 客户端程序并单击 ConnectButton 时,消息被发送到输出 Message Recieved: user Connected!
的服务器程序,但随后客户端程序遇到ChatText.AppendText(recieved);
行的空引用异常表明 recieved
变量为空。 string recieved = await recieveTask;
行好像没有等待任务执行完毕,直接跳转到下一行,没有给recieved
赋值。如果我在 private async Task<string> RecieveMessage(NetworkStream stream)
函数的顶部放置一个断点并单步执行它,那么 recieved
变量将获取它的值并且代码将成功完成,但如果没有断点,我将得到空引用异常。
我遇到的下一个问题是,如果我离开服务器 运行ning 并再次打开客户端并尝试连接,服务器会在行 message = message.Replace('[=19=]', ' ');
上收到空引用异常。第一次用客户端运行,服务端成功接收到消息,但是第二次,没有从流中获取任何数据,将变量留空,导致空引用异常。
如果我的代码是垃圾,我深表歉意,我已经阅读了数小时的 MSDN 文档,但无法提出解决方案,我觉得我这样做是完全错误的。所以我的问题如下:
是什么导致了我遇到的这些错误?我是否以正确的方式解决这个问题?
你的两个问题都与异步函数无关,实际上这两个问题都是因为同一个问题:
while (stream.DataAvailable)
{
// read stream here
}
如果数据还 无法从流中读取 - 您的 ReceiveMessage 和 HandleConnections 函数都完全跳过读取流。你应该做的(在你的情况下)是:
do
{
// read your stream here
} while (stream.DataAvailable);
然后第一个 Read(或 ReadAsync)将等待直到第一个数据块到达,并且只有在第一个数据块到达之后才会检查是否有更多数据可用。
另请注意,您使用大缓冲区(256 字节),而 client\server 发送短消息(如 "Client received: xxx"),这意味着大部分缓冲区是空的,当您将其转换为字符串时Encoding.ASCII.GetString - 末尾有很多空格 ("Client received: xxx ... ")。
它看起来不像是 async
/await
的问题,而是您的 TCP 流的问题。
您似乎并没有真正在等待回复。 SendMessage
写入服务器,然后 RecieveMessage
期望响应已经在流中。
如果第一次点击 while
循环时 stream.DataAvailable
为假,message
将保持 null
。
您需要某种方式来等待流中有数据,然后再尝试从中读取数据。