嚴華 发表于 2024-8-7 21:37:09

网口串口(Serialport)服务器

文章所用工具
http://t.csdnimg.cn/2gIR8https://csdnimg.cn/release/blog_editor_html/release2.3.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=N7T8http://t.csdnimg.cn/2gIR8



搭建服务器界面

https://img-blog.csdnimg.cn/direct/762ac4d3c532482e84d7186c56f95ee6.png
利用配置文件生存方式类


      public string FileName {get; set; }
      public IniHelper(string name)
      {
            this.FileName = name; //在构造函数中给路径赋值
      } 1 先导入c语言进行读取利用ini文件的方法

   GetPrivateProfileString() 获取ini配置文件的数据
        //static 静态的变量 只能声明在当前文件中,不能在其他源文件进行使用。
        //extern 用来声明外部的符号 可以跨文件使用。
//导入方法所在dll文件
public static extern Int32 GetPrivateProfileString(
    string sectionName, //段名
    string keyName,//键名
    string defaultName,//当键不存在默认值。
    StringBuilder returnValue,// 读取到数据
    uint size,// 读取数据的大小
    string fileName// ini文件路径
    ); 2添加写入ini文件的c方法


public static extern Int32 WritePrivateProfileString(
string sectionName, //段名
string keyName,//键名
string value,//设置的值
string fileName// ini文件路径
); 不直接使用上面的俩个c语言的方法 再封装到其他方法进行使用
        //读取ini数据的方法
        //ReadData("Second","B","Setting.ini") "10"
        //参数1 段名 ,参数2键名 ,参数3是文件路径
public static string ReadData(string sectionName,string keyName,string fileName)
{
    StringBuilder sb = new StringBuilder(512); //512是存储的大小
    GetPrivateProfileString(sectionName, keyName, string.Empty, sb, 512, fileName);
    return sb.ToString();
}
对上面ReadData再封装,封装能直接返回指定范例的数据的方法
        //传进段名和键名获取内容
        //以后Read("段名","键名")  返回字符串范例
public string Read(string sectionName,string keyName)
{
    return ReadData(sectionName, keyName,this.FileName);

}   假如读取数据 没读取到 给赋一个初值
        Read("Secotion",A,"1000")  终极结果:hello world
        Read("Secotion",C,"1000")  终极结果:1000
public string Read(string sectionName, string keyName,string defaultValue)
{
   string v = Read(sectionName, keyName);
   if (string.IsNullOrEmpty(v))
   {
         return defaultValue;
   }
   return v;

} 读取的时间 返回一个整型的数据值
   Read("Secotion",C,1000)  终极结果:1000
Read("Secotion",A,1000)  终极结果:1000
 
public int Read(string s1, string k1, int d1)
{
    string v = Read(s1, k1);
    if (int.TryParse(v,out int value))
    {
      return value;
    }
    else
    {
      return d1;
    }

} public bool Read(string s1, string k1, bool d1)
{
    string v = Read(s1, k1);
    if (bool.TryParse(v,out bool value))
    {
      return value;
    }
    else
    {
      //不能转成bool
      if (v=="1"||v=="OK"||v=="ON"||v=="YES")
      {
            return true;
      }
      else if(v=="0"||v=="NO"||v=="OFF"||v.ToUpper()=="OFF")
      {
            //如果值以上几种请求 值为false
            return false;
      }
      else
      {
            return d1;
      }
    }
}
//ini值是double类型
public double Read(string s1, string k1, double d1)
{
    string v = Read(s1, k1);
    if (double.TryParse(v, out double value))
    {
      return value;
    }
    else
    {
      return d1;
    }

} 封装写入数据的方法
先界说一个静态写入方法
public staticint WriteData(string s1,string k1,string v,string file)
{
    return WritePrivateProfileString(s1,k1,v,file);
} 封装方法
//在封装Write方法传递 传进段名 键名 值是字符串
public int Write(string s1,string k1,string v1)
{
    return WriteData(s1, k1, v1, this.FileName);
}

//在封装Write方法传递 传进段名 键名 值是整型
public int Write(string s1, string k1, int v1)
{
    return WriteData(s1, k1, v1.ToString(), this.FileName);
}

//在封装Write方法传递 传进段名 键名 值是bool
public int Write(string s1, string k1, bool v1)
{
    return WriteData(s1, k1, v1?"1":"0", this.FileName);
}

//在封装Write方法传递 传进段名 键名 值是时间格式
public int Write(string s1, string k1, DateTime v1)
{
    return WriteData(s1, k1, v1.ToString("yyyy-MM-dd HH-mm:ss"), this.FileName);
} using调用 
https://img-blog.csdnimg.cn/direct/2b094e0515404373bfa345d4f8844462.png

编辑配置文件

https://img-blog.csdnimg.cn/direct/582a5a9f27b74afd8e361fc77217549b.png
存入File文件夹中,放到Dubug文件夹中
https://img-blog.csdnimg.cn/direct/f3f61ac7c472444b8a42b80e0ffca9df.png
服务器

IniHelper Ini;
string[] botelvs = new string[] { "1200", "4800", "9600", "13200" }; 1 读取配置文件

string dirPath = Path.Combine(Application.StartupPath, "File"); // debug/File
string filePath = Path.Combine(dirPath, "Setting.ini");// debug / file/setting.ini
Ini = new IniHelper(filePath); // 创建读取对象

// 添加串口
comboBox1.Items.AddRange(SerialPort.GetPortNames()); // 获取所有串口 拼接在下拉框的items中
comboBox2.Items.AddRange(botelvs); // 添加波特率数组
comboBox2.Items.Add("自定义");// 添加一个
comboBox3.Items.AddRange(new string[] { "5", "6", "7", "8" });
comboBox4.Items.AddRange(new string[] { "无", "奇校检", "偶校检" });
comboBox5.Items.AddRange(new string[] { "无", "1", "2", "1.5" });
// 2 开始处理逻辑串口接收数据的事件
// 处理串口的数据
this.serialPort1.DataReceived += SerialPort1_DataReceivd;

// 3 处理界面显示默认值 也就是从ini文件读取数据
readStting();

// 4 开始串口通信
startChuanKou();

// 5 开始网口通信
startTCP(); 开始搭建TCP 服务器

TcpListener listen;
List<TcpClient> lists = new List<TcpClient>(); // 存放所有的客户端
void startTCP()
{
    if(!int.TryParse(textBox3.Text,out int port)||port < 1 || port > 65563)
    {
      MessageBox.Show("请输入正确的端口号");

    }
    // 开启服务器 接受客户端
    try
    {
      listen = new TcpListener(System.Net.IPAddress.Any, port);
      listen.Start(100); // 开始监听
      panel2.BackColor = Color.Green;

      // 把多个客户端接收到数组里面
      new Task(() =>
      {
            try
            {
                while (true)
                {
                  // 接受客户端
                  TcpClient c1 = listen.AcceptTcpClient();

                  // 把客户端添加到数组里面 群发需要
                  lists.Add(c1);

                  // 接受客户端发来的消息
                  tcpReceive(c1);

                }
            }
            catch
            {
                MessageBox.Show("TCP服务器关闭");
            }

      }).Start();

    }
    catch (Exception ex)
    {
      MessageBox.Show("TCP启动失败");
      // 把tcp关闭等操作
      foreach(var itemin lists)
      {
            item.Close(); // 关闭所有的客户端
      }
      listen.Stop();
      panel2.BackColor = Color.Gray;
    }
} 吸收数据

void tcpReceive(TcpClient c1)
{
   
    new Task(() =>
    {
      NetworkStream stream = c1.GetStream();
      try
      {
            MessageBox.Show("111");
            while (c1.Connected) // 当客户端链接的时候
            {
            
                byte[] bs = new byte;
                int length = stream.Read(bs, 0, bs.Length);
                if (length == 0) throw new Exception(); // 客户端断了
                // 接收到数据亮灯
                tcpLiangDeng();
               
                // 把数据转给串口
                if (!serialPort1.IsOpen)
                {
                  MessageBox.Show("串口关闭");
                  break;
                }
                // 把数据转给串口 发送数据,com8能接收到消息,
                serialPort1.Write(bs, 0, length);
            }
      }
      catch
      {
            c1.Close();
            lists.Remove(c1);
      }
    }).Start();
} 亮灯

async void tcpLiangDeng()
{
   
    this.Invoke((Action)(() =>
    {
      textBox2.Text = (int.Parse(textBox2.Text) + 1).ToString();
      // 亮灯
      panel4.BackColor = Color.Pink;
    }));

    // 过一段时间
    await Task.Delay(70);

    this.Invoke((Action)(() =>
    {
      // 关灯
      panel4.BackColor = Color.Gray;
    }));
} 配置串口对象

void startChuanKou()
{
    // 配置串口对象
    try
    {
      this.serialPort1.PortName = comboBox1.Text;// 配置串口名称
      this.serialPort1.BaudRate = int.Parse(comboBox2.Text); // 波特率
      this.serialPort1.DataBits = int.Parse(comboBox3.Text);
      this.serialPort1.StopBits = (StopBits)comboBox5.SelectedIndex; // 正常赋值 StopBits.NOne 枚举值
      this.serialPort1.Parity = (Parity)comboBox4.SelectedIndex; //

      this.serialPort1.Open();
      // 亮灯
      this.panel1.BackColor = Color.Green;

    }
    catch
    {
      MessageBox.Show("打开串口失败");
      // 停止串口
      if(serialPort1.IsOpen) serialPort1.Close();
      // 灭灯
      this.panel1.BackColor = Color.Gray;

    }

}

private void readStting()
{
    // 先配置串口
    comboBox1.SelectedItem = Ini.Read("Serialport", "name", "");
    string botelv = Ini.Read("Serialport", "botelv", "9601");
    int botelvIndex = Array.IndexOf(botelvs, botelv); // 获取botelv在数组里面的索引值
    if (botelvIndex != -1)
    {
      comboBox2.SelectedIndex = botelvIndex;

    }
    else
    {
      // 波特率在数组里面 自定义波特率情况
      comboBox2.DropDownStyle = ComboBoxStyle.DropDown; // 可编辑的下拉框
      // DropDownList 不可编辑的下拉框
      comboBox2.Text = botelv;
    }
    // 处理数据位
    comboBox3.SelectedItem = Ini.Read("Serialport", "databit", "8");
    // 处理奇偶校检位
    comboBox4.SelectedIndex = Ini.Read("Serialport", "parity", 0);
    comboBox5.SelectedIndex = Ini.Read("Serialport", "stopbit", 0);

    comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
    comboBox3.DropDownStyle = ComboBoxStyle.DropDownList;
    comboBox4.DropDownStyle = ComboBoxStyle.DropDownList;
    comboBox5.DropDownStyle = ComboBoxStyle.DropDownList;

    // 网口数据的读取
    textBox3.Text = Ini.Read("NetWork", "port", "8080");
    if (Ini.Read("NetWork", "heartOn", false))
    {
      radioButton1.Checked = true;
    }
    else
    {
      radioButton1.Checked = true;
    }
    textBox4.Text = Ini.Read("NetWork", "heartTime", "60000"); // 心跳间隔
    textBox5.Text = Ini.Read("NetWork", "heartTime", ""); // 心跳间隔
    checkBox1.Checked = Ini.Read("NetWork", "heartTime", false); // 心跳间隔


} 吸收串口通报的数据

private void SerialPort1_DataReceivd(object sender, SerialDataReceivedEventArgs e)
{
    byte[] bs = new byte; // 定义一个字节数组
    int count = serialPort1.Read(bs,0,bs.Length); // 读取数据到字节数组
    if(count == 0)
    {
      // 关闭窗口
      // 停止串口
      if (serialPort1.IsOpen) serialPort1.Close();
      // 灭灯
      this.panel1.BackColor = Color.Gray;
    }

    // 1 接收到一条窗口数据 需要panel3亮一次 分装一个方法控制效果
    JieShouDaoChuanKou();

    // 2 转发给所有的客户端发消息 串口转网口就是把串口数据通过网络转给其他客户端
    foreach(var item in lists)
    {
      item.GetStream().Write(bs,0,bs.Length);
    }

}
async void JieShouDaoChuanKou()
{
    this.Invoke((Action)(() =>
    {
      textBox1.Text = (int.Parse(textBox1.Text) + 1).ToString();
      // 亮灯
      panel3.BackColor = Color.Pink;
    }));

    // 过一段时间
    await Task.Delay(70);
    this.Invoke((Action)(() =>
    {
      textBox1.Text = (int.Parse(textBox1.Text) + 1).ToString();
      // 关灯
      panel3.BackColor = Color.Gray;
    }));

} 波特率下拉框出发变化的时间调用

private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
    if(comboBox2.SelectedItem.ToString() == "自定义")
    {
      // 切换到自定义选项上
      comboBox2.DropDownStyle = ComboBoxStyle.DropDown;
    }
    else
    {
      comboBox2.DropDownStyle = ComboBoxStyle.DropDownList;
    }
} 选中央跳开关为开的时间

private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
   
    if(radioButton1.Checked)
    {
      // 选中心跳
      timer1.Interval = string.IsNullOrEmpty(textBox4.Text) ? 6000 :int.Parse(textBox4.Text);
      timer1.Tick += Timer1_Tick;
      timer1.Start();
    }
}
定时发送数据

private void Timer1_Tick(object sender, EventArgs e)
{
    // 定时发送数据
    string data = textBox5.Text; // 心跳数据 选中hex整明把2转成16进制,
    byte[] buffer;
    if(checkBox1.Checked == true)
    {
      // 需要发16进制的心跳
      string[] ds = data.Split(' '); //把2按照空格符号分割成数组结构
      buffer = new byte; // buffer数组长度就是ds的长度
      for(int i = 0; i < ds.Length; i++)
      {
            // System.Globalization.NumberStyles.HexNumber); 转成16进制数
            // 把ds转成16进制
            buffer = byte.Parse(ds,System.Globalization.NumberStyles.HexNumber);
      }
    }
    else
    {
      // 不采用16进制的心跳包
      buffer = Encoding.UTF8.GetBytes(data);

    }
    foreach(var item in lists)
    {
      item.GetStream().Write(buffer, 0, buffer.Length);
    }
} 关闭心跳

rivate void radioButton2_CheckedChanged(object sender, EventArgs e)
      {
            timer1.Stop();
      } 重启

private void button1_Click(object sender, EventArgs e)
{
    // 停掉tcp
    foreach (var item in lists)
    {
      item.Close(); // 关闭所有的客户端
    }
    listen.Stop();
    panel2.BackColor = Color.Gray;

    // 停掉窗口
    if (serialPort1.IsOpen) serialPort1.Close();
    // 灭灯
    this.panel1.BackColor = Color.Gray;

    // 开启tcp
    startTCP();
    // 开启串口
    startChuanKou();
} 生存

private void button2_Click(object sender, EventArgs e)
{
    // "Serialport", "databit","8");
    Ini.Write("Serialport","name",comboBox1.Text);
    Ini.Write("Serialport", "botelv", comboBox2.Text);
    Ini.Write("Serialport", "stopbit", comboBox5.SelectedIndex);
    Ini.Write("Serialport", "parity", comboBox4.SelectedIndex);
    Ini.Write("Serialport", "databit", comboBox3.Text); // 5 6 7 8

    Ini.Write("NetWork", "port", textBox3.Text);
    Ini.Write("NetWork", "heartOn", radioButton1.Checked);
    Ini.Write("NetWork", "heartTime", textBox4.Text);
    Ini.Write("NetWork", "heartData", textBox5.Text);
    Ini.Write("NetWork", "heartHex", checkBox1.Checked);

} 担当和发送默认值
https://img-blog.csdnimg.cn/direct/74652b05f5d3433eb94c0a39f8d1511d.png

效果如下
   QQ202473-20354

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 网口串口(Serialport)服务器