为您推荐:
精华内容
最热下载
问答
  • 5星
    5.25MB weixin_43096877 2021-05-29 15:37:02
  • 5星
    680KB zb12138 2021-03-15 16:34:59
  • 5星
    43KB ZCY5202015 2021-04-22 14:26:38
  • 5星
    317.93MB qq_41740659 2021-07-09 11:12:04
  • 5星
    21KB ZCY5202015 2021-04-22 14:21:09
  • 5星
    8.29MB qq_44526422 2021-02-19 17:01:30
  • 5星
    65.2MB guoruibin123 2021-05-11 06:06:48
  • 5星
    5.22MB GJZGRB 2021-03-10 14:25:58
  • 5星
    82.7MB Roger_717 2021-07-29 14:56:18
  • 5星
    63KB weixin_42696333 2021-09-10 15:29:31
  • 4星
    172KB guanking 2013-05-29 11:03:17
  • 1.82MB liuwan66 2018-01-23 14:44:16
  • 413KB lyxass 2020-02-21 09:43:08
  • 4星
    44KB ilikehigame 2014-11-01 11:20:22
  • 4星
    226KB kijl000 2012-12-14 11:56:04
  • 4星
    449KB benq_leo 2012-11-22 13:44:15
  • 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有...

    第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有一半的人真的解决了。另外一半的人就睁只眼闭只眼阿弥佗佛希望不要在客户那里出现这问题了。

    你看到我的文章,就放心吧,这问题有救了。我们先回顾一下上一篇中的代码

    voidcomm_DataReceived(objectsender, SerialDataReceivedEventArgs e)

    {

    //先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

    intn = comm.BytesToRead;

    //声明一个临时数组存储当前来的串口数据

    byte[] buf =newbyte[n];

    //增加接收计数

    received_count += n;

    //读取缓冲数据

    comm.Read(buf, 0, n);

    //清除字符串构造器的内容

    builder.Clear();

    //因为要访问ui资源,所以需要使用invoke方式同步ui。

    this.Invoke((EventHandler)(delegate{...界面更新,略}));

    }

    privatevoidbuttonOpenClose_Click(objectsender, EventArgs e)

    {

    //根据当前串口对象,来判断操作

    if(comm.IsOpen)

    {

    //打开时点击,则关闭串口

    comm.Close();//这里就是可能导致软件死掉的地方

    }

    else

    {...}

    }

    void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)

    {

    //先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

    int n = comm.BytesToRead;

    //声明一个临时数组存储当前来的串口数据

    byte[] buf = new byte[n];

    //增加接收计数

    received_count += n;

    //读取缓冲数据

    comm.Read(buf, 0, n);

    //清除字符串构造器的内容

    builder.Clear();

    //因为要访问ui资源,所以需要使用invoke方式同步ui。

    this.Invoke((EventHandler)(delegate{...界面更新,略}));

    }

    private void buttonOpenClose_Click(object sender, EventArgs e)

    {

    //根据当前串口对象,来判断操作

    if (comm.IsOpen)

    {

    //打开时点击,则关闭串口

    comm.Close();//这里就是可能导致软件死掉的地方

    }

    else

    {...}

    }

    为什么会死锁呢,并发冲突。

    我们要了解一下SerialPort的实现和串口通讯机制,在你打开串口的时候,SerialPort会创建一个监听线程ListenThread,在这个线程中,等待注册的串口中断,当收到中断后,会调用DataReceived事件。调用完成后,继续进入循环等待,直到串口被关闭退出线程。

    我们的UI主线程如何做的呢,首先创建一个窗体,然后执行了Application.Run(窗体实例)。是这样把,这里的Application.Run就是创建了一个消息循环,循环的处理相关的消息。

    这里我们就有了2个线程,UI主线程、串口监听线程。那么你在DataReceived处理数据的时候,就需要线程同步,避免并发冲突,什么是并发冲突?并发冲突就是2个或多个并行(至少看上去像)的线程运行的时候,多个线程共同的操作某一线程的资源,在时序上同时或没有按我们的预计顺序操作,这样就可能导致数据混乱无序或是彼此等待完成死锁软件。

    而串口程序大多是后者。为什么呢,看看我们的例子中DataReceived做了什么?首先读取数据,然后就是调用this.Invoke方法更新UI了。这里Invoke的时候,监听线程将等待UI线程的标志,等到后,开始操作UI的资源,当操作完成之前,监听线程也就停在DataReceived方法的调用这里,如果这个时候。并发了关闭串口的操作会如何呢?SerialPort的Close方法,会首先尝试等待和监听线程一样的一个互斥体、临界区、或是事件(不确定.net用的哪种)。那这个同步对象什么时候释放呢?每次循环结束就释放,哦。循环为什么不结束呢?因为这一次的循环操作执行到DataReceived之后,执行了Invoke去更新界面了,那Invoke怎么又没有执行完成呢?看上去很简单的几行代码。虽然我没仔细研读过.net的Invoke原理,但我猜测是通过消息的方式来同步的,这也是为什么这么多的类,只有控件(窗体也是控件的一种,.net在概念上,颠覆了微软自己的概念,传统的win32编程,是说所有的控件都是个window,只是父窗体不同,表现形式不同,但都是基于系统消息队列的,.net出于更高的抽象,正好反过来了。呵呵)才有Invoke方法了。(委托自己的Invoke和这个不同)

    我猜测控件/窗体的Invoke是SendMessage方式实现的,那么发送消息后就会等待消息循环来处理消息了。如果你直接去关闭串口了。你点击按钮本身也会被转换成消息WM_CLICK,消息循环在处理按钮的WM_CLICK时候,调用你按钮的OnClick方法,进而触发调用你的ButtonClose_Click事件,这都是同步调用的,你的主线程,处理消息的过程,停在了这个Click事件,而你的Click事件又去调用了SerialPort的Close方法,Close方法又因为和串口监听线程的同步信号量关联在一起需要等待一次的while结束,而这个while循环中调用了DataReceived方法,这个方法中调用了Invoke,也就是发送了消息到消息队列等待结果,但消息循环正在处理你的关闭按钮事件等待退出。

    实在太复杂了,这个情况下,你想要真的关闭串口成功,就需要while中的DataReceived方法调用结束释放同步信号,就需要执行完Invoke,就需要执行消息循环,幸运的是,我们真的有办法执行消息循环来打破僵局。Application.DoEvents()。还好,不幸中的万幸。可是问题又来了,你能让Invoke结束,但你无法确定是否在你调用消息循环后,你的某一时刻不会再次并发,可能由于单cpu的串行操作模拟并行中,又把时间片先分给了优先级高的串口监听线程呢?是有可能的。所以,我们就需要一点方法来避免再次invoke窗体。优化后不会司机的例子如下,我们修改DataReceived方法,关闭方法,并定义2个标记Listening和Closing。

    usingSystem;

    usingSystem.Collections.Generic;

    usingSystem.ComponentModel;

    usingSystem.Data;

    usingSystem.Drawing;

    usingSystem.Linq;

    usingSystem.Text;

    usingSystem.Windows.Forms;

    usingSystem.IO.Ports;

    usingSystem.Text.RegularExpressions;

    namespaceSerialportSample

    {

    publicpartialclassSerialportSampleForm : Form

    {

    privateSerialPort comm =newSerialPort();

    privateStringBuilder builder =newStringBuilder();//避免在事件处理方法中反复的创建,定义到外面。

    privatelongreceived_count = 0;//接收计数

    privatelongsend_count = 0;//发送计数

    privateboolListening =false;//是否没有执行完invoke相关操作

    privateboolClosing =false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke

    publicSerialportSampleForm()

    {

    InitializeComponent();

    }

    //窗体初始化

    privatevoidForm1_Load(objectsender, EventArgs e)

    {

    //初始化下拉串口名称列表框

    string[] ports = SerialPort.GetPortNames();

    Array.Sort(ports);

    comboPortName.Items.AddRange(ports);

    comboPortName.SelectedIndex = comboPortName.Items.Count > 0 ? 0 : -1;

    comboBaudrate.SelectedIndex = comboBaudrate.Items.IndexOf("9600");

    //初始化SerialPort对象

    comm.NewLine ="/r/n";

    comm.RtsEnable =true;//根据实际情况吧。

    //添加事件注册

    comm.DataReceived += comm_DataReceived;

    }

    voidcomm_DataReceived(objectsender, SerialDataReceivedEventArgs e)

    {

    if(Closing)return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环

    try

    {

    Listening =true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。

    intn = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

    byte[] buf =newbyte[n];//声明一个临时数组存储当前来的串口数据

    received_count += n;//增加接收计数

    comm.Read(buf, 0, n);//读取缓冲数据

    builder.Clear();//清除字符串构造器的内容

    //因为要访问ui资源,所以需要使用invoke方式同步ui。

    this.Invoke((EventHandler)(delegate

    {

    //判断是否是显示为16禁止

    if(checkBoxHexView.Checked)

    {

    //依次的拼接出16进制字符串

    foreach(bytebinbuf)

    {

    builder.Append(b.ToString("X2") +" ");

    }

    }

    else

    {

    //直接按ASCII规则转换成字符串

    builder.Append(Encoding.ASCII.GetString(buf));

    }

    //追加的形式添加到文本框末端,并滚动到最后。

    this.txGet.AppendText(builder.ToString());

    //修改接收计数

    labelGetCount.Text ="Get:"+ received_count.ToString();

    }));

    }

    finally

    {

    Listening =false;//我用完了,ui可以关闭串口了。

    }

    }

    privatevoidbuttonOpenClose_Click(objectsender, EventArgs e)

    {

    //根据当前串口对象,来判断操作

    if(comm.IsOpen)

    {

    Closing =true;

    while(Listening) Application.DoEvents();

    //打开时点击,则关闭串口

    comm.Close();

    Closing =false;

    }

    else

    {

    //关闭时点击,则设置好端口,波特率后打开

    comm.PortName = comboPortName.Text;

    comm.BaudRate =int.Parse(comboBaudrate.Text);

    try

    {

    comm.Open();

    }

    catch(Exception ex)

    {

    //捕获到异常信息,创建一个新的comm对象,之前的不能用了。

    comm =newSerialPort();

    //现实异常信息给客户。

    MessageBox.Show(ex.Message);

    }

    }

    //设置按钮的状态

    buttonOpenClose.Text = comm.IsOpen ?"Close":"Open";

    buttonSend.Enabled = comm.IsOpen;

    }

    //动态的修改获取文本框是否支持自动换行。

    privatevoidcheckBoxNewlineGet_CheckedChanged(objectsender, EventArgs e)

    {

    txGet.WordWrap = checkBoxNewlineGet.Checked;

    }

    privatevoidbuttonSend_Click(objectsender, EventArgs e)

    {

    //定义一个变量,记录发送了几个字节

    intn = 0;

    //16进制发送

    if(checkBoxHexSend.Checked)

    {

    //我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数

    MatchCollection mc = Regex.Matches(txSend.Text, @"(?i)[/da-f]{2}");

    List buf =newList();//填充到这个临时列表中

    //依次添加到列表中

    foreach(Match minmc)

    {

    buf.Add(byte.Parse(m.Value));

    }

    //转换列表为数组后发送

    comm.Write(buf.ToArray(), 0, buf.Count);

    //记录发送的字节数

    n = buf.Count;

    }

    else//ascii编码直接发送

    {

    //包含换行符

    if(checkBoxNewlineSend.Checked)

    {

    comm.WriteLine(txSend.Text);

    n = txSend.Text.Length + 2;

    }

    else//不包含换行符

    {

    comm.Write(txSend.Text);

    n = txSend.Text.Length;

    }

    }

    send_count += n;//累加发送字节数

    labelSendCount.Text ="Send:"+ send_count.ToString();//更新界面

    }

    privatevoidbuttonReset_Click(objectsender, EventArgs e)

    {

    //复位接受和发送的字节数计数器并更新界面。

    send_count = received_count = 0;

    labelGetCount.Text ="Get:0";

    labelSendCount.Text ="Send:0";

    }

    }

    }

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Linq;

    using System.Text;

    using System.Windows.Forms;

    using System.IO.Ports;

    using System.Text.RegularExpressions;

    namespace SerialportSample

    {

    public partial class SerialportSampleForm : Form

    {

    private SerialPort comm = new SerialPort();

    private StringBuilder builder = new StringBuilder();//避免在事件处理方法中反复的创建,定义到外面。

    private long received_count = 0;//接收计数

    private long send_count = 0;//发送计数

    private bool Listening = false;//是否没有执行完invoke相关操作

    private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke

    public SerialportSampleForm()

    {

    InitializeComponent();

    }

    //窗体初始化

    private void Form1_Load(object sender, EventArgs e)

    {

    //初始化下拉串口名称列表框

    string[] ports = SerialPort.GetPortNames();

    Array.Sort(ports);

    comboPortName.Items.AddRange(ports);

    comboPortName.SelectedIndex = comboPortName.Items.Count > 0 ? 0 : -1;

    comboBaudrate.SelectedIndex = comboBaudrate.Items.IndexOf("9600");

    //初始化SerialPort对象

    comm.NewLine = "/r/n";

    comm.RtsEnable = true;//根据实际情况吧。

    //添加事件注册

    comm.DataReceived += comm_DataReceived;

    }

    void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)

    {

    if (Closing) return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环

    try

    {

    Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。

    int n = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

    byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据

    received_count += n;//增加接收计数

    comm.Read(buf, 0, n);//读取缓冲数据

    builder.Clear();//清除字符串构造器的内容

    //因为要访问ui资源,所以需要使用invoke方式同步ui。

    this.Invoke((EventHandler)(delegate

    {

    //判断是否是显示为16禁止

    if (checkBoxHexView.Checked)

    {

    //依次的拼接出16进制字符串

    foreach (byte b in buf)

    {

    builder.Append(b.ToString("X2") + " ");

    }

    }

    else

    {

    //直接按ASCII规则转换成字符串

    builder.Append(Encoding.ASCII.GetString(buf));

    }

    //追加的形式添加到文本框末端,并滚动到最后。

    this.txGet.AppendText(builder.ToString());

    //修改接收计数

    labelGetCount.Text = "Get:" + received_count.ToString();

    }));

    }

    finally

    {

    Listening = false;//我用完了,ui可以关闭串口了。

    }

    }

    private void buttonOpenClose_Click(object sender, EventArgs e)

    {

    //根据当前串口对象,来判断操作

    if (comm.IsOpen)

    {

    Closing = true;

    while (Listening) Application.DoEvents();

    //打开时点击,则关闭串口

    comm.Close();

    Closing = false;

    }

    else

    {

    //关闭时点击,则设置好端口,波特率后打开

    comm.PortName = comboPortName.Text;

    comm.BaudRate = int.Parse(comboBaudrate.Text);

    try

    {

    comm.Open();

    }

    catch(Exception ex)

    {

    //捕获到异常信息,创建一个新的comm对象,之前的不能用了。

    comm = new SerialPort();

    //现实异常信息给客户。

    MessageBox.Show(ex.Message);

    }

    }

    //设置按钮的状态

    buttonOpenClose.Text = comm.IsOpen ? "Close" : "Open";

    buttonSend.Enabled = comm.IsOpen;

    }

    //动态的修改获取文本框是否支持自动换行。

    private void checkBoxNewlineGet_CheckedChanged(object sender, EventArgs e)

    {

    txGet.WordWrap = checkBoxNewlineGet.Checked;

    }

    private void buttonSend_Click(object sender, EventArgs e)

    {

    //定义一个变量,记录发送了几个字节

    int n = 0;

    //16进制发送

    if (checkBoxHexSend.Checked)

    {

    //我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数

    MatchCollection mc = Regex.Matches(txSend.Text, @"(?i)[/da-f]{2}");

    List buf = new List();//填充到这个临时列表中

    //依次添加到列表中

    foreach (Match m in mc)

    {

    buf.Add(byte.Parse(m.Value));

    }

    //转换列表为数组后发送

    comm.Write(buf.ToArray(), 0, buf.Count);

    //记录发送的字节数

    n = buf.Count;

    }

    else//ascii编码直接发送

    {

    //包含换行符

    if (checkBoxNewlineSend.Checked)

    {

    comm.WriteLine(txSend.Text);

    n = txSend.Text.Length + 2;

    }

    else//不包含换行符

    {

    comm.Write(txSend.Text);

    n = txSend.Text.Length;

    }

    }

    send_count += n;//累加发送字节数

    labelSendCount.Text = "Send:" + send_count.ToString();//更新界面

    }

    private void buttonReset_Click(object sender, EventArgs e)

    {

    //复位接受和发送的字节数计数器并更新界面。

    send_count = received_count = 0;

    labelGetCount.Text = "Get:0";

    labelSendCount.Text = "Send:0";

    }

    }

    }

    至此,不会再出现关闭死锁问题了。

    希望这篇文章能解你的燃眉之急,非常高兴能与读者分享我层遇到,大多数人都遇到的这个问题。如果说的不明白,欢迎讨论。

    后续的有关通讯程序底层设计的文章会讲述一个具有丰富扩展性,但有设计简介的万能通讯库,支持网络、蓝牙、串口通讯、并口通讯。但不要指望我都实现出来了,我只是设计出这个框架。

    //append by wuyazhe @2011-5-26

    上面有一点疏漏,源自第一篇,结果到这里还是没修改,源码中有一行,需要修改一下。

    //发送按钮中

    buf.Add(byte.Parse(m.Value));

    要修改为

    buf.Add(byte.Parse(m.Value,System.Globalization.NumberStyles.HexNumber));

    2010-05-27 09:54 11853人阅读 评论(139)

    我们的串口程序,除了通用的,进行串口监听收发的简单工具,大多都和下位机有关,这就需要关心我们的通讯协议如何缓存,分析,以及通知界面。

    我们先说一下通讯协议。通讯协议就是通讯双方共同遵循的一套规则,定义协议的原则是尽可能的简单以提高传输率,尽可能的具有安全性保证数据传输完整正确。基于这2点规则,我们一个通讯协议应该是这样的:头+数据长度+数据正文+校验

    例如:AA 44 05 01 02 03 04 05 EA

    这里我假设的一条数据,协议如下:

    数据头:     AA 44

    数据长度: 05

    数据正文: 01 02 03 04 05

    校验:       EA

    一般数据的校验,都会采用常用的方式,CRC16,CRC32,Xor。

    有的数据安全要求高的,不允许丢包的,可能还要加入重发机制或是加入数据恢复算法,在校验后根据前面数据添加恢复字节流以恢复数据。我这里采用的是简单的异或校验,包含数据头的所有字节,依次异或得到的。

    协议很简单,我也认为分析协议是很简单的事情,下面我们就如何分析协议来实际的结合c#看一下。

    er…再等等,在我们实际开始编码之前,还有一个规则需要了解,我们有了通讯协议,如何结合串口的协议来分析,需要关心什么呢?哦。一般就是4个问题:缓存收到的所有数据,找到一条完整数据,分析数据,界面通知。

    如果分的更详细一点,缓存收到的所有数据,我们想到最高效的办法就是顺序表,也就是数组,但数组的操作比较复杂,当你使用完一条数据后,用过的需要移除;新数据如果过多的时候,缓存过大需要清理;数据搬移等等,很有可能一个不小心就会丢数据导致软件出些莫名其妙的小问题。个人建议,使用List,内部是数组方式实现,每次数据不足够的时候会扩容1倍,数据的增删改都已经做的很完善了。不会出现什么小问题。

    找到一条完整数据,如何找到完整数据呢?就我们例子的这个协议,首先在缓存的数据中找AA 44,当我们找到后,探测后面的字节,发现是05,然后看缓存剩下的数据是否足够,不足够就不用判断,减少时间消耗,如果剩余数据>=6个(包含1个字节的校验),我们就算一个校验,看和最后的校验是否一致。

    分析数据:鉴于网络的开放性,我无法确定读者对c#的了解程度,介绍一下,常用的方式就是BitConvert.ToInt32这一系列的方法,把连续的字节(和变量长度一样)读取并转换为对应的变量。c++下使用memcpy,或直接类型转换后进行值拷贝,vb6下使用CopyMemory这个api。

    校验:前面说过了。完整性判断的时候需要和校验对比,大多系统都不太严格,不支持重发,所以数据错误就直接丢弃。导致数据错误的原因很多,比如电磁干扰导致数据不完整或错误、硬件驱动效率不够导致数据丢失、我们的软件缓存出错等。这些软件因素数据系统错误,需要修改,但是电磁干扰么,有这个可能的。虽然很少。

    其实我知道,就算是我,看别人的博客也是,喜欢看图片,看代码,文字性的东西,一看就头大。那我接下来贴出基于上一篇文章的改进版本,支持协议分析(协议不能配置,可配置的协议不是我们讨论的范畴。可以看看有DFA(确定性有限状态机))

    我们修改一下界面,以便能显示收到后分析的数据

    代码如下:

    usingSystem;

    usingSystem.Collections.Generic;

    usingSystem.ComponentModel;

    usingSystem.Data;

    usingSystem.Drawing;

    usingSystem.Linq;

    usingSystem.Text;

    usingSystem.Windows.Forms;

    usingSystem.IO.Ports;

    usingSystem.Text.RegularExpressions;

    namespaceSerialportSample

    {

    publicpartialclassSerialportSampleForm : Form

    {

    privateSerialPort comm =newSerialPort();

    privateStringBuilder builder =newStringBuilder();//避免在事件处理方法中反复的创建,定义到外面。

    privatelongreceived_count = 0;//接收计数

    privatelongsend_count = 0;//发送计数

    privateboolListening =false;//是否没有执行完invoke相关操作

    privateboolClosing =false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke

    privateList buffer =newList(4096);//默认分配1页内存,并始终限制不允许超过

    privatebyte[] binary_data_1 =newbyte[9];//AA 44 05 01 02 03 04 05 EA

    publicSerialportSampleForm()

    {

    InitializeComponent();

    }

    //窗体初始化

    privatevoidForm1_Load(objectsender, EventArgs e)

    {

    //初始化下拉串口名称列表框

    string[] ports = SerialPort.GetPortNames();

    Array.Sort(ports);

    comboPortName.Items.AddRange(ports);

    comboPortName.SelectedIndex = comboPortName.Items.Count > 0 ? 0 : -1;

    comboBaudrate.SelectedIndex = comboBaudrate.Items.IndexOf("19200");

    //初始化SerialPort对象

    comm.NewLine ="/r/n";

    comm.RtsEnable =true;//根据实际情况吧。

    //添加事件注册

    comm.DataReceived += comm_DataReceived;

    }

    voidcomm_DataReceived(objectsender, SerialDataReceivedEventArgs e)

    {

    if(Closing)return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环

    try

    {

    Listening =true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。

    intn = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

    byte[] buf =newbyte[n];//声明一个临时数组存储当前来的串口数据

    received_count += n;//增加接收计数

    comm.Read(buf, 0, n);//读取缓冲数据

    /

    //

    booldata_1_catched =false;//缓存记录数据是否捕获到

    //1.缓存数据

    buffer.AddRange(buf);

    //2.完整性判断

    while(buffer.Count >= 4)//至少要包含头(2字节)+长度(1字节)+校验(1字节)

    {

    //请不要担心使用>=,因为>=已经和>,和=2个符号

    //2.1 查找数据头

    if(buffer[0] == 0xAA && buffer[1] == 0x44)

    {

    //2.2 探测缓存数据是否有一条数据的字节,如果不够,就不用费劲的做其他验证了

    //前面已经限定了剩余长度>=4,那我们这里一定能访问到buffer[2]这个长度

    intlen = buffer[2];//数据长度

    //数据完整判断第一步,长度是否足够

    //len是数据段长度,4个字节是while行注释的3部分长度

    if(buffer.Count 

    //这里确保数据长度足够,数据头标志找到,我们开始计算校验

    //2.3 校验数据,确认数据正确

    //异或校验,逐个字节异或得到校验码

    bytechecksum = 0;

    for(inti = 0; i 

    {

    checksum ^= buffer[i];

    }

    if(checksum != buffer[len + 3])//如果数据校验失败,丢弃这一包数据

    {

    buffer.RemoveRange(0, len + 4);//从缓存中删除错误数据

    continue;//继续下一次循环

    }

    //至此,已经被找到了一条完整数据。我们将数据直接分析,或是缓存起来一起分析

    //我们这里采用的办法是缓存一次,好处就是如果你某种原因,数据堆积在缓存buffer中

    //已经很多了,那你需要循环的找到最后一组,只分析最新数据,过往数据你已经处理不及时

    //了,就不要浪费更多时间了,这也是考虑到系统负载能够降低。

    buffer.CopyTo(0, binary_data_1, 0, len + 4);//复制一条完整数据到具体的数据缓存

    data_1_catched =true;

    buffer.RemoveRange(0, len + 4);//正确分析一条数据,从缓存中移除数据。

    }

    else

    {

    //这里是很重要的,如果数据开始不是头,则删除数据

    buffer.RemoveAt(0);

    }

    }

    //分析数据

    if(data_1_catched)

    {

    //我们的数据都是定好格式的,所以当我们找到分析出的数据1,就知道固定位置一定是这些数据,我们只要显示就可以了

    stringdata = binary_data_1[3].ToString("X2") +" "+ binary_data_1[4].ToString("X2") +" "+

    binary_data_1[5].ToString("X2") +" "+ binary_data_1[6].ToString("X2") +" "+

    binary_data_1[7].ToString("X2");

    //更新界面

    this.Invoke((EventHandler)(delegate{ txData.Text = data; }));

    }

    //如果需要别的协议,只要扩展这个data_n_catched就可以了。往往我们协议多的情况下,还会包含数据编号,给来的数据进行

    //编号,协议优化后就是: 头+编号+长度+数据+校验

    //协议解析>

    /

    builder.Clear();//清除字符串构造器的内容

    //因为要访问ui资源,所以需要使用invoke方式同步ui。

    this.Invoke((EventHandler)(delegate

    {

    //判断是否是显示为16禁止

    if(checkBoxHexView.Checked)

    {

    //依次的拼接出16进制字符串

    foreach(bytebinbuf)

    {

    builder.Append(b.ToString("X2") +" ");

    }

    }

    else

    {

    //直接按ASCII规则转换成字符串

    builder.Append(Encoding.ASCII.GetString(buf));

    }

    //追加的形式添加到文本框末端,并滚动到最后。

    this.txGet.AppendText(builder.ToString());

    //修改接收计数

    labelGetCount.Text ="Get:"+ received_count.ToString();

    }));

    }

    finally

    {

    Listening =false;//我用完了,ui可以关闭串口了。

    }

    }

    privatevoidbuttonOpenClose_Click(objectsender, EventArgs e)

    {

    //根据当前串口对象,来判断操作

    if(comm.IsOpen)

    {

    Closing =true;

    while(Listening) Application.DoEvents();

    //打开时点击,则关闭串口

    comm.Close();

    }

    else

    {

    //关闭时点击,则设置好端口,波特率后打开

    comm.PortName = comboPortName.Text;

    comm.BaudRate =int.Parse(comboBaudrate.Text);

    try

    {

    comm.Open();

    }

    catch(Exception ex)

    {

    //捕获到异常信息,创建一个新的comm对象,之前的不能用了。

    comm =newSerialPort();

    //现实异常信息给客户。

    MessageBox.Show(ex.Message);

    }

    }

    //设置按钮的状态

    buttonOpenClose.Text = comm.IsOpen ?"Close":"Open";

    buttonSend.Enabled = comm.IsOpen;

    }

    //动态的修改获取文本框是否支持自动换行。

    privatevoidcheckBoxNewlineGet_CheckedChanged(objectsender, EventArgs e)

    {

    txGet.WordWrap = checkBoxNewlineGet.Checked;

    }

    privatevoidbuttonSend_Click(objectsender, EventArgs e)

    {

    //定义一个变量,记录发送了几个字节

    intn = 0;

    //16进制发送

    if(checkBoxHexSend.Checked)

    {

    //我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数

    MatchCollection mc = Regex.Matches(txSend.Text, @"(?i)[/da-f]{2}");

    List buf =newList();//填充到这个临时列表中

    //依次添加到列表中

    foreach(Match minmc)

    {

    buf.Add(byte.Parse(m.Value, System.Globalization.NumberStyles.HexNumber));

    }

    //转换列表为数组后发送

    comm.Write(buf.ToArray(), 0, buf.Count);

    //记录发送的字节数

    n = buf.Count;

    }

    else//ascii编码直接发送

    {

    //包含换行符

    if(checkBoxNewlineSend.Checked)

    {

    comm.WriteLine(txSend.Text);

    n = txSend.Text.Length + 2;

    }

    else//不包含换行符

    {

    comm.Write(txSend.Text);

    n = txSend.Text.Length;

    }

    }

    send_count += n;//累加发送字节数

    labelSendCount.Text ="Send:"+ send_count.ToString();//更新界面

    }

    privatevoidbuttonReset_Click(objectsender, EventArgs e)

    {

    //复位接受和发送的字节数计数器并更新界面。

    send_count = received_count = 0;

    labelGetCount.Text ="Get:0";

    labelSendCount.Text ="Send:0";

    }

    }

    }

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Linq;

    using System.Text;

    using System.Windows.Forms;

    using System.IO.Ports;

    using System.Text.RegularExpressions;

    namespace SerialportSample

    {

    public partial class SerialportSampleForm : Form

    {

    private SerialPort comm = new SerialPort();

    private StringBuilder builder = new StringBuilder();//避免在事件处理方法中反复的创建,定义到外面。

    private long received_count = 0;//接收计数

    private long send_count = 0;//发送计数

    private bool Listening = false;//是否没有执行完invoke相关操作

    private bool Closing = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke

    private List buffer = new List(4096);//默认分配1页内存,并始终限制不允许超过

    private byte[] binary_data_1 = new byte[9];//AA 44 05 01 02 03 04 05 EA

    public SerialportSampleForm()

    {

    InitializeComponent();

    }

    //窗体初始化

    private void Form1_Load(object sender, EventArgs e)

    {

    //初始化下拉串口名称列表框

    string[] ports = SerialPort.GetPortNames();

    Array.Sort(ports);

    comboPortName.Items.AddRange(ports);

    comboPortName.SelectedIndex = comboPortName.Items.Count > 0 ? 0 : -1;

    comboBaudrate.SelectedIndex = comboBaudrate.Items.IndexOf("19200");

    //初始化SerialPort对象

    comm.NewLine = "/r/n";

    comm.RtsEnable = true;//根据实际情况吧。

    //添加事件注册

    comm.DataReceived += comm_DataReceived;

    }

    void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)

    {

    if (Closing) return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环

    try

    {

    Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。

    int n = comm.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

    byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据

    received_count += n;//增加接收计数

    comm.Read(buf, 0, n);//读取缓冲数据

    /

    //

    bool data_1_catched = false;//缓存记录数据是否捕获到

    //1.缓存数据

    buffer.AddRange(buf);

    //2.完整性判断

    while (buffer.Count >= 4)//至少要包含头(2字节)+长度(1字节)+校验(1字节)

    {

    //请不要担心使用>=,因为>=已经和>,和=2个符号

    //2.1 查找数据头

    if (buffer[0] == 0xAA && buffer[1] == 0x44)

    {

    //2.2 探测缓存数据是否有一条数据的字节,如果不够,就不用费劲的做其他验证了

    //前面已经限定了剩余长度>=4,那我们这里一定能访问到buffer[2]这个长度

    int len = buffer[2];//数据长度

    //数据完整判断第一步,长度是否足够

    //len是数据段长度,4个字节是while行注释的3部分长度

    if (buffer.Count < len + 4) break;//数据不够的时候什么都不做

    //这里确保数据长度足够,数据头标志找到,我们开始计算校验

    //2.3 校验数据,确认数据正确

    //异或校验,逐个字节异或得到校验码

    byte checksum = 0;

    for (int i = 0; i < len + 3; i++)//len+3表示校验之前的位置

    {

    checksum ^= buffer[i];

    }

    if (checksum != buffer[len + 3]) //如果数据校验失败,丢弃这一包数据

    {

    buffer.RemoveRange(0, len + 4);//从缓存中删除错误数据

    continue;//继续下一次循环

    }

    //至此,已经被找到了一条完整数据。我们将数据直接分析,或是缓存起来一起分析

    //我们这里采用的办法是缓存一次,好处就是如果你某种原因,数据堆积在缓存buffer中

    //已经很多了,那你需要循环的找到最后一组,只分析最新数据,过往数据你已经处理不及时

    //了,就不要浪费更多时间了,这也是考虑到系统负载能够降低。

    buffer.CopyTo(0, binary_data_1, 0, len + 4);//复制一条完整数据到具体的数据缓存

    data_1_catched = true;

    buffer.RemoveRange(0, len + 4);//正确分析一条数据,从缓存中移除数据。

    }

    else

    {

    //这里是很重要的,如果数据开始不是头,则删除数据

    buffer.RemoveAt(0);

    }

    }

    //分析数据

    if (data_1_catched)

    {

    //我们的数据都是定好格式的,所以当我们找到分析出的数据1,就知道固定位置一定是这些数据,我们只要显示就可以了

    string data = binary_data_1[3].ToString("X2") + " " + binary_data_1[4].ToString("X2") + " " +

    binary_data_1[5].ToString("X2") + " " + binary_data_1[6].ToString("X2") + " " +

    binary_data_1[7].ToString("X2");

    //更新界面

    this.Invoke((EventHandler)(delegate { txData.Text = data; }));

    }

    //如果需要别的协议,只要扩展这个data_n_catched就可以了。往往我们协议多的情况下,还会包含数据编号,给来的数据进行

    //编号,协议优化后就是: 头+编号+长度+数据+校验

    //协议解析>

    /

    builder.Clear();//清除字符串构造器的内容

    //因为要访问ui资源,所以需要使用invoke方式同步ui。

    this.Invoke((EventHandler)(delegate

    {

    //判断是否是显示为16禁止

    if (checkBoxHexView.Checked)

    {

    //依次的拼接出16进制字符串

    foreach (byte b in buf)

    {

    builder.Append(b.ToString("X2") + " ");

    }

    }

    else

    {

    //直接按ASCII规则转换成字符串

    builder.Append(Encoding.ASCII.GetString(buf));

    }

    //追加的形式添加到文本框末端,并滚动到最后。

    this.txGet.AppendText(builder.ToString());

    //修改接收计数

    labelGetCount.Text = "Get:" + received_count.ToString();

    }));

    }

    finally

    {

    Listening = false;//我用完了,ui可以关闭串口了。

    }

    }

    private void buttonOpenClose_Click(object sender, EventArgs e)

    {

    //根据当前串口对象,来判断操作

    if (comm.IsOpen)

    {

    Closing = true;

    while (Listening) Application.DoEvents();

    //打开时点击,则关闭串口

    comm.Close();

    }

    else

    {

    //关闭时点击,则设置好端口,波特率后打开

    comm.PortName = comboPortName.Text;

    comm.BaudRate = int.Parse(comboBaudrate.Text);

    try

    {

    comm.Open();

    }

    catch(Exception ex)

    {

    //捕获到异常信息,创建一个新的comm对象,之前的不能用了。

    comm = new SerialPort();

    //现实异常信息给客户。

    MessageBox.Show(ex.Message);

    }

    }

    //设置按钮的状态

    buttonOpenClose.Text = comm.IsOpen ? "Close" : "Open";

    buttonSend.Enabled = comm.IsOpen;

    }

    //动态的修改获取文本框是否支持自动换行。

    private void checkBoxNewlineGet_CheckedChanged(object sender, EventArgs e)

    {

    txGet.WordWrap = checkBoxNewlineGet.Checked;

    }

    private void buttonSend_Click(object sender, EventArgs e)

    {

    //定义一个变量,记录发送了几个字节

    int n = 0;

    //16进制发送

    if (checkBoxHexSend.Checked)

    {

    //我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数

    MatchCollection mc = Regex.Matches(txSend.Text, @"(?i)[/da-f]{2}");

    List buf = new List();//填充到这个临时列表中

    //依次添加到列表中

    foreach (Match m in mc)

    {

    buf.Add(byte.Parse(m.Value, System.Globalization.NumberStyles.HexNumber));

    }

    //转换列表为数组后发送

    comm.Write(buf.ToArray(), 0, buf.Count);

    //记录发送的字节数

    n = buf.Count;

    }

    else//ascii编码直接发送

    {

    //包含换行符

    if (checkBoxNewlineSend.Checked)

    {

    comm.WriteLine(txSend.Text);

    n = txSend.Text.Length + 2;

    }

    else//不包含换行符

    {

    comm.Write(txSend.Text);

    n = txSend.Text.Length;

    }

    }

    send_count += n;//累加发送字节数

    labelSendCount.Text = "Send:" + send_count.ToString();//更新界面

    }

    private void buttonReset_Click(object sender, EventArgs e)

    {

    //复位接受和发送的字节数计数器并更新界面。

    send_count = received_count = 0;

    labelGetCount.Text = "Get:0";

    labelSendCount.Text = "Send:0";

    }

    }

    }

    至此,你只要按这个协议格式发送数据到软件打开的串口。就能在数据的data标签显示出你的数据内容,我们现在是直接显示为:

    01 02 03 04 05

    也就是数据段内容。

    运行截图:

    发送模拟数据的界面,使用通用工具SSCOMM32.exe

    我们在回顾一下,一般二进制格式数据就是这样分析,分析数据长度是否足够,找到数据头,数据长度,校验,然后分析。

    分析方式很多。结合各自实际情况操作,可以使用序列化方式,但是wince不支持,也可以用BitConvert方式将连续的字节读取为某个类型的变量。

    希望看到这里,能给你带来帮助,欢迎大家和我讨论,希望经验丰富的朋友不吝赐教。上一篇中,有朋友说用BeginInvoke可以避免死锁问题,我暂时没有线,没有测试成功,改天测试后再公布结果。

    DataReceived事件中,最高效的做法是指缓存数据,然后异步的去分析数据。但是,这样较复杂,在效率要求不是很高的情况下(大多数情况),可以在DataReceived事件中缓存数据后,立刻进行数据完整性检查,有效性检查,分析数据,以及更新界面。但这么做有一个隐患,底层串口操作的效率依赖于数据分析和界面更新,任何一个环节频繁耗时过长,都会造成数据的堆积。文章只假设都不拖时间的情况。

    谢谢观赏,通讯协议分析系列,未完待续……

    展开全文
    weixin_39522423 2020-12-20 02:21:35
  • 5星
    790KB atgeretg 2017-11-17 14:52:46
  • 4星
    913KB tiancaishishui 2014-02-08 17:13:47
  • 4星
    2.04MB cgtnet 2016-01-27 11:24:07
  • 12.92MB su86yang 2015-11-12 16:37:03
  • 444KB gamethink 2010-11-16 14:13:42
  • 880KB resunli 2020-12-31 09:35:41
  • 446KB baidu_17699333 2015-03-27 08:57:01
  • 642KB yjaff 2021-05-20 17:16:31
  • 383KB weixin_38744435 2019-09-23 15:24:12
  • 4星
    379KB lyf10821 2011-08-04 16:14:03
  • 5.28MB wangheping1234 2018-01-21 16:53:44
  • 383KB weixin_42676824 2021-10-03 10:18:15
  • 1、使用Bud Hound工具监听数据,监听的基本...2、使用虚拟串口工具,进行模拟,使用方法如下: https://blog.csdn.net/weixin_42089190/article/details/106750594 上面两者结合使用,可满足大部分使用场景。 ...

    1、使用Bud  Hound工具监听数据,Bus Hound是一个超级软件总线协议分析器,用于捕捉来自设备的协议包和输入输出操作。是一种专用于PC机各种总线数据包监视和控制的开发工具软件,其名"hound"的中文意思为"猎犬",即指其能敏锐地感知到总线的丝毫变化。

    监听的基本步骤如下:https://jingyan.baidu.com/article/6525d4b194f5beac7c2e9467.html

    2、使用虚拟串口工具Virtual Serial Port Driver,可以模拟插在电脑上设备的端口,例如下面的端口1和端口2,开发的程序使用COM1或COM2,打开串口工具(可意选一个工具),选择其他的端口COM模拟设备。这样程序和设备间的交互。

    使用的方法如下:虚拟串口使用方法_行走-CSDN博客

    利用1抓取设备根据的交互数据,利用2可以模拟设备,上面两者结合使用,可满足大部分使用场景。

    虚拟串口安装及破解版参考:虚拟串口安装与破解-百度经验 (baidu.com)

    展开全文
    hanxiaoyong_ 2021-07-10 14:07:08
  • //等待连接是一个阻塞过程,创建线程来监听 Thread thReceive = new Thread(TSReceive); thReceive.IsBackground = true; thReceive.Start(); } else if (protocol == "TCP Client") { ip = IPAddress.Parse(txtIP....

    【实例简介】服务端 客户端一体化

    【实例截图】

    【核心代码】

    using System;

    using System.Collections.Generic;

    using System.ComponentModel;

    using System.Data;

    using System.Drawing;

    using System.Linq;

    using System.Net;

    using System.Net.Sockets;

    using System.Text;

    using System.Threading;

    using System.Threading.Tasks;

    using System.Windows.Forms;

    namespace NetWork

    {

    public partial class Form1 : Form

    {

    public Form1()

    {

    InitializeComponent();

    }

    int a = 0;

    int b = 0;

    int c = 0;

    IPAddress ip;

    IPEndPoint point = new IPEndPoint(IPAddress.Any,0);

    string protocol;

    Socket aimSocket;

    Socket mySocket;

    //IPEndPoint remote;

    private void Form1_Load(object sender, EventArgs e)//初始化为UDP Server模式

    {

    cobProtocol.SelectedIndex = 0;

    txtIP.Text = GetAddressIP();

    Control.CheckForIllegalCrossThreadCalls = false;

    }

    string GetAddressIP()

    {

    string AddressIP = "";

    foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)

    {

    if (_IPAddress.AddressFamily.ToString() == "InterNetwork")

    {

    AddressIP = _IPAddress.ToString();

    ip = _IPAddress;//设定全局的IP

    }

    }

    return AddressIP;

    }

    private void btnStart_Click(object sender, EventArgs e)//设置目标IP(Client),本地IP(Server)

    {

    try

    {

    if (protocol == "TCP Server")

    {

    mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));//绑定端口号

    mySocket.Bind(point);//绑定监听端口

    MessageBox.Show("TCP Server绑定成功");

    mySocket.Listen(10);//等待连接是一个阻塞过程,创建线程来监听

    Thread thReceive = new Thread(TSReceive);

    thReceive.IsBackground = true;

    thReceive.Start();

    }

    else if (protocol == "TCP Client")

    {

    ip = IPAddress.Parse(txtIP.Text);//目标IP

    point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));//目标端口

    aimSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    aimSocket.Connect(point);//连接服务器

    MessageBox.Show("连接成功");

    Thread thReceive = new Thread(TCReceive);

    thReceive.IsBackground = true;

    thReceive.Start();

    }

    }

    catch

    {}

    }

    private void cobProtocol_SelectedIndexChanged(object sender, EventArgs e)

    {

    // [2] TCP Server [3] TCP Client

    //此处设置标准位??? 然后为btnConnect做准备?

    protocol = cobProtocol.SelectedItem.ToString();

    if (protocol == "TCP Server")

    {

    lblIP.Text = "本地IP地址:";

    lblPort.Text = "本地端口号:";

    }

    else if (protocol == "TCP Client")

    {

    lblIP.Text = "服务器IP地址:";

    lblPort.Text = "服务器端口号:";

    }

    }

    private void btnSend_Click(object sender, EventArgs e)

    {

    try

    {

    byte[] buffer = Encoding.Default.GetBytes(txtSend.Text);

    aimSocket.SendTo(buffer, point);

    }

    catch

    { }

    }

    //TCP Server接收线程

    void TSReceive()

    {

    aimSocket = mySocket.Accept();//服务端监听到的Socket为服务端发送数据的目标socket

    byte[] buffer = new byte[1024];

    while (true)

    {

    try

    {

    int r = aimSocket.Receive(buffer);//接收到监听的Socket的数据

    if (r == 0)

    {

    MessageBox.Show("连接断开");

    break;

    }

    string strRec = Encoding.Default.GetString(buffer, 0, r);

    txtRec.AppendText(aimSocket.RemoteEndPoint.ToString() ":" strRec "\r\n");

    //温度解析

    int p = -1;

    int w = -1;

    p=strRec.IndexOf("wendu:");

    w=strRec.IndexOf("//wendu");

    if ((p != -1)&&(w!=-1))

    {

    int length = w - p;

    wendu.Text = strRec.Substring(p 6, length-6);

    }

    //湿度解析

    int i = -1;

    int u = -1;

    i = strRec.IndexOf("shidu:");

    u = strRec.IndexOf("//shidu");

    if ((i != -1) && (u != -1))

    {

    int length = u - i;

    shidu.Text = strRec.Substring(i 6, length - 6);

    }

    }

    catch

    { }

    }

    }

    //TCP Client接收线程

    void TCReceive()

    {

    byte[] buffer = new byte[1024];

    while (true)

    {

    try

    {

    int r = aimSocket.Receive(buffer);

    if (r == 0)

    {

    MessageBox.Show("连接断开");

    break;

    }

    string strRec = Encoding.Default.GetString(buffer, 0, r);

    txtRec.AppendText(aimSocket.RemoteEndPoint.ToString() ":" strRec "\r\n");

    //温度解析

    int p = -1;

    int w = -1;

    p = strRec.IndexOf("wendu:");

    w = strRec.IndexOf("//wendu");

    if ((p != -1) && (w != -1))

    {

    int length = w - p;

    wendu.Text = strRec.Substring(p 6, length - 6);

    }

    //湿度解析

    int i = -1;

    int u = -1;

    i = strRec.IndexOf("shidu:");

    u = strRec.IndexOf("//shidu");

    if ((i != -1) && (u != -1))

    {

    int length = u - i;

    shidu.Text = strRec.Substring(i 6, length - 6);

    }

    }

    catch

    { }

    }

    }

    private void button1_Click(object sender, EventArgs e)

    {

    try

    {

    if (a % 2 == 0)

    {

    byte[] buffer = Encoding.Default.GetBytes("111");

    aimSocket.SendTo(buffer, point);

    button1.BackColor = Color.Blue;

    txtSend.Text = "111";

    }

    if (a % 2 == 1)

    {

    byte[] buffer = Encoding.Default.GetBytes("100");

    aimSocket.SendTo(buffer, point);

    button1.BackColor = Color.DarkGray;

    txtSend.Text = "100";

    }

    a ;

    }

    catch

    { }

    }

    private void button2_Click(object sender, EventArgs e)

    {

    try

    {

    if (b % 2 == 0)

    {

    byte[] buffer = Encoding.Default.GetBytes("222");

    aimSocket.SendTo(buffer, point);

    button2.BackColor = Color.Blue;

    txtSend.Text = "222";

    }

    if (b % 2 == 1)

    {

    byte[] buffer = Encoding.Default.GetBytes("200");

    aimSocket.SendTo(buffer, point);

    button2.BackColor = Color.DarkGray;

    txtSend.Text = "200";

    }

    b ;

    }

    catch

    { }

    }

    private void button3_Click(object sender, EventArgs e)

    {

    try

    {

    if (c % 2 == 0)

    {

    byte[] buffer = Encoding.Default.GetBytes("333");

    aimSocket.SendTo(buffer, point);

    button3.BackColor = Color.Blue;

    txtSend.Text = "333";

    }

    if (c % 2 == 1)

    {

    byte[] buffer = Encoding.Default.GetBytes("300");

    aimSocket.SendTo(buffer, point);

    button3.BackColor = Color.DarkGray;

    txtSend.Text = "300";

    }

    c ;

    }

    catch

    { }

    }

    private void button4_Click(object sender, EventArgs e)

    {

    try

    {

    byte[] buffer = Encoding.Default.GetBytes("444");

    aimSocket.SendTo(buffer, point);

    txtSend.Text = "444";

    }

    catch

    { }

    }

    private void button5_Click(object sender, EventArgs e)

    {

    try

    {

    byte[] buffer = Encoding.Default.GetBytes("555");

    aimSocket.SendTo(buffer, point);

    txtSend.Text = "555";

    }

    catch

    { }

    }

    private void button6_Click(object sender, EventArgs e)

    {

    try

    {

    byte[] buffer = Encoding.Default.GetBytes("666");

    aimSocket.SendTo(buffer, point);

    txtSend.Text = "666";

    }

    catch

    { }

    }

    private void button7_Click(object sender, EventArgs e)

    {

    txtRec.Text = null;

    }

    private void button8_Click(object sender, EventArgs e)

    {

    txtSend.Text = null;

    }

    }

    }

    展开全文
    weixin_39710179 2020-12-20 02:21:08
  • 4星
    11.59MB cgtnet 2016-01-27 11:21:54

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,511
精华内容 1,804
关键字:

串口监听工具使用方法