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吧