用DataReceived方法接收数据时有时候会丢失,尤其是在debug的时候,有时候甚至多定义几个变量,数据接收也会出现丢失,有什么办法可以解决吗?

解决方案 »

  1.   

    是使用using System.IO.Ports; 这个包吗?那使用ReadLine() 或者Read()方法看看?
      

  2.   

    用sscom3.2丁丁串口调试工具接收的数据正常,发送端应该没有问题,主要是接收时候数据丢失,
      

  3.   

    是这个包。现在使用的就是Read()方法,用ReadLine() 直接timeout了
      

  4.   

    这个DataReceived方法不是只要接收的缓冲区有数据就会调用的嘛?我尝试用timer控件定时读缓冲区的数据,这个也会数据丢失
      

  5.   

    猜测原因是:接收到数据后,会把数据进行处理并进行下一步的操作A,可能A还没有执行完,下位机又传回数据,所以数据在PC上没有接收到。这个会有可能不?
      

  6.   

    你是不是没加回车换行啊,不然readline怎么会超时。
      

  7.   

    comm.NewLine = "/r/n";  已加了
      

  8.   

    在data_received事件里read后解析buf里的数据,如果没解析完,放到另外一个缓冲区,等下次data_received事件里先处理,这样就可以防止丢包
      

  9.   

     _serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);用委托,有数据就触发事件.
      

  10.   

    如果是二进制,最好用流或buffer来操作。 while (i > 0)
                        {
                            int readBufCount = SerialPortClient.BytesToRead;
                            if (readBufCount > 8)
                            {
                                m_autoPortEvent.WaitOne(40, false);
                                readBufCount = SerialPortClient.BytesToRead;
                                recvData = new byte[readBufCount];
                                SerialPortClient.Read(recvData, 0, recvData.Length);
                                return recvData;
                            }
                            i--;
                        }
                        if (recvData == null)
                        {
                            throw new Exception("time out for getData");
                        }
      

  11.   

    每次读之前先获取长度,串口这个比SOCKET好用得多了,获取好了之后,判断撒,不够再获取,直到收完为止。
      

  12.   

    中间一定要sleep下,我用的wait效果差不多。
      

  13.   

    http://bbs.csdn.net/topics/390316188认真看了下,但我的问题貌似解决不了~
    ①你所说的建一个缓冲区,我是用一个文本框接收的这样应该不会有问题吧?;②接收到的数据不用单独解析的,目前是把接收到的字节数组转化字符串后保存在文本框,用timer控件去读取文本框中的值,如含有某一段字符串则进行下一步操作(一共会发送5次指令,字节数组长度是几个到几十万个)
      

  14.   

    谢谢你的建议,其中返回的应该是ascii码
      

  15.   

     void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {  
               try
                {   
                    if (comm != null)
                    {
                        int n = comm.BytesToRead;
                        //comm.ReadTimeout = 1000;
                       
                        if (n != 0)
                        {
                            byte[] buf = new byte[n];
                            comm.Read(buf, 0, n); 
                                 builder.Clear();  
                                 this.Invoke((EventHandler)(delegate
                            {
                                //直接按ASCII规则转换成字符串  
                                      builder.AppendLine(Encoding.ASCII.GetString(buf));                            receivedString = builder.ToString().Replace("\r\n", "");
                                //MessageBox.Show(receivedString+"接收字节"+n.ToString());
                                this.receivedMessage.AppendText(receivedString);//接收的string放到文本框里,然后用timer定时去读取
                            }));
                        }
                        
                       
                    }
                    else
                    {
                        MessageBox.Show("串口连接失败,请重新连接!");
                    }
                }
                catch(Exception exp)
                {
                    MessageBox.Show(exp.Message);
                }
            }
    这是接收数据时进行的处理,帮忙看看有什么问题不
      

  16.   

    不管是ASCII还是UTF8
    用BUFFER去取都能返回。最后就是多了个Encoding.ASCII.GetString罢了。
      

  17.   

    this.Invoke这里就会有问题了,如果你速度快了,这里会产生堆积,卡死在这里,也不报错。
      

  18.   

    SerialPort.ReceivedBytesThreshold 触发条件。
      

  19.   

    http://bbs.csdn.net/topics/390316188认真看了下,但我的问题貌似解决不了~
    ①你所说的建一个缓冲区,我是用一个文本框接收的这样应该不会有问题吧?;②接收到的数据不用单独解析的,目前是把接收到的字节数组转化字符串后保存在文本框,用timer控件去读取文本框中的值,如含有某一段字符串则进行下一步操作(一共会发送5次指令,字节数组长度是几个到几十万个)如果只是接收以单行文本就随便用ReadLine即可 如果要以字节接收并且所接收的要指定接收协议就要用到缓冲。其他不多说了。你自己理解,这东西跟Socket接收字节没什么区别
      

  20.   

    1、可以,全局变量的缓存,可以写成一个类,实现数据入和出的操作,建议循环数组实现!!!(查找生产者和消费者问题MSDN)
    2、触发门限为1时,有一个字节到达,马上触发接收,这个时候串口接收缓存中很可能是1个字节(2个,3个也有可能?),你这个时候接收就可能是1各字节然后,委托进入主线程处理,接收这块就卡了--。关于这个问题,你仅仅把接收门限改为30,应该就是30个字节,你可以试试?
    最后
    1、关于串口接收这个问题,用我上面的思路绝对没有问题,对于串行字节流,这个方法可以一直接收,门限要调
    2、如果上位机发送的一帧帧的数据,也可以将这数据入缓存,后续数据帧处理时,在从中提取数据帧。
    代码我就发提供了,相信你可以解决
      

  21.   


    第一次PC传个字符串a去下位机,下位机返回一个a,也就一个字节,接收到a说明通信成功再进行下面的操作,如果门限不是1,这样是接收不到数据的
      

  22.   

    PC与下位机之间的通信都有协议的,是你没看懂他协议还是啥的。貌似是基于xmodem协议的,串口通信不是只负责收发就可以了吗?只要收到想要的数据,再进行下一步的操作
      

  23.   

    PC与下位机之间的通信都有协议的,是你没看懂他协议还是啥的。貌似是基于xmodem协议的,串口通信不是只负责收发就可以了吗?只要收到想要的数据,再进行下一步的操作
    ..我说的这个不是你个xmodem 我意思是指。PC与下位机之间数据收发协议
    好比你在PC发一个 0x10 0x02 0x00 0x10这样,然后下位机会返回 0x10 0x0A 这样的
    就是双方商量好了。我发给你啥指令,你会返回我啥状态。还有 异步操作不会影响数据完整性,是你自己 程序写的不对。
      

  24.   

    PC与下位机之间的通信都有协议的,是你没看懂他协议还是啥的。貌似是基于xmodem协议的,串口通信不是只负责收发就可以了吗?只要收到想要的数据,再进行下一步的操作
    ..我说的这个不是你个xmodem 我意思是指。PC与下位机之间数据收发协议
    好比你在PC发一个 0x10 0x02 0x00 0x10这样,然后下位机会返回 0x10 0x0A 这样的
    就是双方商量好了。我发给你啥指令,你会返回我啥状态。还有 异步操作不会影响数据完整性,是你自己 程序写的不对。我的思路是这样的麻烦帮忙分析一下:PC传数据到下位机,用DataReceived事件接收数据,接收到数据后转化字符串放到文本框,用timer控件去监听文本框里的数据,再进行处理
      

  25.   

    现在程序正常运行时没有问题,但调试或者在DataReceived事件中加sleep,数据就不完整了,传输的比特率是115200
      

  26.   

    PC与下位机之间的通信都有协议的,是你没看懂他协议还是啥的。貌似是基于xmodem协议的,串口通信不是只负责收发就可以了吗?只要收到想要的数据,再进行下一步的操作
    ..我说的这个不是你个xmodem 我意思是指。PC与下位机之间数据收发协议
    好比你在PC发一个 0x10 0x02 0x00 0x10这样,然后下位机会返回 0x10 0x0A 这样的
    就是双方商量好了。我发给你啥指令,你会返回我啥状态。还有 异步操作不会影响数据完整性,是你自己 程序写的不对。我的思路是这样的麻烦帮忙分析一下:PC传数据到下位机,用DataReceived事件接收数据,接收到数据后转化字符串放到文本框,用timer控件去监听文本框里的数据,再进行处理
    思路就错了,还转化字符到文本框是要放到缓冲区去。还有没必要TIME去监听。利用DataReceived就可以了之前不是给你连接了缓冲区,所以你要搞清楚协议是什么,你会收到什么样的数据包。
      

  27.   

    PC与下位机之间的通信都有协议的,是你没看懂他协议还是啥的。貌似是基于xmodem协议的,串口通信不是只负责收发就可以了吗?只要收到想要的数据,再进行下一步的操作
    ..我说的这个不是你个xmodem 我意思是指。PC与下位机之间数据收发协议
    好比你在PC发一个 0x10 0x02 0x00 0x10这样,然后下位机会返回 0x10 0x0A 这样的
    就是双方商量好了。我发给你啥指令,你会返回我啥状态。还有 异步操作不会影响数据完整性,是你自己 程序写的不对。我的思路是这样的麻烦帮忙分析一下:PC传数据到下位机,用DataReceived事件接收数据,接收到数据后转化字符串放到文本框,用timer控件去监听文本框里的数据,再进行处理
    思路就错了,还转化字符到文本框是要放到缓冲区去。还有没必要TIME去监听。利用DataReceived就可以了之前不是给你连接了缓冲区,所以你要搞清楚协议是什么,你会收到什么样的数据包。恩,你给的那个缓冲区看了,目前只知道基于什么协议(也是根据网上资料猜测的),返回什么就不清楚了
      

  28.   

    PC与下位机之间的通信都有协议的,是你没看懂他协议还是啥的。貌似是基于xmodem协议的,串口通信不是只负责收发就可以了吗?只要收到想要的数据,再进行下一步的操作
    ..我说的这个不是你个xmodem 我意思是指。PC与下位机之间数据收发协议
    好比你在PC发一个 0x10 0x02 0x00 0x10这样,然后下位机会返回 0x10 0x0A 这样的
    就是双方商量好了。我发给你啥指令,你会返回我啥状态。还有 异步操作不会影响数据完整性,是你自己 程序写的不对。我的思路是这样的麻烦帮忙分析一下:PC传数据到下位机,用DataReceived事件接收数据,接收到数据后转化字符串放到文本框,用timer控件去监听文本框里的数据,再进行处理
    思路就错了,还转化字符到文本框是要放到缓冲区去。还有没必要TIME去监听。利用DataReceived就可以了之前不是给你连接了缓冲区,所以你要搞清楚协议是什么,你会收到什么样的数据包。恩,你给的那个缓冲区看了,目前只知道基于什么协议(也是根据网上资料猜测的),返回什么就不清楚了
    那你去找给那个连接的那个楼主的妹子,他是搞懂了。
      

  29.   

    PC与下位机之间的通信都有协议的,是你没看懂他协议还是啥的。貌似是基于xmodem协议的,串口通信不是只负责收发就可以了吗?只要收到想要的数据,再进行下一步的操作
    ..我说的这个不是你个xmodem 我意思是指。PC与下位机之间数据收发协议
    好比你在PC发一个 0x10 0x02 0x00 0x10这样,然后下位机会返回 0x10 0x0A 这样的
    就是双方商量好了。我发给你啥指令,你会返回我啥状态。还有 异步操作不会影响数据完整性,是你自己 程序写的不对。我的思路是这样的麻烦帮忙分析一下:PC传数据到下位机,用DataReceived事件接收数据,接收到数据后转化字符串放到文本框,用timer控件去监听文本框里的数据,再进行处理
    思路就错了,还转化字符到文本框是要放到缓冲区去。还有没必要TIME去监听。利用DataReceived就可以了之前不是给你连接了缓冲区,所以你要搞清楚协议是什么,你会收到什么样的数据包。恩,你给的那个缓冲区看了,目前只知道基于什么协议(也是根据网上资料猜测的),返回什么就不清楚了
    那你去找给那个连接的那个楼主的妹子,他是搞懂了。有一点很奇怪的,我用丁丁串口调试工具是没有问题,既然这个工具是通用,和什么协议没有关系吧?
      

  30.   

    好吧,这不是协议的问题!
    关于你这个具体问题,我给你个建议:1:接收门限设为 1 ,建立缓存,接收的函数只负责接收,这里门限为1应该还是不会卡!!!,只是每次读取数据的时候,要读取串口接收缓存中已有数据,也就是你的代码那句!!!
    2、处理线程里面,可以进行判断,是指定的指令,则对串口写回应指令。
    3、文本框仅仅用来show信息,不要将其当作“缓存”来用,ok?如果还不清楚,我上代码!!!!
      

  31.   

    另外,你DEBUG的时候,如果有断点,那很可能是会接收不到完整数据的,因为你程序“卡”在了断点,而上位机数据可能源源不断的来袭!!!
    这个情况要看你们自己定义的通信协议,也就是你说的上位机来个指令,下位机回复,然后上位如何来数据?是源源不断的大量数据,还是依旧这样你来我往?
    具体情况,要具体分析吧
      

  32.   

    看了下协议(xmodem)应该是一来一往的,上位机传一些指令,下位机接收后处理,处理后返回数据到上位机,PS:有人说协议和串口没有关系,只要把数据流传过去,下位机根据已定义协议的直接解析。
      

  33.   


         public class Buffer
        {
             const int Size  =4096;//缓存大小
             byte[] buffer = new byte[Size];         int InPtr = 0; //当前数据入缓存的起始位置
             int OutPtr = 0;     //当前缓存取数据起始位置(也就是未处理的数据起始位置)
             int dataLength = 0; //当前缓存中数据长度
             int dataSpace = Size; //当前缓存还可存入的数据         public void DataIn(byte[] data)
             {
                 if (data.Length <= dataSpace)
                 {
                     for (int i = 0; i < data.Length; i++)
                     {
                         buffer[InPtr] = data[i];                     
                     }
                     InPtr = (InPtr + data.Length) % Size;
                     dataLength = (dataLength + data.Length);
                     dataSpace = dataSpace - data.Length;
     
                 }
             }
             public byte[] DataOut(int Length)
             {
                 if (Length <= dataLength && Length>0)
                 {
                     byte[] data = new byte[Length];
                     for (int i = 0; i < Length; i++)
                     {
                         data[i] = buffer[(OutPtr + i) % Size];
                     }
                     OutPtr = (OutPtr + Length) % Size;
                     dataLength = (dataLength -Length);
                     dataSpace = dataSpace+Length;
                 }
             }
        }
    我把缓存类的大体结构代码给你,我自己的是泛型,并且有些乱七八糟的事件等之类,你这里没有必要,只需处理字节串
      

  34.   

    非常感谢~~~~~~~~~
    还有个疑惑①这样放在缓冲区是不是效率也比较低(还没有实验,下位机被人拿走了。。),因为传输的比特率比较大115200;②用一个timer控件可以不?(线程不太会);③串口的缓冲区size最大4096吗?
      

  35.   


    1、这个缓存的大小由你设定!根据实际情况,你可以把缓存大小设为100字节或更小。
    2、效率低在那个地方?我不明白你问的!!建立一个线程,监听这个缓存,缓存的dataLength>0,即处理,基本上,缓存中一有数据进入,该线程就取出数据进行处理。
    没必要用TIMER,好吧,我把代码都放上来,你把分给我吧
      

  36.   


    1、这个缓存的大小由你设定!根据实际情况,你可以把缓存大小设为100字节或更小。
    2、效率低在那个地方?我不明白你问的!!建立一个线程,监听这个缓存,缓存的dataLength>0,即处理,基本上,缓存中一有数据进入,该线程就取出数据进行处理。
    没必要用TIMER,好吧,我把代码都放上来,你把分给我吧效率低,是一次接收字节比较多(4000多吧)的时候,用DataIn()方法接收会不会要时间很多,这样又会出现数据丢失吧
      

  37.   

    我用这个思路,串口完全没问题,网络数据也丝毫不惧!!!,比如,网络数据包,一秒10包,一包1400个字节,就这样弄完全没问题,比你串口数据量大多了吧!!,你在全局声明一个缓存类变量,你把串口接收的输入直接调用buffer.DataIn();数据就进这个缓存了。然后接收这块就OK。
    缓存监听这个可以这样写,在主界面下面建立一个线程,线程的执行函数类似这样 bool RequestStop = false;
            int L = 10;
            void Process()
            {
                while (!RequestStop)
                {
                    while (buffer.dataLength > 0)
                    {
                        byte[] data = buffer.DataOut(L );
                        DoWork(data);//处理数据,并做想要做的事情!!
                    }
                }
            }这样一来,你串口的接收是没有问题了,因为串口的接收不会“卡”,但是这样会有另外一个问题,那就是在这个线程里面如果操作主界面的文本框呢?
    对,你很聪敏,你可以用委托,也就是你自己最上面串口接收那块处理的代码块!
    当然还有另外一个思路:事件委托,利用事件参数传递数据,这,不属于这个范围了。
      

  38.   

    忘了说,上面,设计的一个bool变量用于控制线程是否退出。
    另一个L,则要求你根据自己实际需求,或者你们自己的通信协议进行设定。再说一边,这么做的总体思路是:
    1、接收数据的函数,只负责将接收到的数据入缓存。
    2、在处理线程中,每当缓存中有数据,即可以开始想要的处理,这个想要的处理,得根据你的实际需求,比如,
           a、你串口上位机来一个指令,那么,你根据自己的协议,在这个缓存中去匹配这个指令,根据指令,再回发什么数据
           b、如果上位发来的是一帧帧的数据,那么你可以在这个缓存中去提取帧
    总之,你接收到的串口数据都在这个缓存变量中了,怎么处理,你看着办好了。
    这个缓存是个循环数组,来的数据一直往里面写,写到尾巴又自动回到开始
    处理线程一直从这个缓存取数据。你可以想象下,这个缓存,就好比两个人在圆形赛道上跑步,后面的人(OutPtr)一直追赶前面的人(InPtr),前面的人一直往前跑。如果后面的人贴着前面的人了,他就停止跑步!!
    出现问题的可能就在于,来的数据特别快特别多,也就是前面跑的超了后面这个人一圈,那么这就会出现数据处理不过来了,这种情况在串口这种数据量下,是不可能的!!!
    在大数据量下,如果出现这情况,那表示,你可以加大缓存,如果缓存很大还不性,那你该换CPU 了,我唉没遇到这样的情况!
      

  39.   


    1、这个缓存的大小由你设定!根据实际情况,你可以把缓存大小设为100字节或更小。
    2、效率低在那个地方?我不明白你问的!!建立一个线程,监听这个缓存,缓存的dataLength>0,即处理,基本上,缓存中一有数据进入,该线程就取出数据进行处理。
    没必要用TIMER,好吧,我把代码都放上来,你把分给我吧效率低,是一次接收字节比较多(4000多吧)的时候,用DataIn()方法接收会不会要时间很多,这样又会出现数据丢失吧
    无语,一次接收4000多?你触发门限设为1,有数据一来,就会接收!!!!再多的字节,也是分若干次接收,好不?串口自己也是有缓存的!!
    比如,上位机一下子发10000字节,这10000个字节也是一个个字节来到下位机的好么?下位机一发现到来数据到达接收门限,即触发读串口缓存数据。这个DataIn你可以优化,但对于你这样的串口数据量,完全没压力好么?
    我已经说了网络的每秒14000字节数据,它都没问题好不?
    别跟我扯犊子了,你有疑问很好,BUT,你先明白我说的,然后去尝试后,再发言好么?如果到现在你还不明白按我这思路怎么做,我只能无语。
    学习,不是你这样的,上面那么多人提出缓存的解决思路,你有听进去?
    你的分数,我也不要了,你慢慢慢慢琢磨了,我懒得回复了!
      

  40.   

    求LZ的程序  很急!![email protected]