前几期公众号( 上一期
)中有介绍如何使用ESP32_Cam获取图像利用TCP协议上传,之后利用利用Python解析收到的代码并且显示出来。
那么本期我们在之前的C#软件大合集中添加TCP服务器端并完成在C#中的图像显示的功能。 源码会分享在交流群中, QQ群号:656210280
对嵌入式/编程感兴趣的小伙伴可以加一下QQ群哈~~ 在上一期说过,由于HTTP协议需要等待响应,因此可以做到非常好的数据传输校验。
而TCP协议不需要服务器响应,因此虽然在数据传输的可靠性上得到了下降,但是在数据传输的速度上却得到了质的飞跃。 ** 界面布局 ** ** **
首先简简单单地做个界面,右边是选择地址和端口的界面,左边是分开文本接收模式和图片显示模式。
有一个Comombox控件方便我们选择IP地址,由端口号Textbox来选择我们的端口号,需要注意的是通常端口都是开放的,如果我们遇到了打开端口报错的问题,我们需要及时调节我们的端口或者开启管理员模式。
虽然说是1024及以下的低序列端口才需要开启管理员模式才能打开,但是在实际的使用中发现如果需要使用端口还是需要打开管理员模式的。
IPV6地址由四组组成,我们获得的地址包括我们的公网地址以及映射地址。 值得一提的事,如果设置地址是0.0.0.0即默认监听所有可用地址。
private void LoadLocalIPv4Addresses() { try { // 获取本地主机名 string hostName = Dns.GetHostName(); // 获取主机名对应的IP地址列表 IPAddress[] ipAddresses = Dns.GetHostAddresses(hostName); // 筛选IPv4地址并添加到ComboBox foreach (IPAddress ipAddress in ipAddresses.Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)) { IPAddressChoose.Items.Add(ipAddress.ToString()); }
// 设置默认选择第一个IPv4地址 if (IPAddressChoose.Items.Count > 0) { IPAddressChoose.SelectedIndex = 0; } } catch (Exception ex) { MessageBox.Show($"错误:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
首先是加载的时候筛选出本机可以用的IP地址,把IP地址加入我们的控件中,方便我们使用时候的选择。 ** 主要代码 ** 之后编写创建监听的代码。
创建监听的步骤主要是建立一个TCP监听句柄,之后使用异步的方法创建一个回调函数来处理我们接收到的图像。
需要调节关闭时候的任务状态,防止调用过程中关闭监听,导致监听继续从而发生代码中断。
private void Connect_Click(object sender, EventArgs e){ if (Connect.Text == "创建监听") { DisableAll(); StartListening(); Connect.Text = "结束监听"; } else { EnableAll(); StopListening(); Connect.Text = "创建监听"; }}
创建监听的按钮按下后首先是禁用其他控件,之后是创建监听。
private void StartListening() { try { // 获取选定的IP地址和端口 IPAddress ipAddress = IPAddress.Parse(IPAddressChoose.SelectedItem.ToString()); int port = int.Parse(PortChoose.Text);
// 启动TCP监听 tcpListener = new TcpListener(ipAddress, port); tcpListener.Start(); cancellationTokenSource = new CancellationTokenSource(); isListening = true; //开辟线程实现异步监听 listenerThread = new Thread(() => ListenForImages(cancellationTokenSource.Token)); listenerThread.Start(); } catch (Exception ex) { MessageBox.Show($"错误:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void StopListening() { // 停止TCP监听 isListening = false; if (cancellationTokenSource != null) { cancellationTokenSource.Cancel();//取消进程防止出错 cancellationTokenSource.Dispose(); cancellationTokenSource = null; }
if (tcpListener != null) { tcpListener.Stop(); } }
创建监听的时候,我们根据所选择的IP地址和端口开启TCP监听,并且使用一个isListening标志位来判断是否处于监听状态。
需要注意的是这里的cancellationTokenSource.Dispose()是不可少的,如果缺少这行代码就会导致异步程序进行会导致程序错误。
并且开始一个线程来实现异步监听。
private void ListenForImages(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { using (TcpClient tcpClient = tcpListener.AcceptTcpClient()) { using (NetworkStream networkStream = tcpClient.GetStream()) { using (MemoryStream memoryStream = new MemoryStream()) { byte[] buffer = new byte[4096]; int bytesRead;
while ((bytesRead = networkStream.Read(buffer, 0, buffer.Length)) > 0) { memoryStream.Write(buffer, 0, bytesRead); }
// 处理和显示接收到的图像 Bitmap receivedImage = new Bitmap(memoryStream);
// 调用方法在UI线程上显示图像 DisplayImage(receivedImage); } }
// 关闭TcpClient tcpClient.Close(); } } } catch (Exception ex) { Console.WriteLine($"错误:{ex.Message}"); } }
// 在UI线程上显示图像的方法 private void DisplayImage(Bitmap image) { if (InvokeRequired) { Invoke(new Action(() => DisplayImage(image))); } else { pictureBox.Image = image; } }
之后分别是异步监听的函数,将我们收到的TCP数据转化为图片信息进行保存,并且把图片显示到PictureBox控件上。
需要注意的是,在异步监听的函数中,我们使用Console.WriteLine来打印错误信息,防止阻塞UI进程。 ** 效果展示 ** ** **