套接字编程C#聊天服务器,WinForms中的交叉线程

时间:2017-02-26 23:08:11

标签: c# winforms sockets async-await

我尝试创建简单的聊天服务器,它处理多个客户端,并从所有客户端收集消息。 以下是我的代码。

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));
        }
    }
}

1 个答案:

答案 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逐行阅读文本可能更有意义。