Winform实现Tcp/IP通讯协议服务端

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace server
{
    public partial class Form1 : Form
    {
        // 负责监听客户端的套接字
        Socket socket_TCP = null;
        // 负责和客户端通信的套接字
        Socket socket_Communication = null;
        //监听线程
        Thread thread_Listen = null;
        //客户端 端口号集合
        Dictionary<string, Socket> dic = new Dictionary<string, Socket>();

        public Form1()
        {
            InitializeComponent();
            getCount();
            Control.CheckForIllegalCrossThreadCalls = false;

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button_Connection_Click(object sender, EventArgs e)
        {
            if (button_Connection.Text == "连接")
            {
                button_Connection.Text = "断开";
                socket_TCP = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse(textBox_IP.Text), (int)numericUpDown_Port.Value);
                socket_TCP.Bind(iPEndPoint);
                socket_TCP.Listen(0);
                thread_Listen = new Thread(TCP_Listen);
                thread_Listen.IsBackground = true;
                thread_Listen.Start();
            }
            else if (button_Connection.Text == "断开")
            {
                button_Connection.Text = "连接";
                if (socket_Communication != null)
                {
                    socket_Communication.Close();
                }
            }
        }

        private void TCP_Listen()
        {
            while (true)
            {
                try
                {
                    socket_Communication = socket_TCP.Accept();
                    //客户端网络节点
                    string RemoteEndPoint = socket_Communication.RemoteEndPoint.ToString();
                    dic.Add(RemoteEndPoint, socket_Communication);
                    listBoxOnlineList.Items.Add(RemoteEndPoint);
                    //传参的线程
                    ParameterizedThreadStart pts = new ParameterizedThreadStart(TCP_Read);
                    Thread thread = new Thread(pts);
                    thread.IsBackground = true;
                    thread.Start(socket_Communication);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    return;
                }
            }
        }
        private void button_Send_Click(object sender, EventArgs e)
        {
            string sendMessage = textBox_Send.Text;
            Send(sendMessage);
        }
        public void Send(string message)
        {
            for (int i = 0; i < listBoxOnlineList.Items.Count; i++)
            {
                string selectClient = listBoxOnlineList.Items[i].ToString();
                TCP_Write(message, selectClient);
            }
        }
        int count;
        //得到总数
        public void getCount()
        {
            Dao dao = new Dao();
            string sql = "select count (*) from [Temp].[dbo].[Warning]";
            IDataReader dc = dao.read(sql);
            while (dc.Read())
            {
                count = int.Parse(dc[0].ToString());
            }
            dc.Close();
            dao.DaoClose();
        }

        //查询命令
        public void checkOrder()
        {
            for (int i = 1; i <= count; i++)
            {
                //查询功能命令
                string functionOrder = " 03 00 00 00 02 C4 0B ";
                //设备编号转十六进制并截取后两位
                string address = i.ToString("X6").Substring(4, 2);
                //地址码和功能码组合成不带CRC校验的命令码,再去获取对应CRC码
                string commandPro = address + functionOrder;
                //设备编号转btye[]
                byte[] address16 = HexStringToByteArray(commandPro);
                //获取设备编码的CRC校验码
                byte[] addressCRC = ToModbus(address16);
                //CRC校验码转string
                string crcpro = byteToHexStr(addressCRC);
                //获取CRC校验码的低字节位
                string crclow = crcpro.Substring(0, 2);
                //获取CRC校验码的高字节位
                string crchigh = crcpro.Substring(2, 2);
                //增加空格转为有效CRC码
                string crc = crclow + " " + crchigh;
                //地址码+功能码+CRC校验码组成最终查询命令码
                string command = address + functionOrder + crc;
                //textBox_Send.Text = command;
                Send(command);
                Thread.Sleep(2000);
            }
        }

        //插入温湿度
        public void insertTemp(string j, string now, string temp1, string hum1)
        {
            Dao dao = new Dao();
            string sql = "INSERT into [Temp].[dbo].[Temp] VALUES('" + j + "','" + now + "','" + temp1 + "','" + hum1 + "') ";
            IDataReader dc = dao.read(sql);
            dc.Close();
            dao.DaoClose();
        }

        //警告
        public void updateWarn(string j, string temp1, string hum1, string now)
        {
            Dao dao = new Dao();
            string sql = "UPDATE [Temp].[dbo].[Warning] SET nowTemp = ('" + temp1 + "') ,nowHum = ('" + hum1 + "'), Time = ('" + now + "') WHERE No = ('" + j + "')";
            IDataReader dc = dao.read(sql);
            dc.Close();
            dao.DaoClose();
        }

        private void TCP_Write(string data, string EndPoint)
        {
            //string 转换为十六进制
            byte[] array = HexStringToByteArray(data);
            dic[EndPoint].Send(array);
        }
        string temp = null;
        string hum = null;
        List<string> noList = new List<string>();
        List<string> tempList = new List<string>();
        List<string> humList = new List<string>();
        List<string> noFinList = new List<string>();
        List<string> tempFinList = new List<string>();
        List<string> humFinList = new List<string>();
        List<int> disMachine = new List<int>();
        private void TCP_Read(object socket_Read)
        {
            Socket socket = socket_Read as Socket;
            while (true)
            {
                try
                {
                    byte[] buffer_Data = new byte[1024 * 1024];
                    int Accept_length = socket.Receive(buffer_Data);
                    EndPoint endPoint = socket.RemoteEndPoint;
                    string Str_Data = Encoding.UTF8.GetString(buffer_Data, 0, buffer_Data.Length).Trim("\0".ToCharArray());
                    string message = "";
                    for (int i = 0; i <= 10; i++)
                    {
                        //提取有用十六进制数据
                        message += buffer_Data[i].ToString("X2");
                    }
                    //截取前六位判定码
                    string con = message.Substring(2, 4);
                    string nopro = message.Substring(0, 2);
                    if (con == "0304")
                    {
                        string humpro = message.Substring(6, 4);
                        string temppro = message.Substring(10, 4);
                        //十六进制转换为十进制
                        int nopro2 = int.Parse(nopro, System.Globalization.NumberStyles.AllowHexSpecifier);
                        int humpro2 = int.Parse(humpro, System.Globalization.NumberStyles.AllowHexSpecifier);
                        int temppro2 = int.Parse(temppro, System.Globalization.NumberStyles.AllowHexSpecifier);
                        //0℃之下的判断
                        if (temppro2 >= 32767)
                        {
                            temppro2 = temppro2 - 65536;
                        }
                        else
                        {
                            temppro2 = temppro2;
                        }
                        //int转换为double
                        double humpro3 = Convert.ToDouble(humpro2) * 0.1;
                        double temppro3 = Convert.ToDouble(temppro2) * 0.1;
                        //double 转string并规范格式
                        //string temppro4 = temppro3.ToString();
                        //string humpro4 = humpro3.ToString();
                        temp = string.Format("{0:00.0}", temppro3);
                        hum = string.Format("{0:00.0}", humpro3);
                        string No = string.Format("{0:00}", nopro2);


                        tempList.Add(temp);
                        humList.Add(hum);
                        noList.Add(No);

                        //把未有数据的机器筛选出来插入数据
                        bool same = false;
                        for (int a = 1; a <= tempList.Count; a++)
                        {
                            same = false;
                            string b = string.Format("{0:00}", a);
                            if (b == noList[a - 1])
                            {
                                same = true;
                                break;
                            }
                        }

                        //将未重复的数据的机器的数据插入到最终数据List中
                        if (temp != null || same == false)
                        {
                            tempFinList.Add(temp);
                            humFinList.Add(hum);
                            noFinList.Add(No);
                        }

                        if(tempFinList.Count < count)
                        {
                            for(int i = 1; i <= count; i++)
                            {
                                string j = string.Format("{0:00}", i);
                                for(int a = 1; a <= noFinList.Count; a++)
                                {
                                    bool yes = false;
                                    if (j == noFinList[a-1])
                                    {
                                        yes = true;
                                        break;
                                    }
                                    if(yes == false)
                                    {
                                        disMachine.Add(i);
                                    }
                                }
                            }
                        }

                        //richTextBox_Receive.AppendText("客户端 " + endPoint + ":" + No + "号设备当前温度为" + temp + "℃," + "湿度为" + hum + "%" + "\r\n");

                        No = null;
                        temp = null;
                        hum = null;    
                    }
                    //自己设置的网关心跳返回值
                    if (con == "204E")
                    {
                        string text = HexToString(message);
                        richTextBox_Receive.AppendText(text + "\r\n");
                    }
                }
                catch (Exception ex)
                {
                    //提示套接字监听异常 
                    richTextBox_Receive.AppendText("客户端" + socket.RemoteEndPoint + "已经中断连接" + "\r\n");
                    //移除断开的客户端
                    dic.Remove(socket.RemoteEndPoint.ToString());
                    //从listbox中移除断开连接的客户端
                    listBoxOnlineList.Items.Remove(socket.RemoteEndPoint.ToString());
                    //关闭之前accept出来的和客户端进行通信的套接字
                    //socket_Communication.Close();
                    return;
                }
            }
        }
        //字符串转16进制
        public static byte[] HexStringToByteArray(string s)
        {
            s = s.Replace(" ", "");
            byte[] buffer = new byte[s.Length / 2];
            for (int i = 0; i < s.Length; i += 2)
                buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
            return buffer;
        }
        //十六进制转换为字符串
        public static string byteToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");//ToString("X2") 转化为大写的16进制
                }
            }
            return returnStr;
        }

        /// <summary>
        /// 16进制转普通字符串
        /// </summary>
        /// <returns></returns>
        public static string HexToString(string Hexdata)
        {
            string result = string.Empty;
            byte[] arrByte = new byte[Hexdata.Length / 2];
            int index = 0;
            for (int i = 0; i < Hexdata.Length; i += 2)
            {
                arrByte[index++] = Convert.ToByte(Hexdata.Substring(i, 2), 16);
            }
            result = System.Text.Encoding.UTF8.GetString(arrByte);
            return result;
        }


        private void richTextBox_Receive_TextChanged(object sender, EventArgs e)
        {

        }
        private void textBox_Send_TextChanged(object sender, EventArgs e)
        {

        }
        private void textBox_IP_TextChanged(object sender, EventArgs e)
        {

        }
        private void numericUpDown_Port_ValueChanged(object sender, EventArgs e)
        {

        }
        private void listBoxOnlineList_SelectedIndexChanged_1(object sender, EventArgs e)
        {

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            string year = DateTime.Now.Year.ToString();
            string month = DateTime.Now.Month.ToString();
            string day = DateTime.Now.Day.ToString();
            string hour = DateTime.Now.Hour.ToString();
            string min = DateTime.Now.Minute.ToString();
            string second = DateTime.Now.Second.ToString();
            string now = year + "-" + month + "-" + day + " " + hour + ":" + min;
            if (min == "30" && second == "0")
            {
               checkOrder();
            }
                //当存在无数据的机器时,循环第二次
                if (tempFinList.Count < count)
                {
                    Thread.Sleep(60000);
                    for (int i = 1; i <= disMachine.Count; i++)
                    {
                        int a = disMachine[i - 1];
                        string j = string.Format("{0:00}", a);
                        *//*                    Thread.Sleep(5000);*//*
                        //查询功能命令
                        string functionOrder = " 03 00 00 00 02 C4 0B ";
                        //设备编号转十六进制并截取后两位
                        string address = i.ToString("X6").Substring(4, 2);
                        //地址码和功能码组合成不带CRC校验的命令码,再去获取对应CRC码
                        string commandPro = address + functionOrder;
                        //设备编号转btye[]
                        byte[] address16 = HexStringToByteArray(commandPro);
                        //获取设备编码的CRC校验码
                        byte[] addressCRC = ToModbus(address16);
                        //CRC校验码转string
                        string crcpro = byteToHexStr(addressCRC);
                        //获取CRC校验码的低字节位
                        string crclow = crcpro.Substring(0, 2);
                        //获取CRC校验码的高字节位
                        string crchigh = crcpro.Substring(2, 2);
                        //增加空格转为有效CRC码
                        string crc = crclow + " " + crchigh;
                        //地址码+功能码+CRC校验码组成最终查询命令码
                        string command = address + functionOrder + crc;
                        //textBox_Send.Text = command;
                        Send(command);
                        Thread.Sleep(5000);
                    }
                    disMachine.Clear();
                }
                //当还存在无数据的机器时,循环第三次
                if (tempFinList.Count < count)
                {
                    Thread.Sleep(60000);
                    for (int i = 1; i <= disMachine.Count; i++)
                    {
                        int a = disMachine[i - 1];
                        string j = string.Format("{0:00}", a);
                        *//*                    Thread.Sleep(5000);*//*
                        //查询功能命令
                        string functionOrder = " 03 00 00 00 02 C4 0B ";
                        //设备编号转十六进制并截取后两位
                        string address = i.ToString("X6").Substring(4, 2);
                        //地址码和功能码组合成不带CRC校验的命令码,再去获取对应CRC码
                        string commandPro = address + functionOrder;
                        //设备编号转btye[]
                        byte[] address16 = HexStringToByteArray(commandPro);
                        //获取设备编码的CRC校验码
                        byte[] addressCRC = ToModbus(address16);
                        //CRC校验码转string
                        string crcpro = byteToHexStr(addressCRC);
                        //获取CRC校验码的低字节位
                        string crclow = crcpro.Substring(0, 2);
                        //获取CRC校验码的高字节位
                        string crchigh = crcpro.Substring(2, 2);
                        //增加空格转为有效CRC码
                        string crc = crclow + " " + crchigh;
                        //地址码+功能码+CRC校验码组成最终查询命令码
                        string command = address + functionOrder + crc;
                        //textBox_Send.Text = command;
                        Send(command);
                        Thread.Sleep(5000);
                    }
                    disMachine.Clear();
                }

                //当还还还存在无数据的机器时,循环第四次
                if (tempFinList.Count < count)
                {
                    Thread.Sleep(60000);
                    for (int i = 1; i <= disMachine.Count; i++)
                    {
                        int a = disMachine[i - 1];
                        string j = string.Format("{0:00}", a);
                        *//*                    Thread.Sleep(5000);*//*
                        //查询功能命令
                        string functionOrder = " 03 00 00 00 02 C4 0B ";
                        //设备编号转十六进制并截取后两位
                        string address = i.ToString("X6").Substring(4, 2);
                        //地址码和功能码组合成不带CRC校验的命令码,再去获取对应CRC码
                        string commandPro = address + functionOrder;
                        //设备编号转btye[]
                        byte[] address16 = HexStringToByteArray(commandPro);
                        //获取设备编码的CRC校验码
                        byte[] addressCRC = ToModbus(address16);
                        //CRC校验码转string
                        string crcpro = byteToHexStr(addressCRC);
                        //获取CRC校验码的低字节位
                        string crclow = crcpro.Substring(0, 2);
                        //获取CRC校验码的高字节位
                        string crchigh = crcpro.Substring(2, 2);
                        //增加空格转为有效CRC码
                        string crc = crclow + " " + crchigh;
                        //地址码+功能码+CRC校验码组成最终查询命令码
                        string command = address + functionOrder + crc;
                        //textBox_Send.Text = command;
                        Send(command);
                        Thread.Sleep(5000);
                    }
                    disMachine.Clear();
                }

            }
            if (min == "0" && second == "0")
            {
                //Thread.Sleep(20000);
                Dao dao = new Dao();
                bool be = false;
                /*aaa 3记得改为count*/
                for (int i = 1; i <= count; i++)
                {
                    be = false;
                    string j = string.Format("{0:00}", i);
                    string address = i.ToString("X6").Substring(4, 2);
                    int z = 1;
                    //查询对应机器是否存在数据,有数据则把数据插入数据库,没有数据插入null值
                    for (z = 1; z <= tempFinList.Count; z++)
                    {
                        if (noFinList[z - 1] == j)
                        {
                            be = true;
                            string temp1 = tempFinList[z - 1];
                            string hum1 = humFinList[z - 1];
                            string sql = "INSERT into [Temp].[dbo].[Temp] VALUES('" + j + "','" + now + "','" + temp1 + "','" + hum1 + "') ";
                            string sqlUpdate = "UPDATE [Temp].[dbo].[Warning] SET nowTemp = ('" + temp1 + "') ,nowHum = ('" + hum1 + "'), Time = ('" + now + "') WHERE No = ('" + j + "')";
                            dao.read(sql);
                            dao.read(sqlUpdate);
                            break;
                        }
                    }
                    if (be == false)
                    {
                        string temp1 = null;
                        string hum1 = null;
                        string sql = "INSERT into [Temp].[dbo].[Temp] VALUES('" + j + "','" + now + "','" + temp1 + "','" + hum1 + "') ";
                        string sqlUpdate = "UPDATE [Temp].[dbo].[Warning] SET nowTemp = ('" + temp1 + "') ,nowHum = ('" + hum1 + "'), Time = ('" + now + "') WHERE No = ('" + j + "')";
                        dao.read(sql);
                        dao.read(sqlUpdate);
                    }
                }
                dao.DaoClose();
                richTextBox_Receive.AppendText("插入更新成功" + "\r\n");
                noList.Clear();
                tempList.Clear();
                humList.Clear();
                noFinList.Clear();
                tempFinList.Clear();
                humFinList.Clear();
            }

            if (hour == "0" && min == "0" && second == "0")
            {
                richTextBox_Receive.Clear();
            }
        }


        private void textBox_Send_KeyPress(object sender, KeyPressEventArgs e)
        {

        }

        /// <summary>
        /// CRC16_Modbus效验
        /// </summary>
        /// <param name="byteData">要进行计算的字节数组</param>
        /// <returns>计算后的数组</returns>
        public static byte[] ToModbus(byte[] byteData)
        {
            byte[] CRC = new byte[2];

            UInt16 wCrc = 0xFFFF;
            for (int i = 0; i < byteData.Length; i++)
            {
                wCrc ^= Convert.ToUInt16(byteData[i]);
                for (int j = 0; j < 8; j++)
                {
                    if ((wCrc & 0x0001) == 1)
                    {
                        wCrc >>= 1;
                        wCrc ^= 0xA001;//异或多项式
                    }
                    else
                    {
                        wCrc >>= 1;
                    }
                }
            }
            CRC[1] = (byte)((wCrc & 0xFF00) >> 8);//高位在后
            CRC[0] = (byte)(wCrc & 0x00FF);       //低位在前
            return CRC;
        }
    }
}

CheckedListBox不能多选,只能单选,故需要循环遍历里面所有的连接,这里设置的不进行选择也会从第一个开始逐个发送,故不需要专门选定选项。

TCP_Read里,如果出现异常,选择断开连接并关闭原有Socket,重新创建一个新的Socket进行重连,这里的异常属于正常情况,程序会显示错误但仍会继续进行,可以选择忽视。异常可能为网络问题导致的网关设备响应时间过长,系统从而判定失去连接,与天气、环境、网速都可能有关;也存在其他设备端口号相同误连的情况,会自动断开踢出无关设备。

重复发送四遍,原因与网络有关,设备响应不及时后,自动断连会中断发送,故会存在没有返回值的情况,检测没有值的数据,储存在List数组中,之后循环遍历向没有值的设备发送命令(减少流量消耗与提升效率),每遍发送之间间隔一分钟,方便断连设备重连,网卡的话可以延长每台设备发送命令的时间间隔。

timer定时器一秒执行一次,获取系统当前时间,整点插入数据库与半点执行发送命令,这里是全部执行完把数据储存在List数组统一存储更新,减少数据库打开关闭次数。

效果图

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Modbus TCP 是一种基于 TCP/IP 协议的 Modbus 应用协议的变体,它使用标准的短报文形式来传递 Modbus 通信。在 Winform实现 Modbus TCP 服务端,需要以下步骤: 1. 引入 Modbus TCP 库 在 Winform 项目中引入 Modbus TCP 库,可以选择 Modbus TCP Slave Library 或者 NModbus TCP Library。 2. 创建 Modbus TCP 服务端 使用库中提供的类创建 Modbus TCP 服务端,并指定监听端口。 ```csharp // Modbus TCP Slave Library TcpListener slaveTcpListener = new TcpListener(IPAddress.Any, 502); ModbusTcpSlave slave = new ModbusTcpSlave(1, slaveTcpListener); // NModbus TCP Library TcpListener slaveTcpListener = new TcpListener(IPAddress.Any, 502); ModbusFactory factory = new ModbusFactory(); IModbusSlaveNetwork network = factory.CreateSlaveNetwork(slaveTcpListener); IModbusSlave slave = factory.CreateSlave(1); network.AddSlave(slave); ``` 3. 添加 Modbus 数据点 使用库中提供的类创建 Modbus 数据点,并将其添加到服务端中。 ```csharp // Modbus TCP Slave Library ModbusTcpDataPoint<int> dataPoint1 = new ModbusTcpDataPoint<int>(ModbusTcpDataType.INT16, 0); slave.DataStore.AddDataPoint(dataPoint1); // NModbus TCP Library ushort[] data1 = new ushort[] { 0 }; InputRegister[] inputRegisters1 = InputRegister.CreateArray(data1); slave.DataStore.InputRegisters.WriteRange(0, inputRegisters1); ``` 4. 启动 Modbus TCP 服务端 ```csharp // Modbus TCP Slave Library slaveTcpListener.Start(); slave.Start(); // NModbus TCP Library slaveTcpListener.Start(); network.Listen(); ``` 5. 处理 Modbus TCP 请求 在 Winform 中,可以使用后台线程来处理 Modbus TCP 请求,以确保 UI 界面的响应性能。 ```csharp // Modbus TCP Slave Library Task.Run(() => { while (true) { if (slaveTcpListener.Pending()) { TcpClient client = slaveTcpListener.AcceptTcpClient(); ModbusTcpSlaveConnection connection = new ModbusTcpSlaveConnection(client); slave.AddConnection(connection); } Thread.Sleep(10); } }); // NModbus TCP Library Task.Run(() => { while (true) { if (slaveTcpListener.Pending()) { TcpClient client = slaveTcpListener.AcceptTcpClient(); IModbusSlaveConnection connection = factory.CreateSlaveConnection(client); network.AddSlaveConnection(connection); } Thread.Sleep(10); } }); ``` 以上是 Winform实现 Modbus TCP 服务端的基本步骤,具体实现还需要根据项目中的具体需求进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值