C# SerialPort串口接收 丢数据 数据不完整的解决方法

时间:2022-11-10 20:06:22

C# SerialPort的 DataReceived事件,可能是存在问题,使用时,数据丢失,造成数据不完整。

解决方法思路:

使用独立线程读取数据,把串口缓冲区的数据,读取到程序中。抛开DataReceived事件。

使用其它线程获取“串口中读取的数据”再进行解析。

客户程序由被动触发,改为主动获取。

 

相关变量、对象

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 
/// <summary>
/// 保持读取开关
/// </summary>
bool  _keepReading;

/// <summary>
/// 检测频率【检测等待时间,毫秒】【按行读取,可以不用】
/// </summary>
int  _jcpl =  1 ;

/// <summary>
/// 串口
/// </summary>
SerialPort _sp =  new  SerialPort();

/// <summary>
/// 字符串队列【.NET Framework 4.0以上】
/// </summary>
ConcurrentQueue< string > _cq =  new  ConcurrentQueue< string >();

/// <summary>
/// 字节数据队列
/// </summary>
ConcurrentQueue< byte []> _cqBZ =  new  ConcurrentQueue< byte []>();

 

串口读取方法【ViewCommStatus,自定义方法,串口读取状态输出】

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 
/// <summary>
/// 串口读取方法
/// </summary>
void  SerialPortRead()
{
    
while  (_keepReading)
    {
        
if  (_jcpl >  0 ) //按行读取可以省略
        {
            Thread.Sleep(_jcpl);
        }
        
if  (_sp ==  null )
        {
            ViewCommStatus(
false "串口对象未实例化!" );
            Thread.Sleep(
3000 ); //3秒后重新检测
             continue ;
        }
        
if  (!_sp.IsOpen)
        {
            ViewCommStatus(
false "串口未打开" );
            Thread.Sleep(
3000 );
            
continue ;
        }

        
try
        {
            
#region  按行读取
            
string  buffer = _sp.ReadLine();
            
if  (! string .IsNullOrEmpty(buffer)) //可以不加判断,允许添加null值,数据解析时,再做判断。
            {
                _cq.Enqueue(buffer);
            }
            
#endregion

            
//#region 字节读取
             //byte[] readBuffer = new byte[_sp.ReadBufferSize + 1];
             //int count = _sp.Read(readBuffer, 0, _sp.ReadBufferSize);
             //if (count != 0)
             //{
             //    _cqBZ.Enqueue(readBuffer);
             //}
             //#endregion

            ViewCommStatus(
true "串口通信正常" );
        }
        
catch  (TimeoutException) //注意超时时间的定义
        {
            ViewCommStatus(
false "串口读取超时!" );
        }
        
catch  (Exception ex) //排除隐患后可以去掉。
        {
            ViewCommStatus(
false "串口读取异常:"  + ex.Message);
        }

    }
}

  

 数据解析方法

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 
/// <summary>
/// 数据解析
/// </summary>
void  DataAnalysis()
{
    
while  ( true )
    {
        Thread.Sleep(
10 );

        
if  (_cq.IsEmpty)
        {
            
continue ;
        }

        
#region  按行读取
        
for  ( int  i =  0 ; i < _cq.Count; i++)
        {
            
string  strTmp =  "" ;
            _cq.TryDequeue(
out  strTmp);
            
//解析strTmp
             //解析strTmp
             //解析strTmp
        }
        
#endregion

        
#region  按字节读取
        
//StringBuilder sb = new StringBuilder();
         //for (int i = 0; i < _cq.Count; i++)
         //{
         //    byte[] bt = null;
         //    _cqBZ.TryDequeue(out bt);
         //    string strTmp = System.Text.Encoding.ASCII.GetString(bt);
         //    sb.Append(strTmp);
         //}
         ////解析sb
         ////解析sb
         ////解析sb
         #endregion
    }

}

 

调用示例

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
 
void  StartRead()
{
    _keepReading = 
true ;

    Thread trdRead = 
new  Thread(SerialPortRead);
    trdRead.Start();


    Thread trdDataAnalysis = 
new  Thread(DataAnalysis);
    trdDataAnalysis.Start();
}

 

SerialPort的参数配置和资源释放,就先不写了。这个一般情况下都会。