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数组统一存储更新,减少数据库打开关闭次数。
效果图