我尝试创建简单的聊天服务器,它处理多个客户端,并从所有客户端收集消息。 以下是我的代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ChatServer
{
public interface IServerPresenter
{
void Start(IServerView view);
void StartServer(string address, int port);
void CloseServer();
}
public class ServerPresenter : IServerPresenter
{
public IServerView ServerView { get; set; }
private TcpListener Server;
public void CloseServer()
{
throw new NotImplementedException();
}
public void Start(IServerView view)
{
ServerView = view;
}
public void StartServer(string address, int port)
{
try
{
Server = new TcpListener(IPAddress.Parse(address), port);
Server.Start();
ServerView.UpdateChatBox("Server is started, waiting for clients...");
ListenForClientsAsync();
}
catch (Exception e)
{
ServerView.UpdateChatBox(e.Message);
}
}
private async void ListenForClientsAsync()
{
try
{
while (true)
{
var client = await Server.AcceptTcpClientAsync().ConfigureAwait(false);
if (client.Connected)
{
ReadStreamAsync(client);
ServerView.UpdateChatBox("Client connected.");
}
}
}
catch (Exception e)
{
ServerView.UpdateChatBox(e.Message);
}
}
private async void ReadStreamAsync(TcpClient client)
{
NetworkStream streamer = client.GetStream();
Byte[] buff = new Byte[1024];
String msg;
int buffRecived = await streamer.ReadAsync(buff, 0, buff.Length).ConfigureAwait(false);
msg = System.Text.Encoding.ASCII.GetString(buff);
if (buffRecived > 0)
ServerView.UpdateChatBox(msg);
}
}
}
所以如果我不使用Windows窗体但是命令行版本似乎一切正常,多个客户端可以连接到套接字并发送消息。
但如果我使用的是Windows Forms版本(包含代码),则会重现跨线程异常。我认为问题是
var client = await Server.AcceptTcpClientAsync().ConfigureAwait(false);
和
int buffRecived = await streamer.ReadAsync(buff ,0 , buff.Length).ConfigureAwait(false);
因为如果我删除ConfigureAwait(false),看起来没问题但......程序无法正常工作,因为如果客户端连接并发送消息,程序将等待另一个连接,并且不会重新发送消息来自连接客户端。任何人都知道如何更改它才能正常工作?
Form1.cs的
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ChatServer
{
public interface IServerView
{
void UpdateChatBox(string message);
void UpdateClientBox(string message);
};
public partial class ServerView : Form, IServerView
{
private IServerPresenter _presenter;
public ServerView(IServerPresenter presenter)
{
_presenter = presenter;
InitializeComponent();
}
public void UpdateChatBox(string message)
{
chatListBox.Items.Add(message);
}
public void UpdateClientBox(string message)
{
clientListBox.Items.Add(message);
}
private void button1_Click(object sender, EventArgs e)
{
_presenter.StartServer(textBox1.Text, Convert.ToInt32(numericUpDown1.Value));
}
}
}
答案 0 :(得分:1)
如果我删除
ConfigureAwait(false)
,看起来没问题,但......程序无法正常工作
然而,这是解决问题的第一步。通过调用ConfigureAwait(false)
,您告诉框架在执行异步操作的继续时不要再回到UI线程。因此,当您尝试与UI对象进行交互时,您处于错误的线程中。因此例外。
下一步是修复您的接收代码。代码中没有任何内容可以说明为什么您似乎没有接收数据。那部分应该有效。但是你没有等待接收操作,这解释了为什么你的代码还在等待新的连接。如果您不想这样,您的代码应该看起来更像这样:
private async void ListenForClientsAsync()
{
try
{
while (true)
{
var client = await Server.AcceptTcpClientAsync();
if (client.Connected)
{
ServerView.UpdateChatBox("Client connected.");
await ReadStreamAsync(client);
}
}
}
catch (Exception e)
{
ServerView.UpdateChatBox(e.Message);
}
}
private async Task ReadStreamAsync(TcpClient client)
{
NetworkStream streamer = client.GetStream();
Byte[] buff = new Byte[1024];
String msg;
int buffRecived;
// receive data until connection is closed, i.e. receive completes
// with 0-byte length.
while ((buffRecived = await streamer.ReadAsync(buff, 0, buff.Length)) > 0)
{
msg = System.Text.Encoding.ASCII.GetString(buff);
ServerView.UpdateChatBox(msg);
}
}
注意:
ReadStreamAsync()
方法,以便它会读取所有已发送的数据,直到连接关闭。StreamReader
逐行阅读文本可能更有意义。