通讯(03):C#上位机TCP/IP通讯与海康相机软触发拍照)
一、环境配置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 ListClientInfo _connectedClients new ListClientInfo(); 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 ActionClientInfo OnClientConnected; /// summary /// 客户端断开事件 /// /summary public event ActionClientInfo OnClientDisconnected; /// summary /// 接收到数据事件 /// /summary public event ActionClientInfo, string OnDataReceived; /// summary /// 服务器启动事件 /// /summary public event Action OnServerStarted; /// summary /// 服务器停止事件 /// /summary public event Action OnServerStopped; /// summary /// 日志事件 /// /summary public event ActionDateTime, string, string OnLogMessage; #endregion4、构造函数public TcpServer() { } /// summary /// 构造函数 /// /summary /// param nameport监听端口/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 Actionstring OnDisconnected; /// summary /// 收到数据事件参数为原始字符串 /// /summary public event Actionstring OnDataReceived; /// summary /// 发生错误事件参数为异常和消息 /// /summary public event ActionException, string OnError; /// summary /// 日志事件 /// /summary public event ActionDateTime, string, string OnLogMessage; #endregion3、连接/// summary /// 连接到服务器 /// /summary /// param namehost服务器 IP 或主机名/param /// param nameport端口号/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 namemessage要发送的字符串/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 TargetTypeButton Setter PropertyFontFamily ValueSegoe UI/ Setter PropertyFontSize Value14/ Setter PropertyForeground ValueWhite/ Setter PropertyPadding Value16,8/ Setter PropertyBorderThickness Value0/ Setter PropertyCursor ValueHand/ Setter PropertyEffect Setter.Value DropShadowEffect ShadowDepth2 BlurRadius6 Opacity0.15/ /Setter.Value /Setter Setter PropertyTemplate Setter.Value ControlTemplate TargetTypeButton Border Background{TemplateBinding Background} CornerRadius6 Padding{TemplateBinding Padding} BorderBrush{TemplateBinding BorderBrush} BorderThickness{TemplateBinding BorderThickness} ContentPresenter HorizontalAlignmentCenter VerticalAlignmentCenter/ /Border ControlTemplate.Triggers Trigger PropertyIsMouseOver ValueTrue Setter PropertyOpacity Value0.85/ /Trigger Trigger PropertyIsPressed ValueTrue Setter PropertyOpacity Value0.7/ /Trigger Trigger PropertyIsEnabled ValueFalse Setter PropertyOpacity Value0.5/ /Trigger /ControlTemplate.Triggers /ControlTemplate /Setter.Value /Setter /Style !-- 文本框样式 -- Style TargetTypeTextBox Setter PropertyFontFamily ValueConsolas, Segoe UI/ Setter PropertyFontSize Value13/ Setter PropertyBackground ValueWhite/ Setter PropertyBorderBrush Value#D0D7DE/ Setter PropertyBorderThickness Value1/ Setter PropertyPadding Value8,6/ Setter PropertyTemplate Setter.Value ControlTemplate TargetTypeTextBox Border Background{TemplateBinding Background} BorderBrush{TemplateBinding BorderBrush} BorderThickness{TemplateBinding BorderThickness} CornerRadius4 ScrollViewer x:NamePART_ContentHost Margin{TemplateBinding Padding}/ /Border ControlTemplate.Triggers Trigger PropertyIsFocused ValueTrue Setter PropertyBorderBrush Value#007ACC/ /Trigger /ControlTemplate.Triggers /ControlTemplate /Setter.Value /Setter /Style !-- 标签样式 -- Style TargetTypeLabel Setter PropertyFontFamily ValueSegoe UI/ Setter PropertyFontSize Value14/ Setter PropertyForeground Value#2C3E50/ Setter PropertyVerticalContentAlignment ValueCenter/ Setter PropertyMargin Value0/ /Style !-- 复选框样式 -- Style TargetTypeCheckBox Setter PropertyFontFamily ValueSegoe UI/ Setter PropertyFontSize Value13/ Setter PropertyForeground Value#2C3E50/ Setter PropertyVerticalContentAlignment ValueCenter/ /Style !-- 分组边框样式 -- Style x:KeyGroupBorder TargetTypeBorder Setter PropertyBackground ValueWhite/ Setter PropertyBorderBrush Value#E2E8F0/ Setter PropertyBorderThickness Value1/ Setter PropertyCornerRadius Value8/ Setter PropertyEffect Setter.Value DropShadowEffect ShadowDepth2 BlurRadius8 Opacity0.06/ /Setter.Value /Setter /Style !-- 标题文本样式 -- Style x:KeySectionTitle TargetTypeTextBlock Setter PropertyFontFamily ValueSegoe UI/ Setter PropertyFontSize Value15/ Setter PropertyFontWeight ValueSemiBold/ Setter PropertyForeground Value#2C3E50/ Setter PropertyMargin Value0,0,0,8/ /Style /Window.Resources(2) 布局Grid.RowDefinitions RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition Height*/ RowDefinition HeightAuto/ /Grid.RowDefinitions(3) 标题栏和连接区!-- 标题栏 -- TextBlock Grid.Row0 Text TCP 调试助手 FontSize22 FontWeightBold Foreground#2C3E50 Margin0,0,0,18 FontFamilySegoe UI/ !-- 连接设置区 -- Border Grid.Row1 Style{StaticResource GroupBorder} Padding16,14 Margin0,0,0,18 Grid Grid.ColumnDefinitions ColumnDefinition WidthAuto/ ColumnDefinition Width100/ ColumnDefinition WidthAuto/ ColumnDefinition Width140/ ColumnDefinition WidthAuto/ ColumnDefinition Width100/ ColumnDefinition WidthAuto/ ColumnDefinition WidthAuto/ ColumnDefinition WidthAuto/ /Grid.ColumnDefinitions Label Content模式 Grid.Column0 Margin0,0,8,0/ ComboBox x:NameModeCombo Grid.Column1 Height32 FontSize13 SelectedIndex0 BackgroundWhite BorderBrush#D0D7DE ForegroundBlack VerticalContentAlignmentCenter ComboBoxItem Content客户端 BackgroundWhite IsSelectedTrue/ ComboBoxItem Content服务端 BackgroundWhite / /ComboBox Label Content本地 IP Grid.Column2 Margin0,0,8,0/ TextBox x:NameLocalIPBox Grid.Column3 Text127.0.0.1 / Label Content端口 Grid.Column4 Margin16,0,8,0/ TextBox x:NamePortBox Grid.Column5 Text8000 / Button x:NamebtnConnect Grid.Column6 Content连接 Background#27AE60 Height36 Width80 Margin16,0,6,0 ClickbtnConnect_Click/ Button x:NamebtnDisconnect Grid.Column7 Content断开 Background#E74C3C Height36 Width80 Margin0,0,16,0 ClickbtnDisconnect_Click/ StackPanel Grid.Column8 OrientationHorizontal VerticalAlignmentCenter Margin0,0,16,0 Ellipse x:NameEllipse1 Width12 Height12 Fill#E74C3C Margin0,0,6,0 / TextBlock x:NametbConnected Text未连接 Foreground#E74C3C FontSize13 FontWeightSemiBold / /StackPanel /Grid /Border(4)发送区和接收区!-- 接收区 -- Grid Grid.Row2 Margin0,0,0,12 Grid.RowDefinitions RowDefinition HeightAuto/ RowDefinition Height*/ /Grid.RowDefinitions !-- 接收工具栏 -- Grid Grid.Row0 Margin0,0,0,8 Grid.ColumnDefinitions ColumnDefinition Width100/ ColumnDefinition Width450/ ColumnDefinition WidthAuto/ ColumnDefinition Width100/ ColumnDefinition Width100/ /Grid.ColumnDefinitions TextBlock Grid.Column0 Text 接收数据 Style{StaticResource SectionTitle}/ CheckBox Grid.Column2 Content十六进制显示 Margin0,0,16,0 VerticalAlignmentCenter/ CheckBox Grid.Column3 Content暂停显示 Margin0,0,16,0 VerticalAlignmentCenter/ Button Grid.Column4 NamebtnClear Content清空 Background#95A5A6 Width70 FontSize13 Padding0 ClickButton_Click Margin15,0,15,0/ /Grid !-- 接收文本框 -- Border Grid.Row1 Style{StaticResource GroupBorder} Padding6 ScrollViewer VerticalScrollBarVisibilityAuto HorizontalScrollBarVisibilityAuto TextBox x:NameReceiveBox TextWrappingWrap IsReadOnlyTrue Background#FAFCFE FontSize13 BorderThickness0 VerticalScrollBarVisibilityDisabled HorizontalScrollBarVisibilityDisabled AcceptsReturnTrue MinHeight200 TextBox.Effect DropShadowEffect ShadowDepth0 BlurRadius0 Opacity0/ /TextBox.Effect /TextBox /ScrollViewer /Border /Grid!-- 发送区 -- Grid Grid.Row3 Grid.RowDefinitions RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition HeightAuto/ /Grid.RowDefinitions !-- 发送工具栏 -- Grid Grid.Row0 Margin0,0,0,8 Grid.ColumnDefinitions ColumnDefinition WidthAuto/ ColumnDefinition Width*/ ColumnDefinition WidthAuto/ ColumnDefinition WidthAuto/ ColumnDefinition WidthAuto/ ColumnDefinition WidthAuto/ /Grid.ColumnDefinitions TextBlock Grid.Column0 Text 发送数据 Style{StaticResource SectionTitle}/ CheckBox Grid.Column2 Content十六进制发送 Margin0,0,16,0 VerticalAlignmentCenter/ CheckBox Grid.Column3 Content自动发送 Margin0,0,10,0 VerticalAlignmentCenter/ TextBox Grid.Column4 Text1000 Width60 Height28 FontSize12 TextAlignmentCenter VerticalContentAlignmentCenter/ TextBlock Grid.Column5 Textms FontSize13 Foreground#7F8C8D VerticalAlignmentCenter Margin6,0,0,0/ /Grid !-- 发送输入框 -- Border Grid.Row1 Style{StaticResource GroupBorder} Padding6 Margin0,0,0,10 Grid Grid.ColumnDefinitions ColumnDefinition Width*/ ColumnDefinition WidthAuto/ /Grid.ColumnDefinitions TextBox x:NameSendBox Grid.Column0 TextWrappingWrap AcceptsReturnTrue Background#FAFCFE FontSize13 BorderThickness0 MinHeight70 VerticalScrollBarVisibilityAuto TextBox.Effect DropShadowEffect ShadowDepth0 BlurRadius0 Opacity0/ /TextBox.Effect /TextBox Button x:NameSendBtn Grid.Column1 Content发送 Background#3498DB Height40 Width90 FontSize16 Margin12,0,0,0 VerticalAlignmentCenter/ /Grid /Border !-- 底部状态栏可选 -- TextBlock Grid.Row2 Text按 CtrlEnter 快速发送 FontSize12 Foreground#95A5A6 HorizontalAlignmentRight/ /Grid5效果运行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 isConnectedclient.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) { ListClientInfo 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 (data1234) { 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 (data1234) { 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) { ListClientInfo ClientList server.GetAllConnectedClients(); server.SendToClient(ClientList[0], 拍照完成); } } }); }