sp_DataReceived函数是当接收缓冲区有元素时触发,本人使用串口自发自收,接收到图片后界面卡死。使用断点追踪运行过程发现在 sp_DataReceived函数触发运行一次后,又自动运行第二次,在读取函数 sp.Read(Recieve_bytes, 0, Recieve_bytes.Length)处卡死。尝试发送本人定义的数组时不会出现该问题,只有发送被转为byte[ ]的image时才会这样。发送和接收缓冲区使用后均已丢弃。下面是串口定义,图片发送和接收的部分源码,希望高手帮看看,分析一下原因。
这是串口设置:
这是串口设置:
private void SetPortProperty()//设置串口的属性 { sp = new SerialPort(); sp.PortName = Serial_Number.Text.Trim();//设置串口名 sp.BaudRate = Convert.ToInt32(Baud_Rate.Text.Trim());//设置波特率,将字符型转换为整型 sp.StopBits = StopBits.One;//设置停止位为1 sp.DataBits = 8;//设置数据位为8位 sp.Parity = Parity.None;//设置为无奇偶校验位 sp.ReadTimeout = -1;//设置超时读取时间 sp.RtsEnable = false;//启用请求发送(RTS)信号 //定义DataReceived事件,当串口收到数据后触发事件 sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived); sp.Encoding = System.Text.Encoding.GetEncoding("GB2312"); //sp.ReceivedBytesThreshold = 7000; sp.WriteBufferSize = 10000; sp.ReadBufferSize = 10000; }
这是保存摄像头采集的图片,并打开再发送:
private void Take_photo_Click(object sender, EventArgs e) { try { if (videoSourcePlayer1.IsRunning) { //Bitmap source = videoSourcePlayer1.GetCurrentVideoFrame(); BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( videoSourcePlayer1.GetCurrentVideoFrame().GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); JpegBitmapEncoder pE = new JpegBitmapEncoder(); pE.Frames.Add(BitmapFrame.Create(bitmapSource)); string picName = GetImagePath() + "\" + "photo" + ".jpeg"; while (File.Exists(picName)) { File.Delete(picName); } using (Stream stream = File.Create(picName)) { pE.Save(stream); } System.Drawing.Image image = System.Drawing.Image.FromFile(picName); MemoryStream ms = new MemoryStream(); image.Save(ms, ImageFormat.Jpeg); ms.Flush(); ms.Seek(0, SeekOrigin.Begin); byte[] Source_bytes = new byte[ms.Length]; data_lenth = Source_bytes.Length; ms.Read(Source_bytes, 0, (int)ms.Length);//这里已经转成了字节 ms.Close(); ms.Dispose(); image.Dispose();//!占用的系统资源使用后一定要释放! if (isOpen) { sp.Write(Source_bytes,0, Source_bytes.Length); sp.DiscardOutBuffer(); } else { MessageBox.Show("串口未打开!", "错误提示"); return; } } else { MessageBox.Show("摄像头未打开!"); } } catch (Exception ex) { MessageBox.Show("摄像头异常:" + ex.Message); } }
这是串口接收:
private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) { System.Threading.Thread.Sleep(1000);//延时10ms等待接收完数据 //this.Invoke就是跨线程访问ui的方法,也是本文的范例 this.Invoke((EventHandler)(delegate { //The_Recive_Data.Text += sp.ReadLine(); byte[] Recieve_bytes = new byte[data_lenth]; sp.Read(Recieve_bytes, 0, Recieve_bytes.Length); MemoryStream ms = new MemoryStream(Recieve_bytes); try { System.Drawing.Image recieveImage = System.Drawing.Image.FromStream(ms); ms.Flush(); ms.Close(); pictureBox1.Image = recieveImage; //recieveImage.Dispose(); } catch (ArgumentException) { MessageBox.Show("数据流为空!"); } sp.DiscardInBuffer();//丢弃接收缓冲区数据 })); }
解决方案
5
不知道你的接收和发送,是不是在2个线程里执行
System.Threading.Thread.Sleep(1000);//延时10ms等待接收完数据
这个是调用方法后,延迟执行下面代码,要增加两次调用sp_DataReceived方法的时间间隔
System.Threading.Thread.Sleep(1000);//延时10ms等待接收完数据
这个是调用方法后,延迟执行下面代码,要增加两次调用sp_DataReceived方法的时间间隔
10
还有,在执行sp.Read(Recieve_bytes, 0, Recieve_bytes.Length);前
应该判断发送的图片是不是全部发完,而不是一部分
应该判断发送的图片是不是全部发完,而不是一部分
10
不该用Invoke的时候就不要用
10
是不是原因是你读的数据长度不够data_lenth,最好用SerialPort.BytesToRead来定义byte[]大小,后面加个结束验证,收到结束验证后再处理
10
首选弄清楚为什么会自动再次执行(假如不是你预期的话)
另外,本人想说的是,相似问题,不论是发送图片还是接收图片,需要有“处理反馈”,告诉程序,本人发送完了,或本人接收完了,这样程序才够健壮。本人给你写了一个例子,是在主线程下开辟一个次线程,并利用委托来实现
另外,本人想说的是,相似问题,不论是发送图片还是接收图片,需要有“处理反馈”,告诉程序,本人发送完了,或本人接收完了,这样程序才够健壮。本人给你写了一个例子,是在主线程下开辟一个次线程,并利用委托来实现
public delegate void myDel(); protected void myMethod() //模拟接收 { //接收图片的代码 } protected void Completed(IAsyncResult Iasy) //回调函数,当接收完图片后,次线程会主动调用 { //告诉主线程,图片接收完了 } //主线程调用 myDel d = new myDel(myMethod); d.BeginInvoke(new AsyncCallback(Completed), null); //异步调用 //Thread.Sleep(1000);
你根据本人的例子,试着改写你的发送,接收,很简单的
30
这种编程设计的逻辑就不对。
Sleep 的问题已经有人说过了,本人就不多说。你之所以故意阻塞程序执行,这是错上加错,把问题搞复杂了。正常的程序是根本不应该有任何延时的。
接收数据时,你并不能保证恰好一个图片放到缓冲区里了之后 .net 才触发 sp_DataReceived(这也是你“不知死活”硬要去故意阻塞的原因),那么从缓冲区里Read移出来的信息应该放到一个 List<byte> 或 MemoryStream 中累积,直到你获得一个消息的结束标记,才应该启动你的 Invoke 来从这个累积缓冲区中取出图片(但是剩下的 bytes 应该继续保存)。
因此,Sleep(1000) 语句应该删除,sp.Read 语句应该放到 BeginInvoke 之前(并且把 Recieve_bytes 累积起来),应该在判断到累积数据中含有完整消息时才应该使用系统线程池(或 new Thread 对象)在子线程中去处理数据,此时
sp_DataReceived 应该立刻结束(释放 I/O 线程继续进行信息接收)。在这个新的子线程处理中,才需要调用 Invoke/BeginInvoke。
基本上,你的这段程序包括了多个低效、错误的编程方式,是个比较差的代码!
而关键要穴,其实很简单,就是你要删除 Sleep 语句。删除它,你就逼着本人正常地编程。不删除它,找各种理由继续陷在误区中。
20
有的人头脑疯狂,总是认为“山不走过来,就是山的毛病”。事物的规律都是相对的,辩证的。你以为的理由,让你顶多只能达到一半技术。
5
该不会你掉用过2次SetPortProperty吧