一、环境配置
1、创建wpf工程
文件->新建->解决方案
C#->WPF应用程序
修改配置管理器更改为release/X64
运行->工程配置成功
2、TCP/IP通讯依赖项
using System.Net; using System.Net.Sockets;二、TCP服务器后台代码实现
1、客户端信息结构体,用于管理连接的客户端
public class ClientInfo { public Socket ClientSocket { get; set; } public string ClientId { get; set; } public DateTime ConnectTime { get; set; } public string RemoteEndPoint { get; set; } public bool IsConnected { get; set; } public Thread ClientThread { get; set; } public DateTime LastActivityTime { get; set; } }2、变量
private Socket _serverSocket; private Thread _listenerThread; private Thread _heartbeatThread; private readonly List<ClientInfo> _connectedClients = new List<ClientInfo>(); private readonly object _clientListLock = new object(); private bool _isRunning = false; private bool _disposed = false; private readonly int _receiveBufferSize = 1024; private readonly int _heartbeatInterval = 30; // 秒 public IPAddress ip = IPAddress.Parse("127.0.0.1");3、事件
public event Action<ClientInfo> OnClientConnected; /// <summary> /// 客户端断开事件 /// </summary> public event Action<ClientInfo> OnClientDisconnected; /// <summary> /// 接收到数据事件 /// </summary> public event Action<ClientInfo, string> OnDataReceived; /// <summary> /// 服务器启动事件 /// </summary> public event Action OnServerStarted; /// <summary> /// 服务器停止事件 /// </summary> public event Action OnServerStopped; /// <summary> /// 日志事件 /// </summary> public event Action<DateTime, string, string> OnLogMessage; #endregion4、构造函数
public TcpServer() { } /// <summary> /// 构造函数 /// </summary> /// <param name="port">监听端口</param> public TcpServer(int port) { Port = port; }5、开启监听
public bool Start(int port = 0) { if (_isRunning) { LogWarning("服务器已经在运行中"); return false; } try { if (port > 0) { Port = port; } if (Port <= 0) { LogError("端口号无效"); return false; } // 创建服务器Socket _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 设置Socket选项:允许地址重用 _serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); // 绑定IP和端口 IPEndPoint localEndPoint = new IPEndPoint(ip, Port); _serverSocket.Bind(localEndPoint); // 开始监听,设置最大等待连接数 _serverSocket.Listen(100); _isRunning = true; // 启动监听线程 _listenerThread = new Thread(ListenerThreadProc) { IsBackground = true, Name = "TCP_Server_Listener" }; _listenerThread.Start(); // 启动心跳检测线程 _heartbeatThread = new Thread(HeartbeatThreadProc) { IsBackground = true, Name = "TCP_Server_Heartbeat" }; _heartbeatThread.Start(); LogInfo($"TCP服务器启动成功,监听端口: {Port}"); OnServerStarted?.Invoke(); return true; } catch (Exception ex) { LogError($"启动服务器失败: {ex.Message}", ex); _isRunning = false; return false; } }6、结束监听
public void Stop() { if (!_isRunning) return; LogInfo("正在停止服务器..."); _isRunning = false; try { // 断开所有客户端连接 DisconnectAllClients(); // 关闭服务器Socket if (_serverSocket != null) { _serverSocket.Close(); _serverSocket = null; } // 等待监听线程结束 if (_listenerThread != null && _listenerThread.IsAlive) { _listenerThread.Join(2000); } // 等待心跳线程结束 if (_heartbeatThread != null && _heartbeatThread.IsAlive) { _heartbeatThread.Join(1000); } LogInfo("服务器已停止"); OnServerStopped?.Invoke(); } catch (Exception ex) { LogError($"停止服务器时出错: {ex.Message}", ex); } }7、发送和接收
public bool SendToClient(ClientInfo client, string message) { if (client == null || !client.IsConnected || client.ClientSocket == null) { return false; } try { byte[] data = Encoding.UTF8.GetBytes(message); int bytesSent = client.ClientSocket.Send(data); LogDebug($"向客户端 {client.ClientId} 发送了 {bytesSent} 字节"); return bytesSent > 0; } catch (Exception ex) { LogError($"向客户端 {client.ClientId} 发送消息失败: {ex.Message}", ex); HandleClientDisconnect(client); return false; } }接收数据在主界面注册事件,方便在UI上显示
server.OnDataReceived += server_OnDataReceived;private void server_OnDataReceived(ClientInfo client, string data) { // 确保在 UI 线程操作 ReceiveBox Application.Current.Dispatcher.Invoke(() => { string time = DateTime.Now.ToString("HH:mm:ss"); // 格式化客户端信息 string clientInfo = $"{"Client-"+client.ClientId}"; // 按指定格式追加到文本框,每条数据换行 ReceiveBox.AppendText($" {clientInfo} { time}:{data}\n"); // 可选:自动滚动到最新内容 ReceiveBox.ScrollToEnd(); }); }三、TCP客户端后台代码实现
1、变量
#region 私有字段 private Socket _clientSocket; private Thread _receiveThread; private bool _isRunning = false; private bool _disposed = false; private int _receiveBufferSize = 1024; private readonly object _sendLock = new object(); private string _remoteEndPoint;2、事件
#region 事件 /// <summary> /// 连接成功事件 /// </summary> public event Action OnConnected; /// <summary> /// 断开连接事件(参数为断开原因) /// </summary> public event Action<string> OnDisconnected; /// <summary> /// 收到数据事件(参数为原始字符串) /// </summary> public event Action<string> OnDataReceived; /// <summary> /// 发生错误事件(参数为异常和消息) /// </summary> public event Action<Exception, string> OnError; /// <summary> /// 日志事件 /// </summary> public event Action<DateTime, string, string> OnLogMessage; #endregion3、连接
/// <summary> /// 连接到服务器 /// </summary> /// <param name="host">服务器 IP 或主机名</param> /// <param name="port">端口号</param> /// <returns>是否连接成功</returns> public bool Connect(string host, int port) { if (_isRunning) { LogWarning("客户端已处于连接状态"); return false; } try { // 解析主机地址 IPAddress ipAddress; if (!IPAddress.TryParse(host, out ipAddress)) { // 尝试解析主机名 var hostEntry = Dns.GetHostEntry(host); ipAddress = hostEntry.AddressList[0]; } IPEndPoint remoteEndPoint = new IPEndPoint(ipAddress, port); _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 连接服务器 _clientSocket.Connect(remoteEndPoint); _remoteEndPoint = _clientSocket.RemoteEndPoint?.ToString(); // 设置接收超时(30秒,防止线程阻塞) _clientSocket.ReceiveTimeout = 30000; _isRunning = true; // 启动接收线程 _receiveThread = new Thread(ReceiveThreadProc) { IsBackground = true, Name = "TCP_Client_Receiver" }; _receiveThread.Start(); LogInfo($"已连接到服务器 {_remoteEndPoint}"); OnConnected?.Invoke(); return true; } catch (Exception ex) { LogError($"连接服务器失败: {ex.Message}", ex); OnError?.Invoke(ex, "连接失败"); return false; } }4、断开
/// <summary> /// 断开连接 /// </summary> public void Disconnect() { if (!_isRunning) return; LogInfo("正在断开连接..."); _isRunning = false; try { // 关闭 Socket if (_clientSocket != null) { if (_clientSocket.Connected) { _clientSocket.Shutdown(SocketShutdown.Both); } _clientSocket.Close(); _clientSocket = null; } // 等待接收线程结束 if (_receiveThread != null && _receiveThread.IsAlive) { _receiveThread.Join(2000); } LogInfo("已断开连接"); OnDisconnected?.Invoke("主动断开"); } catch (Exception ex) { LogError($"断开连接时出错: {ex.Message}", ex); } }5、发送和接收
/// <summary> /// 发送字符串数据(UTF-8 编码) /// </summary> /// <param name="message">要发送的字符串</param> /// <returns>是否发送成功</returns> public bool Send(string message) { if (string.IsNullOrEmpty(message)) { LogWarning("发送内容为空"); return false; } if (!IsConnected) { LogWarning("未连接到服务器,无法发送"); return false; } try { byte[] data = Encoding.UTF8.GetBytes(message); lock (_sendLock) { int bytesSent = _clientSocket.Send(data); LogDebug($"发送了 {bytesSent} 字节"); return bytesSent > 0; } } catch (Exception ex) { LogError($"发送失败: {ex.Message}", ex); OnError?.Invoke(ex, "发送失败"); HandleDisconnect("发送异常"); return false; } }#region 接收线程 private void ReceiveThreadProc() { LogInfo("接收线程已启动"); byte[] buffer = new byte[_receiveBufferSize]; try { while (_isRunning && _clientSocket != null && _clientSocket.Connected) { try { int bytesReceived = _clientSocket.Receive(buffer); if (bytesReceived == 0) { // 服务器主动关闭连接 LogInfo("服务器已关闭连接"); break; } // 获取数据字符串 string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesReceived); LogDebug($"收到 {bytesReceived} 字节: {receivedData}"); // 处理心跳(服务器发送 "PING" 时回复 "PONG") if (receivedData == "PING") { LogDebug("收到心跳请求,回复 PONG"); Send("PONG"); continue; // 不触发数据事件 } // 触发数据接收事件 OnDataReceived?.Invoke(receivedData); } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.TimedOut) { // 接收超时,继续循环 continue; } else { LogError($"Socket 异常: {ex.Message}", ex); break; } } catch (Exception ex) { LogError($"接收数据异常: {ex.Message}", ex); break; } } } catch (Exception ex) { LogError($"接收线程异常: {ex.Message}", ex); } finally { // 线程结束时,如果仍在运行状态则触发断开 if (_isRunning) { HandleDisconnect("接收线程退出"); } LogInfo("接收线程已退出"); } }接收数据在主界面注册事件,方便在UI上显示
client.OnDataReceived += client_OnDataReceived;private void client_OnDataReceived(string data) { // 确保在 UI 线程操作 ReceiveBox Application.Current.Dispatcher.Invoke(() => { string time = DateTime.Now.ToString("HH:mm:ss"); // 格式化客户端信息 // 按指定格式追加到文本框,每条数据换行 ReceiveBox.AppendText($" {time}:{data}\n"); // 可选:自动滚动到最新内容 ReceiveBox.ScrollToEnd(); }); }三、用WPF设计一个TCP调试助手
1、界面设计
(1)资源
<Window.Resources> <!-- 通用按钮样式 --> <Style TargetType="Button"> <Setter Property="FontFamily" Value="Segoe UI"/> <Setter Property="FontSize" Value="14"/> <Setter Property="Foreground" Value="White"/> <Setter Property="Padding" Value="16,8"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="Effect"> <Setter.Value> <DropShadowEffect ShadowDepth="2" BlurRadius="6" Opacity="0.15"/> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Opacity" Value="0.85"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Opacity" Value="0.7"/> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="Opacity" Value="0.5"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- 文本框样式 --> <Style TargetType="TextBox"> <Setter Property="FontFamily" Value="Consolas, Segoe UI"/> <Setter Property="FontSize" Value="13"/> <Setter Property="Background" Value="White"/> <Setter Property="BorderBrush" Value="#D0D7DE"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Padding" Value="8,6"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TextBox"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4"> <ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsFocused" Value="True"> <Setter Property="BorderBrush" Value="#007ACC"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- 标签样式 --> <Style TargetType="Label"> <Setter Property="FontFamily" Value="Segoe UI"/> <Setter Property="FontSize" Value="14"/> <Setter Property="Foreground" Value="#2C3E50"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="Margin" Value="0"/> </Style> <!-- 复选框样式 --> <Style TargetType="CheckBox"> <Setter Property="FontFamily" Value="Segoe UI"/> <Setter Property="FontSize" Value="13"/> <Setter Property="Foreground" Value="#2C3E50"/> <Setter Property="VerticalContentAlignment" Value="Center"/> </Style> <!-- 分组边框样式 --> <Style x:Key="GroupBorder" TargetType="Border"> <Setter Property="Background" Value="White"/> <Setter Property="BorderBrush" Value="#E2E8F0"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="CornerRadius" Value="8"/> <Setter Property="Effect"> <Setter.Value> <DropShadowEffect ShadowDepth="2" BlurRadius="8" Opacity="0.06"/> </Setter.Value> </Setter> </Style> <!-- 标题文本样式 --> <Style x:Key="SectionTitle" TargetType="TextBlock"> <Setter Property="FontFamily" Value="Segoe UI"/> <Setter Property="FontSize" Value="15"/> <Setter Property="FontWeight" Value="SemiBold"/> <Setter Property="Foreground" Value="#2C3E50"/> <Setter Property="Margin" Value="0,0,0,8"/> </Style> </Window.Resources>(2) 布局
<Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions>(3) 标题栏和连接区
<!-- ========== 标题栏 ========== --> <TextBlock Grid.Row="0" Text="🔌 TCP 调试助手" FontSize="22" FontWeight="Bold" Foreground="#2C3E50" Margin="0,0,0,18" FontFamily="Segoe UI"/> <!-- ========== 连接设置区 ========== --> <Border Grid.Row="1" Style="{StaticResource GroupBorder}" Padding="16,14" Margin="0,0,0,18"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="140"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Label Content="模式" Grid.Column="0" Margin="0,0,8,0"/> <ComboBox x:Name="ModeCombo" Grid.Column="1" Height="32" FontSize="13" SelectedIndex="0" Background="White" BorderBrush="#D0D7DE" Foreground="Black" VerticalContentAlignment="Center"> <ComboBoxItem Content="客户端" Background="White" IsSelected="True"/> <ComboBoxItem Content="服务端" Background="White" /> </ComboBox> <Label Content="本地 IP" Grid.Column="2" Margin="0,0,8,0"/> <TextBox x:Name="LocalIPBox" Grid.Column="3" Text="127.0.0.1" /> <Label Content="端口" Grid.Column="4" Margin="16,0,8,0"/> <TextBox x:Name="PortBox" Grid.Column="5" Text="8000" /> <Button x:Name="btnConnect" Grid.Column="6" Content="连接" Background="#27AE60" Height="36" Width="80" Margin="16,0,6,0" Click="btnConnect_Click"/> <Button x:Name="btnDisconnect" Grid.Column="7" Content="断开" Background="#E74C3C" Height="36" Width="80" Margin="0,0,16,0" Click="btnDisconnect_Click"/> <StackPanel Grid.Column="8" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,16,0"> <Ellipse x:Name="Ellipse1" Width="12" Height="12" Fill="#E74C3C" Margin="0,0,6,0" /> <TextBlock x:Name="tbConnected" Text="未连接" Foreground="#E74C3C" FontSize="13" FontWeight="SemiBold" /> </StackPanel> </Grid> </Border>(4)发送区和接收区
<!-- ========== 接收区 ========== --> <Grid Grid.Row="2" Margin="0,0,0,12"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- 接收工具栏 --> <Grid Grid.Row="0" Margin="0,0,0,8"> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="450"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="📥 接收数据" Style="{StaticResource SectionTitle}"/> <CheckBox Grid.Column="2" Content="十六进制显示" Margin="0,0,16,0" VerticalAlignment="Center"/> <CheckBox Grid.Column="3" Content="暂停显示" Margin="0,0,16,0" VerticalAlignment="Center"/> <Button Grid.Column="4" Name="btnClear" Content="清空" Background="#95A5A6" Width="70" FontSize="13" Padding="0" Click="Button_Click" Margin="15,0,15,0"/> </Grid> <!-- 接收文本框 --> <Border Grid.Row="1" Style="{StaticResource GroupBorder}" Padding="6"> <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"> <TextBox x:Name="ReceiveBox" TextWrapping="Wrap" IsReadOnly="True" Background="#FAFCFE" FontSize="13" BorderThickness="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled" AcceptsReturn="True" MinHeight="200"> <TextBox.Effect> <DropShadowEffect ShadowDepth="0" BlurRadius="0" Opacity="0"/> </TextBox.Effect> </TextBox> </ScrollViewer> </Border> </Grid><!-- ========== 发送区 ========== --> <Grid Grid.Row="3"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- 发送工具栏 --> <Grid Grid.Row="0" Margin="0,0,0,8"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="📤 发送数据" Style="{StaticResource SectionTitle}"/> <CheckBox Grid.Column="2" Content="十六进制发送" Margin="0,0,16,0" VerticalAlignment="Center"/> <CheckBox Grid.Column="3" Content="自动发送" Margin="0,0,10,0" VerticalAlignment="Center"/> <TextBox Grid.Column="4" Text="1000" Width="60" Height="28" FontSize="12" TextAlignment="Center" VerticalContentAlignment="Center"/> <TextBlock Grid.Column="5" Text="ms" FontSize="13" Foreground="#7F8C8D" VerticalAlignment="Center" Margin="6,0,0,0"/> </Grid> <!-- 发送输入框 --> <Border Grid.Row="1" Style="{StaticResource GroupBorder}" Padding="6" Margin="0,0,0,10"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBox x:Name="SendBox" Grid.Column="0" TextWrapping="Wrap" AcceptsReturn="True" Background="#FAFCFE" FontSize="13" BorderThickness="0" MinHeight="70" VerticalScrollBarVisibility="Auto"> <TextBox.Effect> <DropShadowEffect ShadowDepth="0" BlurRadius="0" Opacity="0"/> </TextBox.Effect> </TextBox> <Button x:Name="SendBtn" Grid.Column="1" Content="发送" Background="#3498DB" Height="40" Width="90" FontSize="16" Margin="12,0,0,0" VerticalAlignment="Center"/> </Grid> </Border> <!-- 底部状态栏(可选) --> <TextBlock Grid.Row="2" Text="按 Ctrl+Enter 快速发送" FontSize="12" Foreground="#95A5A6" HorizontalAlignment="Right"/> </Grid>(5)效果
运行
2、后台实现
(1)选择客户端和服务端时切换按钮
private void ModeCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (btnConnect == null || btnDisconnect == null) return; ComboBox combo = sender as ComboBox; if (combo.SelectedItem is ComboBoxItem item) { string content = item.Content.ToString(); // 执行您的切换逻辑 // 例如:更新界面、切换主题等 if (ModeCombo.SelectedIndex == 1) { btnConnect.Content = "监听"; btnDisconnect.Content = "结束"; } else { btnConnect.Content = "连接"; btnDisconnect.Content = "断开"; } } }(2) 连接按钮,创建服务器或客户端
private void btnConnect_Click(object sender, RoutedEventArgs e) { string ipText = LocalIPBox.Text.Trim(); //校验IP是都合法 if (string.IsNullOrWhiteSpace(ipText)) { MessageBox.Show("IP 地址不能为空", "输入错误", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (!IPAddress.TryParse(ipText, out IPAddress ipFinal)) { MessageBox.Show("请输入合法的 IP 地址", "格式错误", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (ModeCombo.SelectedIndex == 1) { string portText = PortBox.Text.Trim(); if (IsValidPort(portText)) { int port = int.Parse(portText); server = new TcpServer(port); server.ip = ipFinal; server.OnDataReceived += server_OnDataReceived; } else { MessageBox.Show("请输入合法的端口号", "格式错误", MessageBoxButton.OK, MessageBoxImage.Warning); } //开启监听 bool isStartSuccess = server.Start(); if (!isStartSuccess) { MessageBox.Show("开启失败", "异常错误", MessageBoxButton.OK, MessageBoxImage.Warning); } else { btnDisconnect.IsEnabled = true; btnConnect.IsEnabled = false; tbConnected.Text = "已连接"; tbConnected.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#27AE60")); Ellipse1.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#27AE60")); } }else { string portText = PortBox.Text.Trim(); if (IsValidPort(portText)) { int port = int.Parse(portText); client = new TcpClient(); client.OnDataReceived += client_OnDataReceived; bool isConnected=client.Connect(ipText, port); if (!isConnected) { MessageBox.Show("开启失败", "异常错误", MessageBoxButton.OK, MessageBoxImage.Warning); } else { btnDisconnect.IsEnabled = true; btnConnect.IsEnabled = false; tbConnected.Text = "已连接"; tbConnected.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#27AE60")); Ellipse1.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#27AE60")); } } else { MessageBox.Show("请输入合法的端口号", "格式错误", MessageBoxButton.OK, MessageBoxImage.Warning); } } }(3)断开连接按钮
private void btnDisconnect_Click(object sender, RoutedEventArgs e) { if (ModeCombo.SelectedIndex == 1) { if (server != null) { server.Stop(); server.Dispose(); } btnDisconnect.IsEnabled = false; btnConnect.IsEnabled = true; tbConnected.Text = "未连接"; tbConnected.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#E74C3C")); Ellipse1.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#E74C3C")); } else { if (client != null) { client.Disconnect(); client.Dispose(); } btnDisconnect.IsEnabled = false; btnConnect.IsEnabled = true; tbConnected.Text = "未连接"; tbConnected.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#E74C3C")); Ellipse1.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#E74C3C")); } }(4)接收到数据的事件处理程序
private void server_OnDataReceived(ClientInfo client, string data) { // 确保在 UI 线程操作 ReceiveBox Application.Current.Dispatcher.Invoke(() => { string time = DateTime.Now.ToString("HH:mm:ss"); // 格式化客户端信息 string clientInfo = $"{"Client-"+client.ClientId}"; // 按指定格式追加到文本框,每条数据换行 ReceiveBox.AppendText($" {clientInfo} { time}:{data}\n"); // 可选:自动滚动到最新内容 ReceiveBox.ScrollToEnd(); }); }private void client_OnDataReceived(string data) { // 确保在 UI 线程操作 ReceiveBox Application.Current.Dispatcher.Invoke(() => { string time = DateTime.Now.ToString("HH:mm:ss"); // 格式化客户端信息 // 按指定格式追加到文本框,每条数据换行 ReceiveBox.AppendText($" {time}:{data}\n"); // 可选:自动滚动到最新内容 ReceiveBox.ScrollToEnd(); }); }(5)发送按钮实现
private void btnSend_Click(object sender, RoutedEventArgs e) { if (ModeCombo.SelectedIndex == 1) { if(server!=null) { List<ClientInfo> ClientList = server.GetAllConnectedClients(); server.SendToClient(ClientList[0], tbSend.Text); } } else { if (client != null) { client.Send(tbSend.Text); } } }3、效果演示
(1)做客户端时
利用TCP助手创建TCP服务器
连接
接收助手信息
发送消息到助手
(2)做服务端时
连接
多台客户端同时向服务器发送数据
向指定客户端发送数据
(3)两个自己写的助手通讯
四、利用TCP通讯实现海康相机软触发拍照及向PLC发送NG信号
1、通讯触发拍照
采用TCP服务器作为上位机触发接收端,可以同时接受多个客户端的触发信号。
(1) 相机初始化
camera = new interfaceMVS_SDK(); camera.RefreshDeviceList(); camera.SelectCamera(0); camera.OpenDevice();(2)注册事件
server = new TcpServer(8000); server.OnDataReceived += server_OnDataReceived;(3)事件处理函数
private void server_OnDataReceived(ClientInfo client, string data) { MessageBox.Show("接收到数据"); // 必须通过 Dispatcher 更新 UI Dispatcher.Invoke(() => { // 将接收到的数据追加到 TextBox if (data=="1234") { camera.StartGrabbing(); int flag = camera.GetOneFrame(); //MessageBox.Show(flag.ToString()); if (flag == MvError.MV_OK) { IFrameOut temp = camera.ReturnOneFrame(); var bitmap = temp?.Image?.ToBitmap(); Bitmap colorBitmap = GrayscaleTo24bppRgb(bitmap); var show = ConvertToBitmapImage(colorBitmap); ImageOri.Dispatcher.BeginInvoke(new Action(() => { ImageOri.Source = show; })); } camera.StopGrabbing(); } }); }2、发送相机识别结果
private void server_OnDataReceived(ClientInfo client, string data) { MessageBox.Show("接收到数据"); // 必须通过 Dispatcher 更新 UI Dispatcher.Invoke(() => { // 将接收到的数据追加到 TextBox if (data=="1234") { camera.StartGrabbing(); int flag = camera.GetOneFrame(); //MessageBox.Show(flag.ToString()); if (flag == MvError.MV_OK) { IFrameOut temp = camera.ReturnOneFrame(); var bitmap = temp?.Image?.ToBitmap(); Bitmap colorBitmap = GrayscaleTo24bppRgb(bitmap); var show = ConvertToBitmapImage(colorBitmap); ImageOri.Dispatcher.BeginInvoke(new Action(() => { ImageOri.Source = show; })); } camera.StopGrabbing(); if (server != null) { List<ClientInfo> ClientList = server.GetAllConnectedClients(); server.SendToClient(ClientList[0], "拍照完成"); } } }); }