首页
归档
友情链接
留言
更多
关于
动态
Search
1
[分享栈]centos7安装gcc10.2.0
7,077 阅读
2
[技术栈]CRC校验原理及C#代码实现CRC16、CRC32计算FCS校验码
6,701 阅读
3
[技术栈]C#利用Luhn算法(模10算法)对IMEI校验
6,314 阅读
4
[分享栈]esxi6.7虚拟机安装omv(openmediavault)教程
5,765 阅读
5
[分享栈]centos7安装python3.8.5
4,830 阅读
分享栈
技术栈
经验栈
登录
Search
标签搜索
C#
centos
winform
仪器
IPV4
IPV6
测速
crc
crc16
crc32
fcs
luhn
模10
算法
sql
gcc
python
紫光展锐
omv
openmediavault
武小栈
累计撰写
21
篇文章
累计收到
37
条评论
首页
栏目
分享栈
技术栈
经验栈
页面
归档
友情链接
留言
关于
动态
搜索到
5
篇与
的结果
2021-02-23
[技术栈]网络协议3UDP协议
1. 前言其实前文了解了TCP之后,UDP也不难理解了,肯定是一种协议格式和一些交互机制。TCP所有机制可以说都是围绕着可靠二字展开的,难道TCP就没有缺点吗?连接三次握手,传输数据还有应答,断开都要四次挥手,听上去就觉得很麻烦。而且每次交互都是需要响应时间和传输时间的。UDP就应运而生了,它所应用的场景就是数据传输不需要那么可靠(如视频直播、网络电台等),或者是整个数据包就本身就很小的情况。2. UDP头部可以看到UDP报文头就超级简单了,仅仅8个字节,包含了最基本的四个字段:源端口、目的端口、长度、校验值。3. 机制我感觉UDP没啥机制,就是简单的发送,仅是将内容包装了一下(加上UDP报文头部),非要所机制应该就是有个校验,可以校验数据包是否正确传输。这一块待小伙伴来补充吧。
2021年02月23日
668 阅读
0 评论
0 点赞
2021-02-22
[技术栈]网络协议2TCP协议
1.前言其实TCP协议所在的传输层是一个在网络系统里很基础的层级了,模组中常用的HTTP,MQTT,FTP都是在TCP的基础上建立起来的,如果TCP不能用,以上这样应用都要凉凉。2.TCP协议前面的《网络协议1协议栈》已经简单的介绍了TCP协议的工作方式,在这里再稍微深入的说一下TCP协议,为啥只讲稍微深入呢,因为探究深入了以小弟的水平还真说不明白是怎么回事。大家关于TCP的印象是啥?三次握手四次挥手?还是不丢包?这里就以一个完整数据传输流程简单的介绍一些TCP是怎样传输数据的,整体流程下图所示。2.1 TCP连接阶段第一步就是建立连接。建立TCP连接,这里的建立连接并不是真正意义上的建立连接,当设备接入互联网的时候,互联网的设备就已经连接好了,本质上来说互联网所有设备都是相连的,这里的连接而是一个准备数据传输的过程。都知道建立连接要三次握手,这里引入几个问题,有问题的小伙伴也可以在下面提问,大家来补充答案哦:1、既然设备接入网络客户端和服务器就已经连接了,为什么TCP通信还要建立连接? 答:更好的保证可靠性,经过三次握手也可以让通信双方验证各自的发送能力和接收能力是否正常,同时交换一些必要参数。2、建立连接为什么要三次握手? 答:三次是可靠性和效率兼顾的次数,具体可以看下图。估计有好奇宝宝还会疑问两次握手不行吗?还真不行,如果没有最后一个ACK, 此时主机B 是无法知道自己的发送能力和对方的接收能力是否正常,ACK后面会说。还会问四次握手可以吗?答案当然是可以的,甚至无数次都可以,但是这样就丢失了效率,有点因噎废食。3、三次握手干了什么? 答:总体来说就是发送了三次TCP报文。看一下在连接过程中到底传输了什么,这里以ML302模组连接TCP服务器为例,下图为TCP服务器抓包结果:可以从图中看出,TCP连接的确经历了三次握手。第一次握手,模组-->服务器,模组15885端口向服务器9090端口发送了一段TCP报文,报文内容即下图蓝色部分数据,具体含义可以参照《网络协议1协议栈》中关于TCP报文头的介绍部分。其中主要内容有[SYN]、Seq=0、Win=24000、MSS=1360、WS=1这几个参数。SYN,同步序列号(Synchronize sequence numbers),作用也就是同步信息收发双发,也就是建立连接,把携带SYN标识的称为同步报文段。Seq=0,序列号(sequence),用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节。表示的是我方(发送方)这边,这个packet的数据部分的第一位应该在整个data stream中所在的位置。当存在SYN时,这个值为初始值,之后的首次数据发送均需要在这个初始值上+1。理论上,它是随机的。关于这个序列号的变化规则可以看下表。类型握手(SYN)或终止(FIN)传输数据包为0seq(自己发送)上次发送 seq+1上次发送seq+数据长度上次发送seqWin=24000,窗口(Window),又称滑动窗口(Sliding Window),这里表示模组能够接收最大数据量大小为24000字节,一般和接收缓冲区大小相等。MSS=1360,最大报文片段长度(Maximum Segment Size),在连接建立的时候,即在发送 SYN 段的时候,同时会将 MSS 发送给对方(MSS 选项只能出现在 SYN 段中!!!),告诉对端它期望接收的 TCP 报文段数据部分最大长度。WS=1,窗口因子(window scaling),窗口值*窗口因子=最终的窗口值。但是并不是所有的客户端、服务端都支持窗口因子的。两端都支持,窗口因子才会最终被启用。如有一端不支持,则因子不会被使用。窗口因子是在TCP的SYN包协商的,而且只协商一次。也就是滑动窗口是根据实际情况进行变化的,但是因子是固定的。第二次握手,服务器-->模组,服务器向模组发送了一段TCP报文,报文内容为下图蓝色部分数据。其中主要内容有[SYN,ACK]、Seq=0、Ack=1、Win=8192、MSS=1460、WS=256这几个参数。ACK,确认号是否有效字段(Acknowledgment field significant),只有ACK标志为1时确认序号字段(Ack)才有效。一旦一个连接建立起来,这个字段总是被设置,ACK标志也总是被设置为1。Ack=1,确认序号(Acknowledgment Number),应当是上次已成功收到数据字节序号加1。具体规则看下表类型握手(SYN)或终止(FIN)传输数据包为0ack(接收对方)上次接收 seq+1上次接收seq+数据长度上次接收seq第三次握手,模组-->服务器,模组向服务器发送TCP报文数据,如下图蓝色部分所示。其中主要内容有[ACK]、Seq=1、Ack=1、Win=24000这几个参数。用白话文说这三次握手应该是: 模组向服务器说:我想要传输数据,我们先同步(**SYN**)一下,还有我的初始序列号是0(**Seq=0**),你别一个劲的向我发数据,我能接收的最大数据量是24000字节(**Win=24000**),记住(由发送方计算)别给我撑爆了(接收缓存溢出),我一次最多能接收1360长度的数据,给多了我不要。 服务器向模组说:我已经收到了你的说的话了,我这边准备好了(ACK),你也记一下(SYN)我的情况,balabalabala。模组向服务器说:好的,我这边也准备好了(ACK),我们可以开始传数据了。2.2 TCP通信阶段连接好了,就可以开始发送数据了,下图是模组向服务端发送4014字节数据的抓包情况。可以从图中看到,4014字节模组总共发送了三次,服务器回了两次。还是从疑问出发,同样有问题的小伙伴也可以在下面提问:1、为什么4014字节要分开发送?答:这里就是TCP特性之一==数据分段==,TCP分段产生原因是MSS。可以从图中看到第一段和第二段发送的数据长度为1360,这个值刚好是连接时发送的MSS=1360。2、为什么MSS这个值是?答:协议栈会根据一个 叫作MTU的参数来进行判断。MTU 表示一个网络包的最大长度,在以太网中一般是 1500 字节。MTU 是包含头部的总长度,因此需要从 MTU 减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大 数据长度,这一长度叫作 MSS。3、为什么不一个字节一个字节发送?答:这个问题比较傻,想也知道如果是一个字节一个字节发送的话,报文头都比数据量更大,这数据传输效率有多低就不想了吧。4、为什么模组发了两次数据,服务器才应答,不应该是一应一答吗?答:这里显示了TCP的两个特性,==滑动窗口==、==延迟应答==和==捎带应答==。ACK 计算是需要时间的,而且网络也会存在堵塞时延,如果每包数据发送方都要等到接收方确认才继续发下一包,那等待确认这一段时间发送方什么都干不了,这样效率太低了,所以这里就引入了==滑动窗口==这一特性,之前已经说过了,类似于接收缓存区,服务器告诉模组我的接收滑动窗口大小为8192*256=2097152字节,所以模组可以不等确认信息返回直接向服务器不断的发数据,可以看下图。在TCP中,确认应答机制以保证数据的可靠传输。但是是不是接收方接收到数据就立即返回ACK应答呢?但是接收的数据都是先到接收缓存区,然后再由应用程序读取到应用中,如果是接收到数据就立即返回ACK,这时候的缓存区中接收区的数据还没能够由应用程序读取,缓存区的剩余大小就是窗口大小。但是如果我们延迟一会,等待缓存区中数据被读取,那么剩余的缓存区就会大些——这就是==延时应答==,每个操作系统中设置的等待时间是不一样的。,但是肯定不可能是只延时这么死板,延时应答有数量限制和时间限制两部分:条件协议规定个人补充(这里为个人猜测)数量限制每隔两个包就应答一次应该是每隔大于等于2个包,可以想象随着网速越快,比如现在家用的100M宽带,每秒可传输12800KB数据,1毫秒就可以传送近13K的数据,前面的MSS介绍了每包数据最大约为1.5K,也就是1毫秒就能传送近10包数据。时间限制超过最大延时时间就应答一次,一般是200ms但是这个值是可以设定的,随着网速提升这个值肯定会越来越小的,理由同上。发现没有,上面抓包图中,模组发了两包数据,服务端只回应了一个ACK应答信息,观察应答信息,其中将模组的发的两包数据的应答合并了(Ack=2721),模组发了两次1360字节长度的数据,服务端直接回复“我已经收到了前2720字节数据”。==捎带应答==上面介绍了滑动窗口这一机制,所以就多出来了一个步骤,接收方得告诉发送方我的窗口大小的变化,理论上应该是应用程序把数据从接收缓存区取出的时候向发送方发信息告诉我的窗口大小目前是多少。前面我们知道接收方发送应答消息是需要时间的,要是这个时间和应用程序读取缓存区的时间刚好一致呢,分别发送一个应答消息,一个窗口更新消息是不是太傻了,从上面的抓包图也可以看到,应答消息和窗口更新是一起发送的,因为它们是TCP头部中不同的区域,互不影响。从这个扩展一点呢,如果双方都在同时发送数据呢,是不是可以在数据包的头部包含应答消息和窗口更新,当然是可以的,这里就引入了==捎带应答==,即在发送数据的时候捎带着其他信息。其实TCP传输中还有很多特性,但是一个一个将就内容太多了,还不如看书呢。剩下的就留给小伙伴们补充。确认应答机制超时重传机制滑动窗口机制快速重传机制流量控制机制拥塞控制机制延时应答机制捎带应答机制2.3 TCP断开阶段前面说了连接阶段,再说这个阶段觉得索然无味了,总了个之呢,就是交互了四次控制信息,发送了四次报文,是不是呢,我们还是看一下抓包情况。有点奇怪,发送的是RST,而且只有一条,表格贴在下面了,懒得翻回上一篇看了,可以看到设个RST表示强制断开连接,应该是302的设置就是这样吧。看一个正式的,下面以本机电脑连接服务端的情况,可以看到,的确是4次挥手,具体的小伙伴来细说。
2021年02月22日
852 阅读
0 评论
0 点赞
2021-02-07
[技术栈]网络协议1协议栈
1.前言接下来我打算写几篇文章简单的介绍一下我们常用的一些通信协议,当然所有描述都是基于我自己现有了解,肯定会有所纰漏,甚至错误的地方,望各位大佬不吝指正。2.网络模型介绍以中移ML302模组为例,在模组部官网中查询到的产品规格中网络协议支持以上这几项,所以以测IPv4,IPv6,TCP,UDP,PPP,FTP,HTTP,NTP,MQTT这八种协议为例说明。既然要测网络协议,那就先找一个网络协议模型瞧一瞧,个人比较喜欢看五层协议,就找个五层协议说说。上面这个就是TCP/IP五层模型,不熟悉的也了解过,不了解的也听说过。IPv4,IPv6这俩位于第3层,网际层。TCP,UDP这俩位于第4层,传输层。PPP位于第2层,网络接口层FTP,HTTP,NTP,MQTT这四个都位于第5层,应用层。2.通信示例介绍2.1通信流程就以我们最耳熟能详的TCP为例来讲讲一次数据的发送和接收发生了什么,总得来说就如下图所示。从用户A操作发送信息开始,经过了协议栈层层“打包”,然后数据终于通过网线传到了用户B处,然后这个数据经过了用户B设备的协议栈层层“拆包”,最终用户B拿到了用户A发送的原内容。2.2演示方式图上看着是这样,但是实际上是不是这样呢,眼见为实,这次的演示方式如下:在B电脑创建TCP SERVER服务,并开启抓包软件进行抓包。 在A电脑创建TCP CLIENT连接B电脑服务。 从A电脑向B电脑发送一段数据。 在B电脑通过抓包软件观测A电脑传过来的数据。2.3 实例演示做好环境准备工作,首先通过TCP连接发送36个字节长度的数据,如下图所示2.3.1 TCP协议根据上面流程中的描述,我这里点击了“发送”按钮,然后数据被传到传输层,在这里经过TCP协议的加工,给添加上TCP报头,先看一下TCP报头是什么样的。从图中也可以看到,TCP报头基础长度为20个字节,每个字节代表的含义图中也有相应的说明。详细的可以自己去查阅https://tools.ietf.org/html/rfc793#page-15原文,下表为各字段的简要说明。看一下抓包软件抓到的数据是不是这样组成的,下面图中蓝色部分的20字节就是TCP(Transmission Control Protocol)报文头,简单的对比一下前面4个字节,看看是不是向上图中所描述的那样。从TCP报文头说明图中可以看到前面2个字节(16位)是源端口,后面2个字节(16位)是目标端口,前面连个字节是0x1030用计算器一算就可以得出0x1030正是源端口4144。后面的目标端口也是一样的。传输层的TCP协议处理完了,根据流程图,接下来就应该把数据传给网络层处理了。2.3.2 IP协议同样呢,先看看网络层中IP协议的头部定义描述可以看到,IP协议头部同样基础长度为20字节。下图就是抓包的数据,其中蓝色部门的20字节就是IP协议头部,同样我们选取几个字段来对比看看,可以从上图中看到,IP协议头部基础的20个字节长度里,最后8个字节分别为源地址和目标地址,就看看抓包数据中最后8个字节是不是原地址和目标地址。从图中的标注也可以明确的看到,最后8个字节的确分别是源地址和目标地址。从流程图上来看,下面就应该传到数据链路层,由以太网协议来蹂躏它了。2.3.3 以太网协议网络层的事情到现在完成了,然后网络层将IPv4报文传到第2层,数据链路层。在数据链路层还会将数据再封装一层,这一层会将IP数据包封装成以太帧,如下图所示,这一步的主要目的是加入MAC地址,所以这里记录了当前以太帧需要由网卡发送到哪一个屋里设备上去,在不同的设备或者系统中,这里的帧格式不一定相同。到了这一层了,自己也不甚了解,就不大言不惭了,这一层已经比较接近物理层了,对于我一个软件开发的来说,基本上难以言传。总了个之,数据链路层和物理层就是将数据加以处理,使数据能在物理设备中转发后达到指定设备上。3.后记其实除了TCP/IP网络协议之外还有很多,基本上各个大厂都出过,比如微软、苹果、思科等等都搞过,不过随着互联网的飞速发展,TCP/IP协议可以说已经应用到了互联网的每一个角落,所以,这里讲述网络协议模型就采取了TCP/IP协议为例。4.网络协议关系图4.1 TCP/IP协议关系图4.2 网络通信协议关系图
2021年02月07日
849 阅读
0 评论
0 点赞
2019-09-18
[技术栈]CRC校验原理及C#代码实现CRC16、CRC32计算FCS校验码
1.CRC、FCS是什么CRC,全称Cyclic Redundancy Check,中文名称为循环冗余校验,是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。FCS,全称Frame Check Sequence,中文名称为帧校验序列,俗称帧尾,即计算机网络数据链路层的协议数据单元(帧)的尾部字段,是一段4个字节的循环冗余校验码。注:CRC循环冗余校验和FCS帧校验序列是单独的概念,CRC是一种错误校验方法,FCS是帧尾校验码,FCS可以采用CRC校验方法,也可以采用其他校验方法。2.CRC算法原理我们可以把任意的一串二进制数据表示为一个与之对应的多项式。比如:二进制数据:1100101 多项式:$x^6 + x^5 + x^2+1$多项式: $x^6 + x^4+x^3 + x^2+1$二进制数据:1011101有了这样的对应关系,对二进制数据的CRC校验就可以利用多项式运算规则进行校验计算。CRC校验算法正是采用了模2除法,在数据处理里的具体表现为异或运算。CRC的具体运算规则为:假设要传输的二进制数据为:10010110,对应的m阶多项式为:$M =x^7+x^4+x^2+x^1$,除数为h阶的多项式为:$H=x^4+x$,对应的二进制码为:10010,先将M乘以$x^h$,即将M对应的二进制数据后面加h个0,然后除以h阶的多项式H,得到的h-1阶的余数项R对应的二进制数据即为数据10010110的CRC校验码。3.计算CRC校验3.1.手工计算CRC校验码M和H的多项式除法运算,可以用模2除法运算计算。下面为以生成多项式为H求10010110的CRC校验码运算过程:对应到异或运算:通过示例即其他自定义的一些数据运算后,根据运算现象总结可以得到一些规律:1.每次异或运算,当从左到右首位为1的时候,就与生成多项式H异或运算,然后再左移1位;当首位为0的时候只将数据左移1位。2.每次异或运算后的数据,首位必定为0,因为首位为1的时候进行异或运算,而生成多项式的首位也必定为1。所以当需要进行异或运算时,可以舍弃H的首位,舍弃后为H',直接将数据左移一位后再与H'异或。3.每次运算,参与运算的是数据的前h位,可以用一个存储h位二进制数据的寄存器S,将数据的前h位存储到这个寄存器中。每次运算先将寄存器的首位移除,然后将二进制数据后一位移入,然后再参与运算,最后寄存器中的值即为CRC校验码。3.2.C#代码计算CRC校验码//代码验证如下: static void Main(string[] args) { int data = 0b10010110; int ploy = 0b0010; ploy <<= 4; Console.WriteLine($"第0次运算结果:"+Convert.ToString(data, 2)); for (int i = 0; i < 8; i++) { if ((data & 0b10000000) == 0b10000000) { data = (data << 1) ^ ploy; } else { data <<= 1; } Console.WriteLine($"第{i+1}次运算结果:"+Convert.ToString(data, 2)); } Console.WriteLine($" 最终运算结果:"+Convert.ToString(data, 2)); Console.ReadKey(); } 这里用int的第5位到第8位作为一个四位寄存器,可以看到与手算一致,最后算得校验位1100。4.查表法可以看到,参与运算的始终只有4位,所以在移位D1数据时,参与运算的数据只有D1和D2,经过四次移位运算,D1被移除寄存器,这个时候受到影响的只有D2。而将D2的初值经过四次异或运算后的值就可以获得四次移位后的新数据$D2'=D2\bigoplus H1 \bigoplus H2\bigoplus H3\bigoplus H4 = D2 \bigoplus \sum{h}$。每一次D2是异或0还是异或生成多项式H',与D2本身的值无关,仅与D1中被移除的数据有关(首位为0还是1),所以这里引入了一个查表法,即先将所有可能的D1组合都计算出对应的$\sum{h}$,一次性移除四位,然后以$D2\bigoplus{\sum{h}}$即可以获得D2'。生成多项式为H,则一共有$2^h$种可能,代码如下:class CalcByCrcTable { private byte[] CrcTable; private void CteateTable() { int ploy = 0b0010; CrcTable = new byte[(int)Math.Pow(2,4)]; ploy <<= 4; for (int i = 0; i < CrcTable.Length ; i++) { int data = i<<4; for (int j = 0; j < 4; j++) { if ((data & 0b10000000) == 0b10000000) { data = (data << 1) ^ ploy; } else { data <<= 1; } } CrcTable[i] = Convert.ToByte((data & 0xf0)>>4); } } public byte CalcCrc() { CteateTable(); int data = 0b10010110; byte crchigh4 = CrcTable[(data>>4)&0x0f];//用查表法先查到data的高四位1001的crc值; byte value = Convert.ToByte((data & 0x0f) ^ crchigh4);//将高四位的CRC与低四位异或,得到移位四次后的数据值; byte crc = CrcTable[value]; //在用移位后的数据值查出数据的CRC校验码; return crc; } } static void Main(string[] args) { CalcByCrcTable calcByCrcTable = new CalcByCrcTable(); byte crc = calcByCrcTable.CalcCrc(); Console.WriteLine($" CRC校验码为:" + Convert.ToString(crc, 2)); Console.ReadKey(); }//打印结果如下CRC校验码为:1100可以看到与前面的计算法结果一致。5.反向校验上面所诉的均为正向检验(Normal),当然也有反向校验(Reversed),反向校验是将数据和生成多项式均进行了一个镜像,当然算法也需要镜像,即镜像后从右往左运算。5.1手工计算CRC反向校验码原二进制数据:10010110原生成多项式:0010正向CRC校验码:1100镜像二进制数据:01101001镜像生成多项式:0100镜像算法:反向CRC校验码:00115.2.C#代码计算CRC反向校验码class CalcByCrcTable { private byte[] CrcTable; private void CteateReversedTable() { int ploy = 0b0100; CrcTable = new byte[(int)Math.Pow(2, 4)]; for (int i = 0; i < CrcTable.Length; i++) { int data = i; for (int j = 0; j < 4; j++) { if ((data & 1) == 1) { data = (data >> 1) ^ ploy; } else { data >>= 1; } } CrcTable[i] = Convert.ToByte((data & 0x0f)); } } public byte CalcReversedCrc() { CteateReversedTable(); int data = 0b01101001; byte crclow4 = CrcTable[data & 0x0f];//用用查表法先查到data的低四位1001的crc值; byte value = Convert.ToByte(((data>>4) & 0x0f) ^ crclow4);//将第四位的CRC与低四位异或,得到移位四次后的数据值; byte crc = CrcTable[value]; //在用移位后的数据值查出数据的CRC校验码; return crc; } } static void Main(string[] args) { CalcByCrcTable calcByCrcTable = new CalcByCrcTable(); byte crc = calcByCrcTable.CalcReversedCrc(); Console.WriteLine($" CRC反向校验码为:" + Convert.ToString(crc, 2)); Console.ReadKey(); }//打印结果如下CRC反向校验码为:116.C#查表法计算CRC16校验码//多线程使用时请注意干扰 class CalcOnCrc16 { private ushort[] Crc16NormalTable; private ushort[] Crc16ReversedTable; private void CreateNormalCrc16Table(ushort ploy) { ushort data; Crc16NormalTable = new ushort[256]; int i, j; for (i = 0; i < 256; i++) { data = (ushort)(i << 8); for (j = 0; j < 8; j++) { if ((data & 0x8000) == 0x8000) data = Convert.ToUInt16((ushort)(data << 1) ^ ploy); else data <<= 1; } Crc16NormalTable[i] = data; } } private void CreateReversedCrc16Table(ushort ploy) { ushort data; Crc16ReversedTable = new ushort[256]; int i, j; for (i = 0; i < 256; i++) { data = (ushort)i; for (j = 0; j < 8; j++) { if ((data & 1) == 1) data = Convert.ToUInt16((ushort)(data >>1) ^ ploy); else data >>= 1; } Crc16ReversedTable[i] = data; } } /// <summary> /// 正向计算CRC16校验码 /// </summary> /// <param name="bytes">校验数据</param> /// <param name="poly">生成多项式</param> /// <param name="crcInit">校验码初始值</param> /// <returns></returns> public ushort CalcNoemalCrc16(byte[] bytes,ushort poly,ushort crcInit) { CreateNormalCrc16Table(poly); ushort crc = crcInit; for (int i = 0; i < bytes.Length; i++) { crc = Convert.ToUInt16((ushort)(crc << 8) ^ Crc16NormalTable[((crc >> 8) & 0xff) ^ bytes[i]]); } return crc; } /// <summary> /// 反向计算CRC16校验码 /// </summary> /// <param name="bytes">校验数据</param> /// <param name="poly">反向生成多项式</param> /// <param name="crcInit">校验码初始值</param> /// <returns></returns> public ushort CalcReversedCrc16(byte[] bytes, ushort poly, ushort crcInit) { CreateReversedCrc16Table(poly); ushort crc = crcInit; for (int i = 0; i < bytes.Length; i++) { crc = Convert.ToUInt16((ushort)(crc >> 8) ^ Crc16ReversedTable[(crc & 0xff) ^ bytes[i]]); } return crc; } }7.C#查表法计算CRC32校验码class CalcOnCrc32 { private uint[] Crc32NormalTable; private uint[] Crc32ReversedTable; private void CreateNormalCrc32Table(uint ploy) { uint data; Crc32NormalTable = new uint[256]; int i, j; for (i = 0; i < 256; i++) { data = (uint)(i << 24); for (j = 0; j < 8; j++) { if ((data & 0x80000000) == 0x80000000) data = Convert.ToUInt32((uint)(data << 1) ^ ploy); else data <<= 1; } Crc32NormalTable[i] = data; } } private void CreateReversedCrc32Table(uint ploy) { uint data; Crc32ReversedTable = new uint[256]; int i, j; for (i = 0; i < 256; i++) { data = (uint)i; for (j = 0; j < 8; j++) { if ((data & 1) == 1) data = Convert.ToUInt32((uint)(data >> 1) ^ ploy); else data >>= 1; } Crc32ReversedTable[i] = data; } } /// <summary> /// 正向计算CRC32校验码 /// </summary> /// <param name="bytes">校验数据</param> /// <param name="poly">生成多项式</param> /// <param name="crcInit">校验码初始值</param> /// <returns></returns> public uint CalcNoemalCrc32(byte[] bytes, uint poly, uint crcInit) { CreateNormalCrc32Table(poly); uint crc = crcInit; for (int i = 0; i < bytes.Length; i++) { crc = Convert.ToUInt32((uint)(crc << 8) ^ Crc32NormalTable[((crc >> 24) & 0xff) ^ bytes[i]]); } return crc; } /// <summary> /// 反向计算CRC32校验码 /// </summary> /// <param name="bytes">校验数据</param> /// <param name="poly">反向生成多项式</param> /// <param name="crcInit">校验码初始值</param> /// <returns></returns> public uint CalcReversedCrc32(byte[] bytes, uint poly, uint crcInit) { CreateReversedCrc32Table(poly); uint crc = crcInit; for (int i = 0; i < bytes.Length; i++) { crc = Convert.ToUInt32((uint)(crc >> 8) ^ Crc32ReversedTable[(crc & 0xff) ^ bytes[i]]); } return crc; } }参考资料循环冗余检验 (CRC) 算法原理CRC查找表法推导及代码实现比较CRC(循环冗余校验)在线计算
2019年09月18日
6,701 阅读
0 评论
0 点赞
2019-09-11
[技术栈]C#利用Luhn算法(模10算法)对IMEI校验
1、Luhn算法(模10算法)通过查看ISO/IEC 7812-1:2017文件可以看到对于luhn算法的解释,如下图:算法主要分为三步:第一步:从右边第一位(最低位)开始隔位乘2;第二步:把第一步所得的每一个数字加入到原来的数中,比如9*2=18,为1+8;第三步:用以0结尾且大于第二步所获得的数的和的最小整数减去第二步所获得的和即可以获得校验位,如70-67=3,3即为校验位,如果第二步所有数字的和以0结尾,比如30、40、50等,那么校验为0;2、IMEI校验IMEI码由GSM(Global System for Mobile Communications,全球移动通信协会)统一分配,授权BABT(British approvals Board of Telecommunications,英国通信认证管理委员会)审受。在TS.06 IMEI Allocation and Approval Process中规定IMEI校验应该通过Luhn算法计算,如下图所示:3、C#代码public class LuhnCalcCheckDigit { /// <summary> /// 通过Luhn算法计算校验位,适合IMEI、银行卡等 /// </summary> /// <param name="imei">不包含校验位的号码</param> /// <returns></returns> public static int CalcLuhnCheckDigit(string imei) { int checkDigit = 0; int addValue = 0; for (int i = 0; i < imei.Length; i++) { if (i % 2 == 0) { int result = Convert.ToInt32(imei[imei.Length - i - 1].ToString()) * 2; if (result > 9) { addValue += (result - 9); } else { addValue += result; } } else { addValue += Convert.ToInt32(imei[imei.Length - i - 1].ToString()); } } if (addValue % 10 == 0) { checkDigit = 0; } else { checkDigit = 10 - addValue % 10; } return checkDigit; } /// <summary> /// 通过Luhn算法核验号码是否合法,适合IMEI、银行卡等 /// </summary> /// <param name="imei">包含校验位的号码</param> /// <returns></returns> public static bool VerifyLuhn(string imei) { int checkDigit = 0; int addValue = 0; for (int i = 1; i < imei.Length; i++) { if (i % 2 == 1) { int result = Convert.ToInt32(imei[imei.Length - i - 1].ToString()) * 2; if (result > 9) { addValue += (result - 9); } else { addValue += result; } } else { addValue += Convert.ToInt32(imei[imei.Length - i - 1].ToString()); } } if (addValue % 10 == 0) { checkDigit = 0; } else { checkDigit = 10 - addValue % 10; } return (checkDigit - Convert.ToInt32(imei[imei.Length - 1].ToString())) == 0; } }4、参考资料链接TS.06 IMEI Allocation and Approval ProcessISO/IEC 7812-1:2017
2019年09月11日
6,314 阅读
1 评论
0 点赞