交通运输部·车载导航系统——终端如何与服务器通信——玩转通信协议(源码下载)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

一.引子与协议说明

      之前开发了一个项目——车载导航系统。遇到的第一个问题就是硬件设备如何与服务器通信。

      关键在于通信协议!

      众所周知:要想实现通信,首先通信双方就要达成通信协议。

      话不多说,且看协议:

      

   

 

   

 ————————————————华丽的分割线—————————————————

      以上的这些协议说明是不是看得很头大呢?

      遵循如此这般的通信协议的硬件设备又如何才能与服务器以及PC顺利通信呢?

      还请各位看官稍安勿躁!且听我娓娓道来!       

二.基础知识-TCP与粘包     

     我们都知道,互联网的核心是TCP/IP协议簇。其中TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。另外我们大概都听过一个词,叫做“粘包”,然而很多人对其内涵不甚了了。其实,粘包问题和TCP密切相关。因为TCP是面向连接的而且基于字节流,我们可以用一根水管来比喻TCP的工作方式。

      

      字节流就跟水流一样,当两个消息一起读取时,你无法分别出二者的边界。 

三.粘包的解决-消息定界

     为了解决粘包问题,就需要对消息定界。

方法1:文本协议模式

方法2:二进制协议模式

     文本协议模式通过在消息尾部加上特殊的标志来作为划分消息的依据;二进制协议则将消息封装成消息头+消息体,通过解析定长消息头,然后从消息头中取得消息体长度,进一步解析出消息体——从而粘包的问题得到了解决。 

四.回到问题-协议选择

 

    以上是硬件设备的消息结构,从中我们既能够看到文本协议的影子也能够看到二进制协议的影子。

    因为含有标志位,所以可以采用文本协议。

    然后我们将开头的标志位作为消息头的一部分,剩下的部分都当成消息体,那么就是一个二进制协议的形式。二进制协议的两个要求是:1.消息头定长 2.消息头中能解析出消息体长度。而这个消息结构是满足这个要求的。

  

五.文本协议实现示例

复制代码
复制代码
    // 摘要:
    // 文本协议助手接口。
    public interface ITextContractHelper
    {
        // 摘要:
        // 消息结束标识符(经过编码后得到的字节数组)的集合。
        // 比如一般应用使、用"\0"作为消息结束标志,那么,集合中只有一个元素("\0"的二进制)。
        // 有的应用可能有多个标识符(如"\0"、"\n"及其它)都可以作为消息的结束标志,则集合中就有多个元素。
        // 如果设置为null,引擎则不进行消息完整性识别及构造,每次接收到数据,就直接触发MessageReceived事件。
        List<byte[]> EndTokens { get; }
    }
复制代码
复制代码

文本协议助手接口定义了采用文本协议的最基本的规范——具备消息结束符。接下来我们来看该接口的一个简单的实现。

复制代码
复制代码
    public class DefaultTextContractHelper : ITextContractHelper
    {        
        public DefaultTextContractHelper(params string[] endTokenStrs)
        {
            this.endTokens = new List<byte[]>(); ;
            if (endTokenStrs == null || endTokenStrs.Length == 0)
            {
                return;
            }

            foreach (string str in endTokenStrs)
            {
                this.endTokens.Add(System.Text.Encoding.UTF8.GetBytes(str));
            }
        }

        private List<byte[]> endTokens;
        public List<byte[]> EndTokens
        {
            get
            {
                return this.endTokens;
            }
        }
    }
复制代码
复制代码

其实文本协议的本质就是:消息的结束符经过编码(比如UTF-8)后得到的字节数组作为字节流中识别消息边界的标志。

我们将其注入通信引擎,引擎即可根据我们设置的标志来分割出一个个消息。

  //初始化并启动客户端引擎(TCP、文本协议)
   this.tcpPassiveEngine = NetworkEngineFactory.CreateTextTcpPassiveEngine(this.textBox_IP.Text, int.Parse(this.textBox_port.Text), new DefaultTextContractHelper("\0"));

Demo中用“\0”来作为消息结束标志。我们知道,消息结束符是我们人为的加到消息尾部,而真正的消息是不具备这样的内容的。所以在收到消息时我们需要剔除这个结束符,也就是解封。

 void tcpPassiveEngine_MessageReceived(System.Net.IPEndPoint serverIPE, byte[] bMsg)
{
    string msg = System.Text.Encoding.UTF8.GetString(bMsg); //消息使用UTF-8编码
    msg = msg.Substring(0, msg.Length - 1); //将结束标记"\0"剔除
    this.ShowMessage(msg);
}

 附:文本协议demo源码下载

 

六.二进制协议Demo实现示例 

复制代码
复制代码
    // 摘要:
    //     二进制协议助手接口。
    public interface IStreamContractHelper
    {
        // 摘要:
        //     消息头的长度。
        int MessageHeaderLength { get; }

        // 摘要:
        //     从消息头中解析出消息体的长度(注意,不是整个消息的长度,而是不包含消息头的Body的长度)。
        //
        // 参数:
        //   head:
        //     完整的消息头,长度固定为MessageHeaderLength
        int ParseMessageBodyLength(byte[] head);
    }
复制代码
复制代码

文本协议助手接口定义了采用文本协议的最基本的规范——消息头定长,从消息头中能够解析出消息体长度。接下来我们看一个该接口的简单实现:消息头规定为8个字节,其中头4个字节存放一个int类型的消息体长度。

复制代码
复制代码
    public class StreamContractHelper : IStreamContractHelper
    {
        public int MessageHeaderLength
        {
            get { return 8; }
        }

        public int ParseMessageBodyLength(byte[] head)
        {
            return BitConverter.ToInt32(head, 0);
        }
    }
复制代码
复制代码

我们将其注入通信引擎,引擎即可识别消息头、取出消息体长度来分割出一个个消息。

 //初始化并启动客户端引擎(TCP、文本协议)
this.tcpPassiveEngine = NetworkEngineFactory.CreateStreamTcpPassivEngine(this.textBox_IP.Text, int.Parse(this.textBox_port.Text), new StreamContractHelper());

附:二进制协议demo源码下载

 

七.总结

    本文旨在介绍文本协议设计的一般方法,通过对于文本协议与二进制协议的本质的掌握,大家就能根据实际的需要来针对性的实现其通信协议了。大家可以根据这个一般的方法自己来实现一开始我给出来的卫星定位系统的通信协议。

    有许多需要与硬件设备通信的应用,诸如与GPS设备通信、与工厂车间的一些单片机设备通信、与物联网设备通信等等,大家只要掌握了这些设备的通信协议,或采用文本协议或采用二进制协议,将其实现,那么通信的问题就迎刃而解了!

    最后,随着HTML5 WebSocket技术的日益成熟与普及,B/S架构的应用于C/S架构的应用的通信也逐渐走向大一统。有兴趣的朋友可以参考:打通B/S与C/S!让HTML5 WebSocket与.NET Socket共用一个服务端!

目录
相关文章
|
4月前
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
83 0
|
2月前
|
Linux
Linux 服务器下载百度网盘文件
本教程指导如何使用 `bypy` 库从百度网盘下载文件。首先通过 `pip install bypy` 安装库,接着运行 `bypy info` 获取登录链接并完成授权,最后将文件置于指定目录并通过 `bypy downdir /Ziya-13b-v1` 命令下载至本地。
44 1
Linux 服务器下载百度网盘文件
|
2月前
|
API Windows
揭秘网络通信的魔法:Win32多线程技术如何让服务器化身超级英雄,同时与成千上万客户端对话!
【8月更文挑战第16天】在网络编程中,客户/服务器模型让客户端向服务器发送请求并接收响应。Win32 API支持在Windows上构建此类应用。首先要初始化网络环境并通过`socket`函数创建套接字。服务器需绑定地址和端口,使用`bind`和`listen`函数准备接收连接。对每个客户端调用`accept`函数并在新线程中处理。客户端则通过`connect`建立连接,双方可通过`send`和`recv`交换数据。多线程提升服务器处理能力,确保高效响应。
41 6
|
2月前
|
网络协议 安全 Unix
6! 用Python脚本演示TCP 服务器与客户端通信过程!
6! 用Python脚本演示TCP 服务器与客户端通信过程!
|
2月前
|
运维 安全 网络安全
运维笔记:基于阿里云跨地域服务器通信
运维笔记:基于阿里云跨地域服务器通信
83 1
|
2月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
70 0
|
2月前
|
Java
Java模拟文件发送给服务器,服务器将文件转发给其他用户,并保存到服务器本地,其他用户可以接收,并保存到本地磁盘,支持各种文件格式,并解决通信中服务器怎么区分客户端发来的文件类型
Java模拟文件发送给服务器,服务器将文件转发给其他用户,并保存到服务器本地,其他用户可以接收,并保存到本地磁盘,支持各种文件格式,并解决通信中服务器怎么区分客户端发来的文件类型
|
3月前
|
SQL DataWorks Java
DataWorks操作报错合集之在与某个数据库服务器建立或保持通信连接时遇到报错,该如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
2月前
|
安全 应用服务中间件 Linux
nginx搭建静态文件下载服务器
nginx搭建静态文件下载服务器
155 0
|
3月前
|
弹性计算 云计算
云服务器 ECS产品使用问题之如何将据点终端清除
云服务器ECS(Elastic Compute Service)是各大云服务商阿里云提供的一种基础云计算服务,它允许用户租用云端计算资源来部署和运行各种应用程序。以下是一个关于如何使用ECS产品的综合指南。