添加TCP通讯功能(接收) - (第十三讲)
视频讲解如下:
工程源码下载:GPS定位系统系列教程源码下载
在上一节中,我们一家把当前项目需要的通讯协议格式制定好了,接下来就是教大家如何用程序来实现我们的通讯。
当前章节是要实现后端部分的TCP通讯,这里需要集成一个自定义的库“Link”,该库的架构可以参考《如何实现TCP通信》章节。该章节的基本架构和自定义库“Link”的架构是一样的。当然,你也可以直接去集成《如何实现TCP通信》章节的功能模块。
在本章节中,ASP.Net主要是作为TCP Server模式来运行,这样可以确保我们的服务器能够对接多台下位机设备,Link库的添加方法和DbEntity库的添加方法一样,同样是通过新建工程的方式添加,然后将《如何实现TCP通信》章节的代码拷贝进去。
关于自定义的“Link”库,在工程源码里面都有集成,对于初学者来说,这个库可能会比较复杂,可以先尝试去集成《如何实现TCP通信》章节的功能模块,再回头来看“Link”库,可能就会简单一些了,“Link”库的目录结构说明如下,新建过程我这里就不讲解了,我这里只教大家如何使用这个Link库。
“Link”库的目录结构说明如下:

其中GpsModel.cs文件和GpsModel目录属于成对出现的,表示一种类型的下位机设备,这里注意了,是同类型,比如GPS的一组、温湿度传感器的一组,以此类推。
我们这里采用的校验算法是和校验,代码如下:
/// <summary>
/// 和校验
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public static byte GetSum(this byte[] date)
{
int value = 0;
if (date.Length > 0)
{
for (int i = 0; i < date.Length; i++)
{
value ^= date[i];
}
}
return (byte)(value & 0x00FF);
}
数据包的解析和组装都在Package.cs文件中。
数据包组装方法如下:
/// <summary>
/// 按标准格式组装成数据包,并返回
/// </summary>
/// <param name="equi">当前的设备类型</param>
/// <param name="equiNo">设备号</param>
/// <param name="pro">功能码</param>
/// <param name="cont">数据</param>
public Package(byte equi, byte[] equiNo, byte pro, byte[] cont)
{
// 数据格式:网络识别码 + 设备类型 + 设备号 + 包号 + 功能码 + 内容 + 校验码
// 网络识别码
netID = Res.NetID;
// 当前的设备类型
equipment = equi;
// 设备号
equipmentNo = equiNo;
// 包号
packageNo = Res.GetPackageNo();
// 功能码
program = pro;
// 内容
context = cont;
// 组装成list
List<byte> b = new List<byte>();
b.Add(netID);
b.Add(equipment);
b.AddRange(equipmentNo);
b.AddRange(packageNo);
b.Add(program);
b.AddRange(context);
// 校验
sum = b.ToArray().GetSum();
b.Add(sum);
Data = b.ToArray();
}
数据包解析方法如下:
/// <summary>
/// 按标准格式解析接收到的数据包
/// </summary>
/// <param name=""></param>
public Package(byte[] bytes)
{
try
{
Data = bytes;
int len = bytes.Length;
netID = bytes[0]; // 网络识别码
if (netID != Res.NetID) { errcode = $"网络设备号错误:{netID}"; return; }
equipment = bytes[1]; // 设备类型
equipmentNo = bytes.Skip(2).Take(2).ToArray(); // 设备号
packageNo = bytes.Skip(4).Take(2).ToArray(); // 包号
program = bytes[6]; // 功能码
context = bytes.Skip(7).Take(len - 8).ToArray(); // 数据
sum = bytes.Last();
byte s = (bytes.Take(len - 1).ToArray()).GetSum();
if (s != sum) errcode = "和校验失败";
}
catch
{
errcode = "数据包解析失败!";
}
}
当集成完“Link”库后,需要在我们的工程中引用“Link”,如下:

然后修改ServerInit.cs文件,初始化我们的TCP Server,修改如下:
using DbEntity;
using Link;
using WebApplicationGPS.APP;
using WebApplicationGPS.comm;
namespace WebApplicationGPS
{
public class ServerInit
{
public ServerInit(string sql)
{
Res.SqlStr = sql;
Res.MyDb = new MyDbContext() { config = sql };
LogHelper.Start(); // 启动日志系统
DbInit(); // 启动数据库
AppStart(); // 启动后台应用程序
EventInit(); // 通信数据事件
LinkInit(); // 启动TCP Server
}
/// <summary>
/// 启动数据库
/// </summary>
private void DbInit()
{
try
{
Res.MyDb.Database.EnsureCreated();
}
catch (Exception e)
{
LogHelper.WriteLog(e.Message);
}
}
/// <summary>
/// 启动后台应用
/// </summary>
private void AppStart()
{
new Thread(() =>
{
Service app = new Service();
while (true)
{
app.TaskService();
Thread.Sleep(1000);
}
})
{ IsBackground = true }.Start();
}
/// <summary>
/// 启动TCP Server
/// </summary>
private void LinkInit()
{
// 定义一个设备
Link.Res.Socs.Add(new Link.Equipment.GpsModel() { equi = 0x01, equino = 1.IntToByte() });
// 启动Socket Server,端口是2233
Link.Res.TSC = new Link.Sockets.TcpSocketServer(2233);
Link.Res.TSC.HandleRecMsg = new Link.Equipment.Server().Calculation;
Link.Res.TSC.HandleClientClose = new Link.Equipment.Server().ClientDisconnect;
Link.Res.TSC.StartServer();
}
/// <summary>
/// 事件初始化
/// </summary>
private void EventInit()
{
// 保存传感器数据
Link.Res.SenseDataMsg = (a, b) =>
{
System.Diagnostics.Debug.WriteLine($"接收到数据:{b}");
};
// 后面可以扩展
/* ... */
}
}
}
修改完,我们就可以直接使用网络调试助手,发送我们的数据包了,最终测结果如下:
测试包:CC 01 00 01 00 01 11 33 34 35 36 39 E1
