You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在单个WPF应用中实现TCP/IP连接的技术求助

问题描述

我正在为学校做一个实时测验的个人项目,想把TCP/IP集成到单个WPF应用里(不拆成客户端和服务端两个独立程序),而且只能用TcpClient和TcpListener类(不能用SignalR、Socket或者其他包),但实现的时候卡壳了。我知道控制台里可以用reader.WriteLine()这类方法,但不知道在WPF里该怎么操作。下面是我写的代码(之后还要加线程),不确定代码能不能跑,也不知道怎么让这些类之间通信。


现有代码

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    Server Server = new Server();
    Client Client = new Client();

    public MainWindow()
    {
        InitializeComponent();
    }
}

Client类

public class Client
{
    public static void Maain(string message) 
    {
        TcpClient client = new TcpClient("127.0.0.1", 8080);

        StreamReader ClientInput = new StreamReader(client.GetStream());
        StreamWriter ServerOutput = new StreamWriter(client.GetStream());

        Console.WriteLine();

        ServerOutput.WriteLine(message);
        ServerOutput.Flush();

        string server_string = ClientInput.ReadLine();
        Console.WriteLine(server_string); 
    }
}

Server类

public class Server
{
    public static void Maaein() 
    {
        TcpListener server =  new TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
        // server has started - message
        server.Start();

        while (true) 
        {
            // accepts tcp client - message
            TcpClient client = server.AcceptTcpClient();

            // gets messages from the client
            StreamReader clientInput = new StreamReader(client.GetStream());
            //sends messages from server
            StreamWriter serverOutput = new StreamWriter(client.GetStream());

            string message = "";

            Console.WriteLine("Message from Client recieved: " + message);
            Console.WriteLine("Message recieved to Server: " + message);
        
            clientInput.Close();
            serverOutput.Close();
            client.Close();
        }
    }        
}

问题修复与实现方案

现有代码的核心问题

  1. 方法名拼写错误:Maaein、Maain都是拼写失误,且静态方法无法通过实例调用,你在MainWindow里实例化了Server/Client却没法触发逻辑。
  2. 服务端未读取消息:Server里的message变量是空的,根本没调用clientInput.ReadLine()读取客户端内容。
  3. 主线程阻塞:AcceptTcpClient()是阻塞方法,直接在WPF主线程调用会导致界面卡死,必须用异步/多线程。
  4. 控制台输出不适用WPF:WPF里应该把消息显示到界面控件(如TextBox),且更新UI必须在主线程执行。
  5. 资源未正确释放:StreamReader、TcpClient等资源没做自动释放,容易内存泄漏。

修复后的完整实现

1. 重构Server类(异步+UI回调)

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Threading;

public class Server
{
    private TcpListener _listener;
    private Dispatcher _uiDispatcher;
    private Action<string> _onLogUpdate;

    public Server(Dispatcher uiDispatcher, Action<string> onLogUpdate)
    {
        _uiDispatcher = uiDispatcher;
        _onLogUpdate = onLogUpdate;
    }

    public async Task StartAsync()
    {
        _listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
        _listener.Start();
        UpdateLog("服务端已启动,等待客户端连接...");

        while (true)
        {
            try
            {
                using TcpClient client = await _listener.AcceptTcpClientAsync();
                UpdateLog("客户端已连接");
                await HandleClientAsync(client);
            }
            catch (Exception ex)
            {
                UpdateLog($"服务端错误:{ex.Message}");
                break;
            }
        }
    }

    private async Task HandleClientAsync(TcpClient client)
    {
        using NetworkStream stream = client.GetStream();
        using StreamReader reader = new StreamReader(stream);
        using StreamWriter writer = new StreamWriter(stream) { AutoFlush = true };

        try
        {
            string clientMsg = await reader.ReadLineAsync();
            if (!string.IsNullOrEmpty(clientMsg))
            {
                UpdateLog($"收到客户端消息:{clientMsg}");
                string response = $"服务端已接收:{clientMsg}";
                await writer.WriteLineAsync(response);
                UpdateLog($"发送响应:{response}");
            }
        }
        catch (Exception ex)
        {
            UpdateLog($"处理客户端消息出错:{ex.Message}");
        }
    }

    public void Stop()
    {
        _listener?.Stop();
        UpdateLog("服务端已停止");
    }

    private void UpdateLog(string content)
    {
        _uiDispatcher.Invoke(() => _onLogUpdate($"{DateTime.Now:HH:mm:ss} - {content}"));
    }
}

2. 重构Client类(异步+UI回调)

using System;
using System.Net.Sockets;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Threading;

public class Client
{
    private Dispatcher _uiDispatcher;
    private Action<string> _onLogUpdate;

    public Client(Dispatcher uiDispatcher, Action<string> onLogUpdate)
    {
        _uiDispatcher = uiDispatcher;
        _onLogUpdate = onLogUpdate;
    }

    public async Task SendMessageAsync(string message)
    {
        if (string.IsNullOrWhiteSpace(message)) return;

        try
        {
            using TcpClient client = new TcpClient();
            await client.ConnectAsync("127.0.0.1", 8080);
            UpdateLog("已连接到服务端");

            using NetworkStream stream = client.GetStream();
            using StreamReader reader = new StreamReader(stream);
            using StreamWriter writer = new StreamWriter(stream) { AutoFlush = true };

            await writer.WriteLineAsync(message);
            UpdateLog($"发送消息:{message}");

            string serverResponse = await reader.ReadLineAsync();
            UpdateLog($"收到服务端响应:{serverResponse}");
        }
        catch (Exception ex)
        {
            UpdateLog($"客户端错误:{ex.Message}");
        }
    }

    private void UpdateLog(string content)
    {
        _uiDispatcher.Invoke(() => _onLogUpdate($"{DateTime.Now:HH:mm:ss} - {content}"));
    }
}

3. MainWindow集成(UI交互)

先在MainWindow.xaml里添加界面元素:

<Window x:Class="QuizApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="实时测验TCP Demo" Height="450" Width="800">
    <Grid Margin="20">
        <StackPanel>
            <TextBox x:Name="LogBox" IsReadOnly="True" Height="300" VerticalScrollBarVisibility="Auto"/>
            <Button x:Name="StartServerBtn" Content="启动服务端" Click="StartServerBtn_Click" Margin="0 10"/>
            <TextBox x:Name="InputMsgBox" Margin="0 10" PlaceholderText="输入要发送的消息"/>
            <Button x:Name="SendMsgBtn" Content="发送消息" Click="SendMsgBtn_Click" Margin="0 10"/>
        </StackPanel>
    </Grid>
</Window>

然后是后台逻辑:

using System;
using System.Windows;
using System.Threading.Tasks;

namespace QuizApp
{
    public partial class MainWindow : Window
    {
        private Server _server;
        private Client _client;

        public MainWindow()
        {
            InitializeComponent();
            _server = new Server(Dispatcher, AddToLog);
            _client = new Client(Dispatcher, AddToLog);
        }

        private void AddToLog(string content)
        {
            LogBox.AppendText($"{content}\n");
            LogBox.ScrollToEnd();
        }

        private async void StartServerBtn_Click(object sender, RoutedEventArgs e)
        {
            StartServerBtn.IsEnabled = false;
            await _server.StartAsync();
        }

        private async void SendMsgBtn_Click(object sender, RoutedEventArgs e)
        {
            await _client.SendMessageAsync(InputMsgBox.Text.Trim());
            InputMsgBox.Clear();
        }

        protected override void OnClosed(EventArgs e)
        {
            _server?.Stop();
            base.OnClosed(e);
        }
    }
}

关键注意事项

  • 异步优先:所有TCP操作必须用异步方法,避免阻塞WPF主线程导致界面卡死。
  • UI线程安全:更新界面控件必须通过Dispatcher.Invoke切回主线程,否则会抛出跨线程异常。
  • 资源自动释放:用using语句处理所有实现IDisposable的对象,避免内存泄漏。
  • 错误捕获:添加异常处理,防止单个连接出错导致整个服务端崩溃。

内容的提问来源于stack exchange,提问作者mskr710

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 19:58:11