C#串口发送图片问题, sp_DataReceived接收函数运行一次后接着就自动触发再次运行,并在读取时卡死

.Net技术 码拜 8年前 (2016-05-25) 1410次浏览
 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方法的时间间隔

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

引用:
 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();//丢弃接收缓冲区数据
            }));
        }

这种编程设计的逻辑就不对。
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

引用:

谢谢你的建议。但是在没有那个一秒延时,以及read这些语句的时候,本人描述的问题还会发生,即sp_DataReceived函数会自行再次运行。所以本人判断与发送部分有关,但拔点串口上发送与接收的连接线时,发送函数能正常工作,本人不清楚是什么问题

有的人头脑疯狂,总是认为“山不走过来,就是山的毛病”。事物的规律都是相对的,辩证的。你以为的理由,让你顶多只能达到一半技术。

5

引用:

在用断点调试的时候,发现其实第一次运行该方法时是正常的,但他会再次自行触发运行,运行到read时,原因是没有数据发送,它卡死在这,整个界面也卡死。至于数据在监控处可以看到第一次运行时可以全部收到

该不会你掉用过2次SetPortProperty吧


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明C#串口发送图片问题, sp_DataReceived接收函数运行一次后接着就自动触发再次运行,并在读取时卡死
喜欢 (0)
[1034331897@qq.com]
分享 (0)