实例介绍
【实例简介】
【实例截图】
【核心代码】
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) 盖楼(回复)