近期刚做的一个项目。关于 Socket TCP 通信。
需求方提供了一个 ARM 机器,及数据採集器,须要我做一个服务端与数据採集器进行交互。
目的:
数据採集器:定时将读取到的数据发送到服务端。
服务端:将数据採集器发送过来的数据保存在本地。
要求:
1、通信以 TCP 方式进行交互,port可配置。
2、自己实现握手、心跳包机制。
3、TCP 包结构包含:包头、有效数据总长度、有效数据、CRC 校验、包尾,当中有效数据包含指令序号和指令内容,为经过 AES 128位加密的 XML 数据。
4、加密算法:AES 128位。加密模式:CBC。填充模式:NoPadding。
5、握手过程:(下面过程均以 TCP 包结构加密形式传输,并仅仅提主要内容数据。省略 CRC 校验说明)
(1)数据採集器发送请求包到服务端,服务端解析包结构后取出数据段进行 AES 解密。
(2)服务端推断该包为请求包后回复一个包括一个随机序列的包到数据採集器。
(3)数据採集器接收到包后将随机序列与 MD5 密钥组合后进行 MD5 加密。并回发给服务端。
(4)服务端接收到加密后的内容,并与本地加密后的内容进行比較,通过则回复 pass,否则回复 fail。
(5)数据採集器收到 pass 则握手成功并建立连接,否则握手失败并断开连接。
6、心跳包:握手建立后。採集器每隔一定时间会往服务端发送一个心跳包。服务端收到心跳包后应马上应答。回复server当前时间信息供採集器校时。
7、数据包:採集器在握手建立后。会定时向服务端发送断点数据包和实时数据包,服务端接收到以后将数据保存在本地 PC 并回复 pass。
1、创建服务端Socket
(C#)
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 36829); //本机预使用的IP和port Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newsock.Bind(ipep); //绑定 newsock.Listen(10); //监听
(Node.js)
var server = net.createServer(function(socket) { //'connection' listener console.log('client connected'); }); server.listen(36829, function() { //'listening' listener console.log('server bound'); });
2、创建client对象并监听
(C#)
Console.WriteLine("waiting for a client"); Socket client = newsock.Accept(); //当有可用的客户端连接尝试时运行,并返回一个新的socket,用于与客户端之间的通信 IPEndPoint clientip = (IPEndPoint)client.RemoteEndPoint; Console.WriteLine("connect with client: " + clientip.Address + " at port: " + clientip.Port); while (true) { byte[] buffer = new byte[1024]; //用于缓存客户端所发送的信息,通过socket传递的信息必须为字节数组 int packageLength = client.Receive(buffer); //用于表示客户端发送的信息长度 if (packageLength == 0) { Console.WriteLine("解除循环监听,Disconnected from " + clientip.Address); break; //当信息长度为0。说明客户端连接断开 } }
(Node.js)
var server = net.createServer(function(socket) { //'connection' listener console.log('client connected'); socket.on('end', function() { console.log('client disconnected'); }); socket.on('data', function(data){ console.log(data.length); console.log(data); }); }); server.listen(36829, function() { //'listening' listener console.log('server bound'); });
由于其它内容涉及到保密性,所以以下仅仅贴一下 AES 加解密过程
// AES 解密 public static string AESDecrypt(byte[] data) { SymmetricAlgorithm aes = Rijndael.Create(); aes.Key = keyArray; // 密钥 aes.IV = keyArray; // 向量 aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.None; byte[] decryptBytes = new byte[data.Length]; using (MemoryStream ms = new MemoryStream(data)) { using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read)) { cs.Read(decryptBytes, 0, decryptBytes.Length); cs.Close(); ms.Close(); } } aes.Clear(); return System.Text.Encoding.Default.GetString(decryptBytes).Replace("