【发布时间】:2017-02-27 07:31:02
【问题描述】:
我尝试创建简单的聊天服务器,它可以处理多个客户端,并从所有客户端接收消息。 下面是我的代码。
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 窗体版本(包含的代码),它会收到跨线程异常。我认为问题是
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));
}
}
}
【问题讨论】:
-
最简单的更改可能是在访问视图中的 UI 控件之前使用 IsInvokeRequired。见Make Thread-Safe Calls to Windows Forms Controls。请注意,您不必定义新的委托类型,只需使用内置的
Action<string>类型即可。 -
@pinkfloydx33:
ListenForClientsAsync()同步运行是不正确的。任何带有await语句的async方法将在到达await语句时返回。该方法的其余部分将异步执行。 -
你说得对……我错过了,因为我陷入了没有等待的方法本身
-
@mikez:我不同意有任何需要使用
IsInvokeRequired。即使在遗留代码中,这也不是真正必要的(只是无条件地调用),但现代 C# 习惯用法完全取消了显式调用,因此当然不需要检查该属性。使用async/await允许编译器和运行时处理所有实际工作。 -
@PeterDuniho 当然不是完全有必要,它实际上只是为您节省了一些额外的分配,用于在您已经在 UI 线程上的场景中执行调用所需的部分。它也可以通过
Progress<T>来完成,它会自动编组回 UI 线程(假设它是在那里创建的)。
标签: c# winforms sockets async-await