c# - 将控制台聊天程序转换为 GUI

标签 c# multithreading sockets networking network-programming

我在控制台中有一个基本的聊天客户端。这是代码

对于服务器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace chat_server
{
 class Program
{
    static TcpListener server = new TcpListener(IPAddress.Any, 9999);

    static void input(object obs)
    {
        StreamWriter writer = obs as StreamWriter;
        string op = "nothing";
        while (!op.Equals("exit"))
        {
            Console.ResetColor();
            Console.WriteLine("This is the " + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Enter your text(type exit to quit)");
            op = Console.ReadLine();
            writer.WriteLine(op);
            writer.Flush();
        }
    }

    static void output(Object obs)
    {
        StreamReader reader = obs as StreamReader;
        Console.ForegroundColor = ConsoleColor.Green;
        while (true)
        {
            Console.WriteLine(reader.ReadLine());
        }
    }

    static void monitor()
    {
        while (true)
        {
            TcpClient cls = server.AcceptTcpClient();
            Thread th = new Thread(new ParameterizedThreadStart(mul_stream));
            th.Start(cls);
        }
    }

    static void mul_stream(Object ob)
    {
        TcpClient client = ob as TcpClient;
        Stream streams = client.GetStream();
        StreamReader reads = new StreamReader(streams);
        StreamWriter writs = new StreamWriter(streams);

        new Thread(new ParameterizedThreadStart(output)).Start(reads);
        input(writs);
    }

    static void Main(string[] args)
    {

        server.Start();
        monitor();
        server.Stop();
        Console.ReadKey();
    }
 }
}

这是客户端代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace chat_client
{
 class Program
 {
    static StreamReader reader;
    static StreamWriter writer;
    static Thread input_thread;

    static void input()
    {
        string op = "nothing";
        while (!op.Equals("exit"))
        {
            Console.ResetColor();
            Console.WriteLine("Enter your text(type exit to quit)");
            op = Console.ReadLine();
            writer.WriteLine(op);
            writer.Flush();
        }
    }

    static void output()
    {
        Console.ForegroundColor = ConsoleColor.Blue;
        while (true)
        {
            Console.WriteLine(reader.ReadLine());
        }
    }


    static void Main(string[] args)
    {
        Console.WriteLine("Enter the ip address");
        string ip = Console.ReadLine();
        TcpClient client = new TcpClient(ip,9999);

        NetworkStream stream = client.GetStream();
        reader = new StreamReader(stream);
        writer = new StreamWriter(stream);

        input_thread = new Thread(input);
        input_thread.Start();

        /*
        writer.Write("Hello world");
        writer.Flush();
        Console.WriteLine("Message Sent");*/
        output();
        client.Close();
        Console.ReadKey();
    }
 }
}

现在的问题是我在将此代码转换为 GUI 时遇到了一些问题。例如,服务器中通过特定流向客户端传递消息的输入功能应该在某种程度上等同于 GUI 中的 SEND 按钮。

但是每个线程都创建自己的流,我认为在不同线程上创建单独的事件处理程序不是一个好主意。

简而言之,我需要一些关于从哪里开始这个项目的建议。

谢谢。

最佳答案

网络很难。您当前的方法只是阅读所有内容并将所有内容视为完整的消息,这种方法很脆弱。它在调试期间有效,但在生产期间会失败,因为 TCP 是基于流的。

相反,您可以使用现有框架来抽象出网络层。碰巧,我制作了一个开源框架 (LGPL)。

在这种情况下,我们只想聊天。所以我添加了一个这样的聊天消息定义:

public class ChatMessage
{
    public DateTime CreatedAt { get; set; }
    public string UserName { get; set; }
    public string Message { get; set; }
}

该消息被放入一个共享程序集中(由客户端和服务器使用)。

服务器本身是这样定义的:

public class ChatServer : IServiceFactory
{
    private readonly List<ClientChatConnection> _connectedClients = new List<ClientChatConnection>();
    private readonly MessagingServer _server;


    public ChatServer()
    {
        var messageFactory = new BasicMessageFactory();
        var configuration = new MessagingServerConfiguration(messageFactory);
        _server = new MessagingServer(this, configuration);
    }

    public IServerService CreateClient(EndPoint remoteEndPoint)
    {
        var client = new ClientChatConnection(this);
        client.Disconnected += OnClientDisconnect;

        lock (_connectedClients)
            _connectedClients.Add(client);

        return client;
    }

    private void OnClientDisconnect(object sender, EventArgs e)
    {
        var me = (ClientChatConnection) sender;
        me.Disconnected -= OnClientDisconnect;
        lock (_connectedClients)
            _connectedClients.Remove(me);
    }

    public void SendToAllButMe(ClientChatConnection me, ChatMessage message)
    {
        lock (_connectedClients)
        {
            foreach (var client in _connectedClients)
            {
                if (client == me)
                    continue;

                client.Send(message);
            }
        }
    }

    public void SendToAll(ChatMessage message)
    {
        lock (_connectedClients)
        {
            foreach (var client in _connectedClients)
            {
                client.Send(message);
            }
        }
    }

    public void Start()
    {
        _server.Start(new IPEndPoint(IPAddress.Any, 7652));
    }
}

看到了吗?任何地方都没有网络代码。

客户端更简单:

static class Program
{
    private static MainForm _mainForm;
    private static MessagingClient _client;

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        ConfigureChat();
        _mainForm = new MainForm();
        Application.Run(_mainForm);
    }

    private static void ConfigureChat()
    {
        _client = new MessagingClient(new BasicMessageFactory());
        _client.Connect(new IPEndPoint(IPAddress.Loopback, 7652));
        _client.Received += OnChatMessage;
    }

    private static void OnChatMessage(object sender, ReceivedMessageEventArgs e)
    {
        _mainForm.InvokeIfRequired(() => _mainForm.AddChatMessage((ChatMessage)e.Message));
    }

    public static void SendChatMessage(ChatMessage msg)
    {
        if (msg == null) throw new ArgumentNullException("msg");
        _client.Send(msg);
    }
}

enter image description here

完整示例可在此处获得:https://github.com/jgauffin/Samples/tree/master/Griffin.Networking/ChatServerClient

更新:

由于这是一个学校项目,而且您不能使用 .NET 以外的任何东西,所以我可能会使用最简单的方法。那就是使用新行 ("\r\n") 作为分隔符。

所以在每一边你只使用了 var chatMessage = streamReader.ReadLine()streamWriter.WriteLine("Chat message");

关于c# - 将控制台聊天程序转换为 GUI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13433706/

相关文章:

c# - (C#) 枚举不会返回 int

c# - 尝试连接到远程 WCF 服务时出现 TCP 错误 (10060)

javascript - 通过 AJAX 调用将 HTML 传递给 C# MVC Controller 。 500 错误

java - 在线程中调用创建线程的类中的方法

c# - 获取StreamSocket的连接状态(UWP)

c# - 0 到枚举的隐式转换

c#:什么是线程轮询?

Java 8+ ConcurrentHashMap 锁 strip 化

java - 从本地计算机连接到ServerSocket

sockets - Wireshark TCP Dup ACK - 奇怪