实例介绍
【实例简介】
【实例截图】
【核心代码】
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Net; using System.Net.Sockets; using System.IO; using System.Text; using System.Configuration; using System.Collections; using System.Threading; using System.Net.NetworkInformation; using ServerForClientRms.Common; using ServerForClientRms.Model; using System.IO.Ports; using Modbus.Device; using ServerForClientRms.Dao; using System.Data; namespace ServerForClientRms { public static class SocketController { public static SaveData save; // private static ReceiveController rvController; private static List<byte[]> list = new List<byte[]>(); static ValidateStatus vaiStatus = new ValidateStatus(); static LogClass loginfo = new LogClass(); //static MachineState mState = new MachineState(); //const int BufferSize = 8192;//缓存大小,8192字节 static StringBuilder sbu = new StringBuilder(); //SerialPort serialPort = new SerialPort(); public static void init() { Console.WriteLine("初始化服务..."); //if (save == null) //{ // save = new SaveData(); // rvController = new ReceiveController(save); //} Console.WriteLine("初始化完毕"); } public static void StartServer() { Console.WriteLine("正在启动服务..."); Socket newsoc = null; try { // TcpClient tcpClient = new TcpClient(); IPAddress _ip = IPAddress.Any; newsoc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newsoc.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); //IPEndPoint locaEp = new IPEndPoint(IPAddress.Any, 8109);//建立连接 IPEndPoint locaEp = new IPEndPoint(IPAddress.Any, 8109); // tcpClient.Connect(IPAddress.Any, 8109); newsoc.Bind(locaEp); newsoc.Listen(500); Console.WriteLine("服务已启动 8109端口 开始监听"); Socket nowClient = null; while (true) { // 当侦听到一个客户端上线后,更新socketUser[],并给此客户端开一个接受信息的线程 //Console.WriteLine("机器已连接:可以开始发送数据"); nowClient = newsoc.Accept(); // 开一个线程, 接受 nowClient 发来的信息。 // 这里调用的方法为有参数的方法, 需要使用委托。 ThreadPool.QueueUserWorkItem(new WaitCallback(ClientRecieveData), nowClient); Thread.Sleep(100); } } catch (Exception e) { loginfo.WirteError("Socket服务器监听出错,异常退出:" e.ToString()); try { if (newsoc != null) { newsoc.Close(); newsoc.Dispose(); } } catch (Exception ex) { //LogClass.WirteError("关闭Socket资源出错:" ex.ToString()); } GC.Collect(); Thread.Sleep(2000); StartServer(); } } static string devno = string.Empty; static ModbusCommon md = new ModbusCommon(); static int addressNo = 0; static string recipeNo = string.Empty; private static void ClientRecieveData(object client) { Socket clent = (Socket)client; //ModbusIpMaster master = ModbusIpMaster.CreateIp(clent); int re = 0; string msg=string.Empty;//下发配方字符串 byte[] comes = new byte[500]; try { while (true) { re = clent.Receive(comes); string recvStr = string.Empty; if ("3030303030303030303031".Equals(StringUtils.ByteToHex(comes, 0, 11))) { recvStr = StringUtils.ByteToHex(comes, 11, re); //comes = comes.Skip(11).Take(re-11).ToArray();//不能处理,否则无法接收数据 } #region MyRegion else if ("FF2C".Equals(StringUtils.ByteToHex(comes, 0, 2)) && comes[1] == 44) { recvStr = Encoding.Default.GetString(comes, 9, re); //comes = comes.Skip(9).Take(re - 9).ToArray(); } else { recvStr = StringUtils.ByteToHex(comes, 0, re); } if (recvStr.Contains("00000000001")) { recvStr = recvStr.Replace("00000000001", ""); //comes = comes.Skip(10).Take(re - 11).ToArray(); } if (recvStr.Contains("3030303030303030303031")) { recvStr = recvStr.Replace("3030303030303030303031", ""); //comes = comes.Skip(11).Take(re - 11).ToArray(); } if (recvStr.Contains("676A")) { //recvStr = Encoding.Default.GetString(comes, 0, re); //01命令解析乱码 recvStr = recvStr.Replace("676A", ""); } if (recvStr.Contains("gj")) { recvStr = recvStr.Replace("gj", ""); } #endregion #region modbus协议 #region modbus功能码 /* 01 读取线圈状态 取得一组逻辑线圈的当前状态(ON/OFF) * 02 读取输入状态 取得一组开关输入的当前状态(ON/OFF) * 03 读取保持寄存器 在一个或多个保持寄存器中取得当前的二进制值 * 04 读取输入寄存器 在一个或多个输入寄存器中取得当前的二进制值 * 05 强置单线圈 强置一个逻辑线圈的通断状态 * 06 预置单寄存器 把具体二进值装入一个保持寄存器 * 07 读取异常状态 取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态 * 08 回送诊断校验 把诊断校验报文送从机,以对通信处理进行评鉴 * 09 编程(只用于484) 使主机模拟编程器作用,修改PC从机逻辑 * 10 控询(只用于484) 可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送 * 11 读取事件计数 可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时 * 12 读取通信事件记录 可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误 * 13 编程(184/384 484 584) 可使主机模拟编程器功能修改PC从机逻辑 * 14 探询(184/384 484 584 可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送 * 15 强置多线圈 强置一串连续逻辑线圈的通断 * 16 预置多寄存器 把具体的二进制值装入一串连续的保持寄存器 * 17 报告从机标识 可使主机判断编址从机的类型及该从机运行指示灯的状态 * 18 (884和MICRO 84) 可使主机模拟编程功能,修改PC状态逻辑 * 19 重置通信链路 发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 * 20 读取通用参数(584L) 显示扩展存储器文件中的数据信息 * 21 写入通用参数(584L) 把通用参数写入扩展存储文件,或修改之 * 22~64 保留作扩展功能备用 * 65~72 保留以备用户功能所用 留作用户功能的扩展编码 * 73~119 非法功能 * 120~127 保留 留作内部作用 * 128~255 保留 用于异常应答 */ #endregion #region RTU //读指令 /* 01 地址码 * 03 功能码 * 01 8E 寄存器地址 1001 * 00 04 寄存器数量 * 25 DE CRC校验 */ //写指令 /* 01 地址码 * 10 功能码 * 01 8E 寄存器地址 * 00 01 寄存器数量 * 02 数据长度 * 00 00 正文 * A8 7E CRC校验 */ #endregion #region 请求 06写单个寄存器 03读多个寄存器 /*请求:00 00 00 00 00 06 09 03 00 00 00 01 * 00 00为此次通信事务处理标识符,一般每次通信之后将被要求加1以区别不同的通信数据报文; * 00 00表示协议标识符,00 00为modbus协议; * 00 06为数据长度,用来指示接下来数据的长度,单位字节; * 09为设备地址,用以标识连接在串行线或者网络上的远程服务端的地址。以上七个字节也被称为modbus报文头; * 03为功能码,此时代码03为读取保持寄存器数据; * 00 00为起始地址; * 00 01为寄存器数量,(word数量)。 */ #endregion #region 响应 /* * 响应:00 00 00 00 00 05 09 03 02 12 34 * 00 00为此次通信事务处理标识符,应答报文要求与先前对应的请求保持一致; * 00 00为协议标识符,与先前对应的请求保持一致; * 00 05为数据长度,用来指示接下来数据的长度,单位字节; * 09为设备地址,应答报文要求与先前对应的请求保持一致; * 03为功能码,正常情况下应答报文要求与先前对应的请求保持一致,如果出错则返回80h 先前的功能码; * 02指示接下来数据的字节长度; * 12 34为被读取的保持寄存器中的数据值,即要求被读取的地址为00 00的保持寄存器中的数值为1234h。 */ #endregion #endregion #region 根据modbus获取和写入数据 //获取到空数据,发送获取请求 if (string.IsNullOrEmpty(recvStr)) { //Tx:000081-01 03 33 F2 00 01 2A BD //Rx:000077-01 03 33 F2 00 01 50 98 //01 03 02 00 24 B8 5F #region 获取设备编码 // 13292 13299-1=13298 33f2 byte[] sendMsg = new byte[] { }; addressNo = 13300; sendMsg = md.FirstGetDev(0x03, 0x33, 0xf3); //功能 地址 //e00a 12 send: 01 01 E0 12 00 01 6A0F //13 send: 01 01 E0 13 00 01 3B CF // sendMsg = md.FirstGetDev(0x01, 0xE0, 0x13); clent.Send(sendMsg); loginfo.WirteInfo(byteToHexStr(sendMsg)); #endregion #region 申请任务号 //byte[] sendbyte = new byte[12]; //sendbyte[5] = Convert.ToByte(06); //数据长度 //sendbyte[6] = Convert.ToByte(00); //sendbyte[7] = Convert.ToByte(3);//功能码 //sendbyte[9] = Convert.ToByte(13292);//为起始地址 //sendbyte[11] = Convert.ToByte(1);// 为寄存器数量 //clent.Send(sendbyte); #endregion } #endregion else { if (string.IsNullOrEmpty(devno) && addressNo == 13300) { Console.WriteLine(recvStr); //byte[] bytere = new byte[recvStr.Length / 2]; //bytere = Encoding.Default.GetBytes(recvStr); //设备编号返回:01 03 02 00 24 B8 5F 0103020024B85F // byte[] bytere = Encoding.Default.GetBytes(recvStr); //recvStr = Encoding.Default.GetString(bytere); devno = Convert.ToInt32(recvStr.Substring(8, 2),16).ToString(); } #region s10 else if (!string.IsNullOrEmpty(devno) && addressNo == 13300) { addressNo = 57354;//s10 byte[] taskreadyNum = new byte[] { }; taskreadyNum = md.FirstGetDev(0x01, 0xe0, 0x0a); clent.Send(taskreadyNum); loginfo.WirteInfo("s10 : " byteToHexStr(taskreadyNum)); } #endregion #region 获取任务号, // devno.Substring(1,1) 区别类型 else if (!string.IsNullOrEmpty(devno) && addressNo == 57354) { if (Convert.ToInt32(recvStr.Substring(6, 2), 16) == 1) //s10 { //workarea获取完成数 基数 byte[] sendMsg = new byte[] { }; //14364 --14371 byte[] btadd = new byte[] { };//地址 btadd[0] = 0xe0; btadd[1] = 0x14; byte[] btnum = new byte[] { };//数量 btnum[0] = 0x00; btnum[1] = 0x09; sendMsg = md.FirstGetDev(0x03, btadd, btnum); clent.Send(sendMsg); loginfo.WirteInfo("Q_获取任务号 : " byteToHexStr(sendMsg)); addressNo = 14364; } else { //s20 线圈 01 byte[] reTaskNo = new byte[] { }; reTaskNo = md.FirstGetDev(0x01, 0xe0, 0x14); clent.Send(reTaskNo); loginfo.WirteInfo("Q_s20 : " byteToHexStr(reTaskNo)); addressNo = 57364; } } else if (!string.IsNullOrEmpty(devno) && addressNo == 14364) { //接收工作区数据 //recvStr 接收的字符串 dao.InsertTask(recvStr); //获取s20 byte[] reTaskNo = new byte[] { }; reTaskNo = md.FirstGetDev(0x01, 0xe0, 0x14); clent.Send(reTaskNo); loginfo.WirteInfo("Q_s20 : " byteToHexStr(reTaskNo)); addressNo = 57364; } else if (!string.IsNullOrEmpty(devno) && addressNo == 57364)//获取任务部分 { if (Convert.ToInt32(recvStr.Substring(6, 2), 16) == 1) //获取1004rw { byte[] sendMsg = new byte[] { }; addressNo = 13292; //sendMsg = md.FirstGetDev(0x03, 0x33, 0xeb); //功能 地址 sendMsg = md.FirstGetDev(0x03, 0x33, 0xec); clent.Send(sendMsg); loginfo.WirteInfo("Q_获取任务号 : " byteToHexStr(sendMsg)); } else { //Thread.Sleep(1000); byte[] sendMsg = new byte[] { }; addressNo = 57364; sendMsg = md.FirstGetDev(0x01, 0xe0, 0x14); //功能 地址 clent.Send(sendMsg); loginfo.WirteInfo("E_未获取到任务号 : " byteToHexStr(sendMsg)); } } else if (!string.IsNullOrEmpty(devno) && addressNo == 13292)//写入下载区 { recipeNo = StringUtils.HexStringToString(recvStr.Substring(8, 2), Encoding.Default); //s查询 dt = dao.GetChartRecipe(recipeNo, devno); //查询的数据 if (dt.Rows.Count > 0) { //s21 1 下载区写入 06 string[] stru = dt.Rows[0]["imglist"].ToString().Split(','); byte[] sendmsg = new byte[stru.Length 9]; int startadd = 14288; for (int i = 0; i < stru.Length; i ) { //写入方法 sendmsg[i] = Convert.ToByte("0x" stru[i], 16); } /*任务号 76-77 * 设定数 78-79 * 完成数 80-81 * 直径 82 * 基数 83 * 钢筋级别 84 * length 84 1D 54H */ byte[] tasksend = md.int2Bytes(Convert.ToInt32(dt.Rows[0]["id"].ToString()), 2); sendmsg[stru.Length] = tasksend[0];//任务号 32uint sendmsg[stru.Length 1] = tasksend[1]; //任务设定数 32 byte[] taskman = md.int2Bytes(Convert.ToInt32(dt.Rows[0][""].ToString()), 2); sendmsg[stru.Length 2] = taskman[0]; sendmsg[stru.Length 3] = taskman[1]; //任务完成数 32 byte[] taskfinsh = md.int2Bytes(Convert.ToInt32(dt.Rows[0][""].ToString()), 2); sendmsg[stru.Length 4] = taskfinsh[0]; sendmsg[stru.Length 5] = taskfinsh[1]; //钢筋直径 16 sendmsg[stru.Length 6] = Convert.ToByte(dt.Rows[0][""].ToString(), 16); //基数 sendmsg[stru.Length 7] = Convert.ToByte(dt.Rows[0][""].ToString(), 16); //钢筋级别 sendmsg[stru.Length 8] = Convert.ToByte(dt.Rows[0][""].ToString(), 16); addressNo = 57366; byte[] bstad=md.int2Bytes(startadd,2); byte[] bynum = new byte[2]; bynum[0] = 0x00; bynum[1] = 0x53; sendmsg = md.WriteModbus(0x10, bstad,bynum,0x54,sendmsg); clent.Send(sendmsg); loginfo.WirteInfo("R_下载区写入 : " byteToHexStr(sendmsg)); } else { /* 01 地址码 * 10 功能码 * 01 8E 寄存器地址 * 00 01 寄存器数量 * 02 数据长度 * 00 00 正文 * A8 7E CRC校验 */ //s22 xieru 1 0 线圈 byte[] sendMsg = new byte[8]; addressNo = 57366; // sendMsg=md.WriteModbus(0x05,BitConverter.GetBytes(57366),0x01); sendMsg[0] = 0x01; sendMsg[1] = 0x05; sendMsg[2] = 0xe0; sendMsg[3] = 0x16; sendMsg[4] = 0x00; sendMsg[5] = 0x01; byte[] crc = BitConverter.GetBytes(GetCRC16(sendMsg)); sendMsg[6] = crc[0]; sendMsg[7] = crc[1]; //sendMsg = md.FirstGetDev(0x03, 0x33, 0xeb); //功能 地址 //sendMsg = md.FirstGetDev(0x05, 0xe0, 0x16,0x00,0x01,0x01,); //写入命令 loginfo.WirteInfo("E_s22 : " byteToHexStr(sendMsg)); clent.Send(sendMsg); } } #endregion } Thread.Sleep(100); } } catch (Exception ex) { loginfo.WirteError(ex.Message); return; } finally { clent.Close(); clent.Dispose(); clent = null; GC.Collect(); } } static DataTable dt = new DataTable(); static InsertDao dao = new InsertDao(); #region CRC16 Modbus public static uint GetCRC16(byte[] crc_array) { uint i, j; uint modbus_crc; modbus_crc = 0xffff; for (i = 0; i < crc_array.Length - 2; i ) { modbus_crc = (modbus_crc & 0xFF00) | ((modbus_crc & 0x00FF) ^ crc_array[i]); for (j = 1; j <= 8; j ) { if ((modbus_crc & 0x01) == 1) { modbus_crc = (modbus_crc >> 1); modbus_crc ^= 0XA001; } else { modbus_crc = (modbus_crc >> 1); } } } return modbus_crc; } #endregion public static string byteToHexStr(byte[] bytes) { string returnStr = ""; if (bytes != null) { for (int i = 0; i < bytes.Length; i ) { returnStr = bytes[i].ToString("X2"); } } return returnStr; } /// <summary> /// 验证数据是否完整,并返回完整数据的长度 /// </summary> /// <param name="data">待验证的二进制</param> /// <returns>完整数据的长度</returns> private static void ValidateAndSave(MyBytes m, byte[] data, Socket client) { //string str = StringUtils.ToHexString(data); //int newStarIndex = str.IndexOf(ReceiveController.STARTBS); //if (newStarIndex > 0) //{ // ValidateAndSave(m, StringUtils.CopyByte(data, newStarIndex / 2, data.Length), client); //} //else //{ // newStarIndex = str.IndexOf("AAAAAAAA"); // if (newStarIndex > 0) // { // ValidateAndSave(m, StringUtils.CopyByte(data, newStarIndex / 2, data.Length), client); // } //} #region //if (data==null || data.Length == 0) //{ // LogClass.WirteInfo("退出数据处理。无剩余字节"); // return; //} ////判断是否完整 开头 数位 校验位验证 理想接收效果 //string strHead = StringUtils.ByteToHex(data, 0, 4); //if (ReceiveController.STARTHEAD.Equals(strHead)) { // //获取后续数位 占2个字节 头占4个字节 设备编号2个字节 // int datalen = BitConverter.ToInt16(data, 6); // string dataType = StringUtils.ByteToHex(data[9]); // //获取校验位 占2个字节 // if (data.Length >= datalen 10) //头:4字节 设备编号:2字节 后续位数:2字节 校验位:2字节 // { // LogClass.WirteInfo("后续字节位数:" datalen); // byte[] check = GetCheckCRCSum(data, 0, datalen 8); //CRC校验和 // int checksum = checkSum(data, 4, datalen 8); // int ch = BitConverter.ToInt16(new byte[] { data[datalen 8], data[datalen 9] }, 0); // if ((checksum == ch) || (check[0] == data[datalen 9] && check[1] == data[datalen 8])) //校验和 及长度判断是否满足协议 // { // //获取设备编号 // int num = BitConverter.ToInt16(data, 4); // m.equimentNum = num.ToString("D4"); // //存储到业务数据缓存 // //业务数据分析处理 // m.bytesList.Add(StringUtils.CopyByte(data, 0, datalen 10)); // LogClass.WirteInfo("进行数据处理"); // rvController.Recivce(m, true); // } // else // { // Console.WriteLine("回复:" ); // } // //递归 // ValidateAndSave(m, StringUtils.CopyByte(data, datalen 10, data.Length), client); // } // else // { // LogClass.WirteInfo("退出数据处理剩余字节:" data.Length); // m.AddBytes(data, data.Length); //缓存接收的数据 // } //} #endregion } /// <summary> /// checkSum /// </summary> /// <param name="memorySpage"></param> /// <param name="startIndex"></param> /// <param name="endIndex"></param> /// <returns></returns> public static int checkSum(byte[] memorySpage, int startIndex, int endIndex) { int num = 0; for (int i = startIndex; i < endIndex; i ) { num = (num memorySpage[i]) % 0xffff; } //实际上num 这里已经是结果了,如果只是取int 可以直接返回了 memorySpage = BitConverter.GetBytes(num); //返回累加校验和 return BitConverter.ToInt16(new byte[] { memorySpage[0], memorySpage[1] }, 0); } public static byte[] CopyByte(byte[] oldBytes, int startLength, int endlength) { byte[] newBytes = new byte[endlength - startLength]; int idx = 0; for (int i = startLength; i < endlength; i ) { newBytes[idx ] = oldBytes[i]; } return newBytes; } public static string ToHexString(byte[] bytes) // 0xae00cf => "AE00CF " { string hexString = string.Empty; if (bytes != null) { StringBuilder strB = new StringBuilder(); for (int i = 0; i < bytes.Length; i ) { strB.Append(bytes[i].ToString("X2")); //十进制转换为十六进制两位字符串("X2") i=1;i.ToString("X3");输出"001" } hexString = strB.ToString(); } return hexString; } /// <summary> /// 计算CRC累加校验和 /// </summary> /// <param name="memorySpage"></param> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> public static byte[] GetCheckCRCSum(byte[] memorySpage, int start, int end) { ushort num = StringUtils.CRC16(memorySpage, start, end); string s = num.ToString("X4"); //转16进制4位字符 return StringUtils.GetBytes(s); } /// <summary> /// 计算累加校验和 /// </summary> /// <param name="memorySpage"></param> /// <param name="start"></param> /// <param name="end"></param> /// <returns></returns> public static byte[] GetCheckSum(byte[] memorySpage, int start, int end) { int num = 0; for (int i = start; i < end; i ) { num = (num memorySpage[i]) % 0xffff; } //实际上num 这里已经是结果了,如果只是取int 可以直接返回了 return BitConverter.GetBytes(Int16.Parse(num "")); } } }
好例子网口号:伸出你的我的手 — 分享!
网友评论
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
支持(0) 盖楼(回复)