精华内容
下载资源
问答
  • PCIe 数据链路的主要功能时保证两台设备之间传送TLP的完整性。还负责链路的初始化和电源管理,包括跟踪链路状态以及上面的处理和下面的物理之间传递的消息和状况。 管理链路时,由3组重要的DLLP:TLP确认...

    PCIe 数据链路层的主要功能时保证两台设备之间传送TLP的完整性。还负责链路的初始化和电源管理,包括跟踪链路状态以及上面的处理层和下面的物理层之间传递的消息和状况。

    在管理链路时,由3组重要的DLLPTLP确认Ack/Nak DLLP;电源管理DLLP;流控制数据包DLLP

    DLLPTLP不同,没有携带目标信息,因为它们只用于相邻最近组件之间的通信。

    DLLP从发送器发送至接收器时,立即被处理,不能对DLLP进行流量控制。与TLP不同,DLLP没有确认协议,PCIe规范定义了超时机制,能够用来从丢失或者丢弃DLLP的状态中恢复出来。

    固定大小的DLLP数据包8个字节。所有的数据链路层数据包由如下几部分组成:①由一个字节的类型字段和3个附加属性字节组成的1DW核心,其中属性随DLLP类型的变化而变化;②一个根据DW核心内容计算的16比特CRC,附加在核心之后;③然后,把这6个字节传送至物理层,物理层给数据包添加一个DLLP开始控制字符和一个数据包结束控制字符。

    DLLP中不会有数据有效载荷,所有有用信息都在类型字段和属性字段中。

    1.5.1 DLLP数据包的类型

    规范定义了3组DLLP,每组都有许多变体。

    DLLP类型

    类型字段编码

    用途

    ACK

    0000 0000b

    TLP传送的完整性

    Nak

    0001 0000b

    TLP传送的完整性

    PM_Enter_L1

    0010 0000b

    电源管理

    PM_Enter_L23

    0010 0001b

    电源管理

    PM_Active_State_Request_L1

    0010 0011b

    电源管理

    PM_Request_Ack

    0010 0100b

    电源管理

    厂商专用

    0011 0000b

    厂商

    INitFC1-P(xxx-VC#)

    0100 0xxxb

    TLP流控制

    INitFC1-NP(xxx-VC#)

    0101 0xxxb

    TLP流控制

    INitFC1-Cpl(xxx-VC#)

    0110 0xxxb

    TLP流控制

    INitFC2-P(xxx-VC#)

    1100 0xxxb

    TLP流控制

    INitFC2-NP(xxx-VC#)

    1101 0xxxb

    TLP流控制

    INitFC2-Cpl(xxx-VC#)

    1110 0xxxb

    TLP流控制

    UpdateFC-P(xxx-VC#)

    1000 0xxxb

    TLP流控制

    UpdateFC-NP(xxx-VC#)

    1001 0xxxb

    TLP流控制

    UpdateFC- Cpl (xxx-VC#)

    1010 0xxxb

    TLP流控制

    保留

    其他

    保留

    1.5.2 Ack或Nak DLLP数据包的格式

    下图是接收器用来确认或否认一个TLP传送的DLLP数据包的格式。

     

    各字段定义如下表:

    字段名

    头字节/比特

    DLLP功能

    AckNak_Seq_Num[11:0]

    字节3的比特7:0

    字节2的比特3:0

    ACK DLLP:对于接收的序列号等于EXT_RCV_SE count的正确TLP数据包,使用NEXT_RCV_SEQ count-1;对于接收的序列号小于EXT_RCV_SE count的正确TLP数据包,使用NEXT_RCV_SEQ count-1;

    NAK DLLP:与CRC校验失败的TLP有关,使用NEXT_RCV_SEQ count-1;对接收的序列号早于NEXT_RCV_SEQ count的TLP,使用NEXT_RCV_SEQ count-1;发送器一旦接收到NAK DLLP,它就会清除序列号等于或早于该序列号的TLP,重发剩余的TLP

    类型[7:0]

    字节0的比特7:0

    表示DLLP的类型

    0000 0000b = ACK DLLP

    0001 0000b = NAK DLLP

    16比特的CRC

    字节5的比特7:0

    字节4的比特7:0

    16比特的CRC用来保护此DLLP的内用,根据ACK/NAK的字节0~3计算的

    1.5.3 电源管理DLLP数据包的格式

    PCIe电源管理DLLPTLP数据包可代替大部分与电源管理状态变化有关的信号。用于电源管理的DLLP的格式如下图。

     

    字段名

    头字节/比特

    DLLP功能

    类型[7:0]

    字节0的比特7:0

    0010 0000b = PM_Enter_L1

    0010 0001b = PM_Enter_L2

    0010 0011b = PM_Active_State_Request

    0010 0100b = PM_Request_Ack

    链路CRC

    字节5的比特7:0

    字节4的比特7:0

    16比特的CRC用来保护此DLLP的内用,根据ACK/NAK的字节0~3计算的

    1.5.4 流控制数据包的格式

    PCIe通过使用基于信用的流控制方案消除了早期总线协议中许多效率低下的方面。初始化信用和当接收器缓冲区空间可用时更新信用要使用3个略有不同的DLLP。两个流控制初始化数据包分别称为InitFC1InitFC2。更新DLLP称为UpdateFC

     

    字段名

    头字节/比特

    DLLP功能

    DataFC 11:0

    字节3的比特7:0
    字节2的比特3:0

    此字段含有与数据存储相关的信用。数据信用的单位时每信用16个字节,能够应用于对V[2:0]表示的虚拟信道和字节0中比特7:4代码表示的流量类别的流控制计数

    HdrFC 11:0

    字节2的比特7:6

    字节1的比特5:0

    此字段含有与头存储相关的信用。数据信用的单位时每信用一个头,能够应用于对V[2:0]表示的虚拟信道和字节0中比特7:4代码表示的流量类别的流控制计数

    VC[2:0]

    字节0的比特2:0

    此字段表示接收信用的虚拟信道(VC0~7)

    类型[3:0]

    字节0的比特7:4

    0100b = INitFC1-P

    0101b = INitFC1-NP

    0110b = INitFC1-Cpl

    0101b = INitFC2-P

    1101b = INitFC2-NP

    1110b = INitFC2-Cpl

    1000b = UpdateFC-P

    1001b = UpdateFC-NP

    1010b = UpdateFC- Cpl

    链路CRC

    字节5的比特7:0

    字节4的比特7:0

    16比特的CRC用来保护此DLLP的内用,根据ACK/NAK的字节0~3计算的

    1.5.5 厂商专用DLLP

     

    字段名

    头字节/比特

    DLLP功能

    类型[3:0]

    字节0的比特7:4

    厂商专用

    链路CRC

    字节5的比特7:0

    字节4的比特7:0

    16比特的CRC用来保护此DLLP的内用,根据ACK/NAK的字节0~3计算的

    展开全文
  • OSI参考模型中,每一个路由器都必须实现网络层、数据链路层和物理层的功能,如下图:其中数据链路层和物理层的结合非常紧密,很多协议比如802系列协议都是同时对数据链路层和物理层进行了规范。TCP/IP协议栈里,...

    在OSI参考模型中,每一个路由器都必须实现网络层、数据链路层和物理层的功能,如下图:

    其中数据链路层和物理层的结合非常紧密,很多协议比如802系列协议都是同时对数据链路层和物理层进行了规范。

    在TCP/IP协议栈里,每个路由器也要实现网络层、数据链路层和物理层的功能。

    网络层使用IP地址、查路由表来决定将数据发送到哪里去。但是因为网络的分层设计,实际上数据还要在物理链路层进行组帧,此时会加上数据链路层的帧头和帧尾(在有些链路层协议中没有帧尾),交给物理层发送,数据链路层使用MAC地址来寻找下一跳,mac地址就包含在数据链路帧的帧头里。

    具体来讲,广泛使用的以太网协议、802.11无线局域网协议,HDLC协议和PPP协议,都规定了不同格式的数据链路帧帧头(有些还包含了帧尾),你可以查看相关的协议,寻找到底这些协议把mac地址放在哪里了,下面帖一张HDLC和PPP协议的帧头格式

    为什么有了网络层的IP地址,还要有数据链路层的MAC地址存在呢?我个人理解

    这是网络分层设计的必然要求。

    网络为什么要分层设计,只设计一层行不行?当然是行的,但这样就势必非常复杂。网络层为什么和数据链路层分开,也是为了设计的简单化,当时在设计IP协议的时候,设计思路就是让网络层的功能尽可能地简单,其他非必须功能由其他层完成。因为互联网是“把采用不同协议的子网连接起来构成的网络之网络”,如果IP层协议太复杂,势必会导致各种子网接入的困难。

    比如说,网络层和数据链路层分开后,链路层就可以使用有线连接的以太网,无线连接的WiFi,以及手机基站这种3G、4G、5G网络,这些使用完全不同连接技术的设备能方便地接入互联网,就得益于数据链路层和网络层协议设计分开这一思想。否则如果混杂在一起,就会导致新的技术接入互联网很困难,比如说即将到来的5G,难以想象每一种链路层新技术的发明都导致需要全网更新IP协议会是什么场景,这样的经济开销就太可怕了。君不见就推行个IPv6还这么困难呢。

    而只有数据链路层的话,因为MAC地址不像IP地址这样支持聚合(CIDR技术),因此路由表不能根据块地址进行转发,而必须为每一个单独的MAC地址保留一个路由项目,算下来每个路由光路由表就得保存1.5PB的数据,共2^48条,简直不可想象。

    总结一下就是:数据链路层对网络层来说,隐藏了不同传输介质的细节,使得IP协议能够运行于不同物理介质的子网上,实现了IP协议将子网连接成互联网的设计初衷。

    数据链路层做了哪些网络层没有做的工作来保证数据传输的可靠性呢?

    查过路由表之后,IP层的功能就是指示,把这个IP数据报发送到下一跳IP去。那么我们可能会遇到这样的问题:就是这条线路有可能是多个节点共同使用的共享链路。

    比如这样一个网络拓扑:

    顶端的主机我们编号为X,下面五个主机我们分别编号为A,B,C,D,E。此时A要向E发送信号,B要向D发送信号,C要向X发送信号。但他们用的是同一条物理介质,要知道数据的物理传输也就是用比如电平高低、光线变化、电磁波变化等方式来区分0和1的,假如图中的线路采取的是通过电平高低来识别01,如果大家没有一种途径来协商如何使用这个链路,而是想发数据了一股脑都发出来,那整条线路的电平就都是混乱的,结果是数据全部毁损,谁都没有成功发送正确的数据,这种现象就叫做冲突。

    发生冲突即意味着数据传输失败,而共享链路无处不在,比如说早期采用集线器连接的以太网,还有现在广泛使用的无线网。物理链路层必须能够处理这些冲突,就是如何保证在共享链路上某一特定时间段只有一个主机占用链路进行通信,或者说虽然是同时占用,但通过某种方式也能让他们的信号彼此区分,这种技术称为多址接入,在数据链路层上由MAC子层(media access control,介质访问控制)完成。

    同时,因为不同物理介质的特点,数据链路层采取的技术也不一样,比如无线网就更不稳定,更容易受到干扰也更容易出错,而有线网络就相对稳定,因此有线网络比如以太网就可以采取类似CDMA/CD技术,甚至因为有线网可以实现完全的点对点,就是说一条物理线路上只连接端点的两个主机,完全避免数据冲突的可能,而无线网络就只好采取CDMA/CA技术,还采取了确认重传等技术来保证数据传输的稳定性。

    在比如,在一条链路上,我们的网络层连续发出了转发三条数据报的要求,于是这条链路上有三条首尾相连的比特流,接收方怎么区分数据报文的边界呢?怎么知道第一条报文和第二条报文从哪个bite开始断开呢?这也需要链路层协议提供支持。(还是那句话,可以把这个功能做到IP层,但过于复杂不便拓展)

    可见,网络层就一句话,我这个数据报的下一跳IP是XX.XXX.XX.XX,请发送过去,链路层就要处理很多问题才能保证你的数据完整、正确、安全地传输到下一个节点去。

    总结一下就是:数据链路层对于物理层来说,是对不可靠易出错的物理层进行了包装,使其对上成为稳定的、不出错的逻辑传输层。

    不知道有没有解答题主的问题。

    展开全文
  • 1、数据链路层的设计问题 引言 数据链路层主要完成的功能包括:(1)向网络层提供一个定义良好的接口(2)处理传输...源机器的网络层有一个实体(称为进程),它将一些比特交给数据链路层,要求传送到目标机...

    1、数据链路层的设计问题

    引言

    • 数据链路层主要完成的功能包括:(1)向网络层提供一个定义良好的接口(2)处理传输错误(3)调节数据流,确保慢速接收方不会被快速发送方淹没。
    • 数据链路层将从网络层获取的数据包封装成帧以便传输。每个帧包含一个帧头、一个有效载荷(存放数据包)以及一个帧尾。

    1.1提供给网络层的服务

    • 在源机器的网络层有一个实体(称为进程),它将一些比特交给数据链路层,要求传送到目标机器。数据链路层的任务就是将这些比特传输给目标机器,然后交付给网络层,如图a;实际传输如图b,但很容易将这个过程想象成两个数据链路层的进程使用一个数据链路协议进行通信。本章中将隐式使用图a模型。在这里插入图片描述
    • 数据链路层提供的服务因具体协议的不同而有所差异。一般情况下,数据链路层提供以下3种可能的服务:(1)无确认的无连接服务(2)有确认的无连接服务(3)有确认的有连接服务
    • 无确认的无连接服务是指源机器向目标机器发送独立的帧,目标机器并不对这些帧进行确认。以太网就是一个提供此类服务的数据链路层极好实例。这种连接,事先不必建立逻辑连接,事后不必释放连接;若由于线路噪声造成了某一帧的丢失,数据链路层并不试图检测这种情况,更不会恢复。这类服务使用于两种场合:一种是错误率极低的场合,差错恢复可以由上层完成;一种是实时通信,及时交付比准确交付更重要。
    • 有确认的无连接服务。当网络层提供这种服务时,数据链路层仍然没有建立逻辑连接,但发送的帧需要单独确认,使发送方知道发送帧是否到达目的地。如果没有在一个指定的间隔到达,则发送方再次发送该帧,但可能导致一个帧被收发多次,浪费带宽。这类服务适用于不可靠信道,比如无线系统。802.11(WiFi)是此类服务的一个例子。
    • 有确认的有连接服务。发送方和接收方在传输数据之前需要建立一个连接,连接上发送的每一帧都被编号,数据链路层保证发出的每一帧都被接收方收到。它还保证每个帧只被接收一次,并且按正确的顺序接收。它适用于长距离且不可靠的链路,比如卫星通信或者长途电话电路。

    1.2成帧

    • 物理层所做的只是接收一个原始比特流并传给目标机器,如果信道存在噪声,物理层会在信号中添加某种冗余以便将误码率降到一定程度。但数据链路层接收的比特流并不一定正确,可能出现值的变化或者比特数量的不一致,因而需要检错和纠错。
    • 数据链路层将比特流拆分成多个离散的帧,为每一个帧计算一个称为校验和的短令牌,并插入到帧中传输。目标机器需要重新计算该帧的校验和,如果计算出来的校验和与该帧包含的校验和不同,则数据链路层知道传输过程中出现了错误,它会采取一定措施(比如丢弃或者发回一个错误报告)。
    • 拆分比特流需要使得接收方发现一个帧的开始,同时要使得所用带宽最少。常用的方法有:(1)字节计数法(2)字节填充的标志字节法(3)比特填充的标志比特法(4)物理层编码违禁法
    • 字节计数法。利用头部中的一个字段表示该帧中的字符数。接收方因此知道该帧有多少字节、在哪里结束,如图a。这个算法的问题在于计数值可能因一个传输错误而被弄混。例如第二帧中的计数值5因为一个比特值反转变成7,如图b,那么接收方就会失去同步,它再也不可能找到下一帧的开始处。即使要求重传,接收方也不知道该跳过多少字节才能到达重传的开始处,因而字节计数法使用很少。
      在这里插入图片描述
    • 字节填充法。它让每个帧用一些特殊的字节作为开始和结束,这些字节称为标志字节。如图a中的FLAG所示。两个连续的标志性字节代表了一帧的结束和开始。因此接收方失去同步也能通过搜索两个标志性字节找到当前帧的结束和下一帧的开始。当标志性字节出现在数据中时,采用特殊的转义字符(ESC)插入其前;接收方的数据链路层在将数据传输给网络层之前必须删除转义字节。如果转义字符也出现在数据中,同样使用一个转义字符插入其前,接收方将删除第一个转义字符。如图b,图中描述的字节填充方案是PPP(Pont-to-Point Protocol)协议使用的略微简化形式(PPP在SONET/SDH链路中使用的是0比特填充法)。在这里插入图片描述
    • 比特填充法。帧的划分可以在比特级完成,因而帧可以包含任意大小单元(而不只是以8比特为单元)组成的二进制比特数。这种方法是为曾经流行的HDLC(高级数据链路控制)协议开发。每帧的开始和结束由一个特殊的比特模式,01111110或十六进制的0x7E标记。这种模式是一个标志性字节。每当发送方的数据链路层在数据中遇到连续5个1,它便自动在输出的比特流中插入一个比特0,接收方会自动删除插入的0。比特填充确保了转换的最小密度,这将有助于物理层保持同步。USB(通用串行总线)采用了比特填充技术。比特填充保证了标志字节只可能出现在帧的边界,而不会出现在帧内数据中。其副作用是一帧的长度取决于它所携带的数据内容。例如,数据中没有标志字节,100个字节数据或许只需要一个100字节左右的帧所携带;然而如果数据全由标志性字节组成,那么每个字节中插入1个0,帧的增幅大约为12.5%。
    • 物理编码违禁法。物理层中叙述的比特编码成信号方法通常包含一些冗余比特,以便帮助接收器同步接收。例如在4B/5B线性编码模式下,4个数据位被映射成5个信号比特,通过这种方法确保线路上的信号有足够的跳变。但32个可用的信号中有16个不会被使用,我们可以利用这些保留的信号来指示帧的开始和结束。
    • 许多链路层协议为安全起见综合使用了这些方法。以太网和802.11使用了共同的分界模式,即用一个定义良好的的比特模式来标识一帧的开始,该比特模式称为前导码。这种定界模式可能很长(802.11典型使用72位),目的是让接收方准备接收输入的数据包。前导码之后是头的长度字段(即计数),这个字段将被用来定位帧的结束处。

    1.3差错控制

    • 对于可靠的、面向连接的服务,数据链路层还需要确保所有的帧被传输到目标机器,并且保持正确的顺序。常用方法是向发送方提供一些有关线路另一端状况的反馈信息。通常情况下,协议要求接收方发回一些特殊的控制帧来对它所接收到的帧进行肯定和否定的确认,如果丢失帧,那么这帧必须重传。
    • 如果一帧完全丢失,接收方没有任何根据做出反应,更不知如何处理。在一个协议中,发送方发出来一帧之后就等待肯定或者否定的确认,为了防止接收方的确认帧也丢失,数据链路层引入了计时器。当发送方发出一帧时,还要启动一个计时器。该计时器的超时值应该设置得足够长,以便保证在正常情况下,该帧能够到达接收方,并在接收方处理后再将确认帧返回到发送方。一般情况下,在计时器超时前,该帧应该被正确接收,并且确认帧也被传了回来,在这种情况下,计时器被取消。否则,如果帧丢失或者确认帧丢失,计时器将被触发,从而警告发送方存在一个潜在的问题,一种解决方案就是重传,然而,当有的帧多次发送后,接收方可能多次收到同一帧,并将它多次传输给网络层。为了避免这种情形,一般有必要给发出去的帧分配序号,这样接收方可以根据帧的序号来区分原始帧和重传帧。
    • 管理好计时器以便保证每一帧最终都恰好一次被传输给目标机器的网络层,这是数据链路层(以及上层)工作的重要组成部分。

    1.4流量控制

    • 另一个设计问题是如果发送方发送帧的速度超过了接收方接收这些帧的速度,发送方该如何处理。当发送方运行在一台性能强大的计算机上,而接收方运行在一台性能差的机器上时,这种情况便会容易发生。例如一个智能手机向一个超强服务器请求一个web页面,接收方将无法以数据到来的速度那样快地处理持续到来的帧,此时必然会丢弃一些帧。
    • 解决方案有两种。一种是基于反馈的流量控制,接收方给发送方返回信息,允许它发送更多的数据,或者至少告诉发送方自己的情况如何。一种是基于速率的流量控制,使用这种方案的协议有一种内置的机制,它能限制发送方传输数据的速率,而无需利用接收方的反馈信息。
    • 本章将学习基于反馈的流量控制方案,因为基于速率的方案仅在传输层的一部分可见,而基于反馈的方案则可同时出现在链路层和更高的层次。后者近年来较为常见,在这种情况中,链路层硬件设计的运行速度足够快到不会造成丢帧。例如,作为链路层硬件实现的网络接口卡(NIC),有时声称能以“线速”运行,这意味着它们能以帧到达的速度来处理帧。因而,任何过载不在是链路层的问题,它们必须由高层处理。基于反馈的流量控制方案有多种,但大多数使用了相同的基本原理。协议包含了许多定义良好的规则,这些规则规定了发送方什么时候可以发送下一帧,发送方在没有得到接收方允许之前禁止发送帧。

    2、差错检测和纠正

    引言

    • 通信信道有许多不同特征。光纤很少发生传输错误,其他信道,尤其是无线链路和老化的本地回路错误率相对光纤来说非常高。数据链路层需要处理这些传输错误。
    • 一种策略是在每一个发送的数据块中包含足够多的冗余信息,以便接收方能据此推断被发送的数据是什么。另一种策略也是包含一些冗余信息,但这些信息只能让接收方推断出是否出现传输错误(而不推断是哪个发生了错误),然后接收方请求重传。前一种策略使用了纠错码(也称前向纠错),后一种策略使用了检错码。在高度可靠的信道上(如光纤)较为合理的方法是使用检错码,当偶尔发生错误时重传整个数据块;在错误发生频繁的信道上(比如无线链路)则应使用纠错码,因为重传数据块也可能出错。无论是纠错码还是检错码都有可能出现传输错误,为了避免这种情况,编码必须足够强大到足以应付预期错误。
    • 在两种错误模型中,突发性错误相比单个比特错误各有特点。由于计算机总是成块发送的,假设数据块大小为1000个比特,误差率为每比特0.001。如果错误是独立的,大多数块将包含一个错误,但如果错误以100个比特的突发形式出现,则平均来说100块中只有一块受到影响。突发错误的缺点在于当它们发生时比单个错误更难以纠正。还有一种类型的错误,由于物理层接收到的一个模拟信号远离了0或1的预期值,因而可以宣布该比特被丢失。这种情况称为擦除信道。擦除信道的错误,我们可以知道哪个比特出了错,相比把比特值反转的信道更易于纠错。

    2.1纠错码

    • 常用纠错码:(1)海明码(2)二进制卷积码(3)里德所罗门码(4)低密度奇偶校验码。上述所有编码都将冗余信息加入到待发送信息中。一帧由m个数据位(信息)和r个冗余位(校检)组成。在码块中,r个校验码是作为与之相关的m个数据位的函数计算获得的,就好像在一张大表中找到m位数据对应的r校验位。在系统码中,直接发送m个数据位,然后发送r个校验位,而不是在发送前对它们进行编码。在线性码中,r个校验位是作为m个数据位的线性函数被计算出来的。异或(XOR)或者模2加是函数的流行选择,这意味着编码过程可以用诸如矩阵乘法或简单逻辑电路来完成。除非另有说明,本节中考察线性码、系统码和块状码。
    • 令数据块总长度为n(n=m+r),将此描述为(n,m)码,称为n码字。码率定义为m/n。实际上这个码率变化很大,在一个有噪声信道上码率或许是1/2;而在高品质信道上码率接近1,只有少数的校验位被添加到一个大块消息中。

    海明码

    • 给定两个被发送或接收的码字,为了确定有多少位不同,只需要异或两个数字,并计算两个结果中的1个数,1的个数称为海明距离。给定计算校检位的算法,完全可以构建一个完整的合法码字列表,然后从这两个列表中找出两个具有最小海明距离的码字,这个距离就是整个编码的海明距离。在大多数数据传输应用中,所有2m种可能的数据报文都是合法的;但是,根据校验位的计算方法,并非所有2n种可能的码字都会被用到。事实上,对于r位校验位,可能的报文中只有很少一部分是2m/2n(=1/2r)是合法的码字。正是这种空间稀疏的方法,使得接收方能检测并纠正错误。
    • 块码的检错和纠错特性跟它的海明距离有关。为了可靠地检测d个错误,需要一个距离为d+1的编码方案(如海明距离为2,编码类似于0011、1100,错误的码字是0001,可以知道码字至少有一个错误;如果海明距离为1,则编码类似于0000、0001,这样是无法检测错误码字的),当接收方看到一个无效码字时,它就知道传输发生了错误。类似的,为了纠正d个错误,需要一个距离为2d+1的编码方案,因为只有足够距离的编码方案才能达到纠错的目的。
    • 考虑一个只有下列4个有效码字的编码方案:0000000000、0000011111、1111100000、1111111111。该编码方案的距离是5,这意味着它可以纠正2个位错误或者检测4个位的错。如果接收到码字0000000111并且期望只有单个或2个错误,则接收方一定知道原始码字是0000011111。然而,如果发生3个错误,0000000000变成了0000000111,则就无法纠正错误了。很明显在这个例子中,我们不能同时纠正2个错误和检测4个错误,因为这需要我们用两种不同的方式来解释收到的码字(编码方案也有异)。
    • 设计一种编码方案,每个码字有m个消息位和r个校验位,并且能纠正所有单个错误。对于2m个合法消息,任何一个消息都对应有n个非法的码字,它们与该消息的距离为1。这些非法码字可以这样构成:将该消息对应的合法码字的n位,逐个取反,可以得到n个距离为1的非法码字。因此。每个2m中的合法消息需要n+1个位模式来标识它们(如果是n个位模式,那么只能用n个非法码字标识合法的消息了,显然是不合理的;如果有7个模式是非法字符,那么需要第8个模式标识合法消息,注意海明距离是1)。由于总共有2n个位模式,所有我们必须有(n+1)2m≦2n。由于n=m+r,因此上式可以化为(m+r+1)≦2r。在给定m的情况下,这个条件给出了纠正单个错误所需要的校检位数的下限。
    • 在海明码中,码字的位从左至右被连续编号。2的幂次方位(1,2,4,8等)是校检位,其余位用来填充m个数据位。如图所示,7个数据位和4个校检位。这11个比特都需要校检,第1个比特用第20=1校检位校检,第2个比特用第21=2校检位校检,第3个比特用第20=1、第21=2(1+2=3)两个校检位校检。换句话说,要检验k位,必须将k改写成2的幂次方之和,例如11=1+2+8,校检1、2和8位就可以确定11是否出错。
    • 此例子中将采用偶校检计算ASCII字母“A”的校检和。首先需要在数据中插入校检码,以2的幂次方插入:??1?000?001,第一个校检码p1需要校检的位包括1、3、5、7、9、11,并以此为一组,对应位数的值是?10001,这个组合值中1的个数是偶数,根据偶校检,组合值中1的个数需要保持偶数,那么确定?值是0。依此法,推算出校检码为0001,插入数据中。这种结构给出了海明距离为3的编码,意味着它可以纠正1个错误(或检测2个错误)。当接收到码字后,接收机器重新计算其校检位,得到校检结果,偶校检的结果必须全为0,否则数据就出现了差错。图中接收时,出现了1个错误。将1、3、5、7、9、11位上的组合值异或得到1,将2、3、6、7、10、11位上的组合值异或得到0,依次得到1010,将结果反转得0101,这个0101称为错误综合集,0101=5,按照设计方案,这意味着第五位有误。把不正确的位取反,就可以得到正确消息。
      在这里插入图片描述

    卷积码

    • 海明距离对理解块码是有价值的,而且海明码还被用在纠错存储器中。然而,大多数网络使用了更强大的编码。第二个要考察的编码是卷积码。这是我们讨论中唯一不属于块码的编码。在卷积码中,编码器处理一个输入位序列,并生成一个输出位序列。输出取决于当前的输入和以前的输入(在块码中没有自然消息大小和编码边界),也就是说编码器有内存。决定当前输出的以前输入位数称为代码的约束长度。卷积码由它们的速率和约束长度标识。
    • 卷积码已经被广泛应用于实际部署的网络中,例如它已经成为GSM移动电话系统的一部分,在卫星通信和802.11中都得到应用。图中给出了一个流行的卷积码。这个代码称为NASA(美国航天局)卷积码,其r=1/2和k=7,因为它是第一个被用在1977年的旅行者号航天飞行任务中的编码,现在已成为802.11的一部分。在这里插入图片描述
    • 图中(两个模2加法器,6个寄存器,1个寄存器对应1个位的值)左边每个输入位产生右边的两个输出位。输出位是输入位和内部状态的XOR和。由于它处理的是比特位并执行线性运算,因此是二进制的线性卷积码。又因为1个输入产生2个输出,因此码率为1/2。这里的输出不位是简单的输入位,从而它不属于系统码。内部状态保存在6个内存寄存器中。每当输入一位,寄存器的值就右移一位。例如,输入序列为111,初始状态都是0(6个寄存器中都是1个0值),则在输入第一、二、三位后从左至右的内部状态为100000、110000、111000。对应输出位分别是11、10和01(输入的第一个值是1,与000000异或,图中input到S1之间的数字是输入值1,该值被连接到一个输出对应的模2加法器,它连接了5个值,最终计算得模2加法器输出处输出1,另一个也是1,所以输入1时,对应输出值是11)。这个过程需要7次移位才能完全清除输入,从而不影响输出,因此该卷积码的约束长度是7。
    • 卷积码的解码过程是针对一个输入位序列,找出最有可能产生观察到的输出位序列。对于较小值的k,一种广泛使用的算法是由Viterbi开发的,该算法逐个检查观察到的序列,记住每一步和输入序列的每个可能的内部状态,即输入序列产生观察序列可能产生的错误。最终其中那个具有最少错误的输入序列就是最有可能的消息。
    • 卷积码实际上非常流行,它之所以很容易被采纳的一个因素在于解码0或1的不确定性。例如,假设-1V表示逻辑0,+1V表示逻辑1,接收方可能接收到的2位分别是0.9V和-0.1V。卷积码不是简单地将这些信号绝对映射成逻辑1和0,而是把0.9V看成“很可能是1”,把-0.1V看成“很可能是0”;从而最终获得正确的整个序列。Viterbi算法的扩展适用于这些不确定因素,因而能提供更强的纠错功能。这种带有1比特不确定性的工作方法称为软判决解码。相反,在执行纠错之前就决定了每个位是0或1的工作方法称为硬判决解码。

    里德所罗门码

    • 里德所罗门码是线性块码,而且往往也是系统码。但与海明码不同的是,里德所罗门码对m位符号进行操作,而不是针对单个位处理。由于这将涉及较多数学理论,所以我们将用类比方法描述该方案。
    • 里德所罗门码基于这样的事实:每一个n次多项式是由n+1点唯一确定的。例如,一条具有ax+b形式的线由两个点确定。同一条线上的额外点是冗余的,这将有助于纠错。可以想象有两个数据点代表了一条线,并且我们给这两个数据点额外加上两个校验点,该两个校验点选自同一条线。如果收到的其中一个点出现了错误,我们仍然可以通过接收点的拟合线来恢复这个数据点。三个点将处于同一直线上,而出错的那个点不在这条线上,只要找到这条线,我们就可以纠正错误。
    • 里德所罗门码实际上被定义成在一个有限域上操作的多项式,但工作方式相同。对于m位符号而言,码字长2m-1个符号。一种流行的选择是m=8,这样符号就是字节。因此,一个码字为255个字节长。(255,233)码被广泛使用,他在233个数据符号上增加了32个冗余符号。带有纠错功能的解码算法有Berlekamp 和 Massey开发,它能有效执行中等长度的解码。
    • 里德所罗门码得到广泛运用的原因还在于其强大的纠错性能,尤其针对突发性错误。它们被用在DSL、线缆上的数据通信、卫星通信、CD、DVD和蓝光光盘。因为它们基于m位符号,因此1位错误和m位突发错误都只是作为一个出错信号来处理。当加入2t个冗余符号后,里德所罗门码能够纠正传输符号中的任意t个错误。例如在(255,233)中,32个冗余符号可以纠正多达16个符号错误。因为符号是连续的,并且每个8位长,所以可以纠正高达128位的突发性错误。在这种情况下,高达2t个错误都可以更正。
    • 里德所罗门码通常与其他编码结合在一起使用,如卷积码。这种想法的依据在于:卷积码在处理孤立的比特错误时很有效,但当接收到的比特流中有太多的错误(和突发性错误类似),卷积码就无法处理了。在卷积码中加入里德所罗门码,两者的结合就能将纠错任务完成得很好,综合起来的编码模式对单个错误和突发性错误都有良好的保障作用。

    低密度奇偶校检码

    • 低密度奇偶校检码(LDPC)是线性码,由Robert Gallagher在它的博士论文中首次提出。LDPC码中的每个输出位由一小部分的输入位形成。这样使得编码可以用一个1的密度很低的矩阵来表示,这也是编码名称的由来。接收到的码字通过一个近似算法解码获得,该算法通过迭代不断改进接收到的数据与合法码字的最佳匹配,如此纠正错误。LDPC码比较适用于大块数据,而且具有出色的纠错能力,因而性能优于其他许多编码(包括前述)。真是基于这个原因,它们迅速被新的协议所采纳,成为数字视频广播、万兆以太网、电力线网络,以及最新版802.11标准的一部分。

    2.2检错码

    纠错编码被广泛应用于无线链路。相比光纤,无线链路很容易嘈杂不堪而且容易出错,如果没有纠错码,将很难从该链路获得任何信息。然而,光纤或者高品质铜线的错误率要低得多,因此对于偶尔出现的错误采用差错检测和重传的处理方式通常更加有效。常用检错码:(1)奇偶(2)校检和(3)循环冗余校检(CRC)

    奇偶

    • 把单个奇偶校检位附加到数据中。奇偶位的选择原则是使得码字中比特1的数目是偶数(或奇数)。这样处理等同于对数据位进行模2加或异或操作来获得奇偶位。例如当以偶校检方式发送1011010时,在数据末尾在数据末尾加一位0,保证数据中1的个数是偶数;否则添加1。具有单个校检位的编码具有码距2,因为任何1位错误都将使得码字的奇偶校检码出错,这意味着奇偶码可以检测出1位错误。
    • 考虑这样的信道,其上发生的错误都是孤立的。令数据块大小为1000位,根据公式(m+r+1)≦2r可知需要10个校检位;因此1兆大小的数据块将需要10000个校检位。如果只为检测出该块数据中是否存在1位错误,每块数据仅一个校检位就足够了,每1000个数据块中有1块出现错误,只需要重传额外的一块(1000个数据位加上1个校检位)即可修复错误,每1兆数据用于错误检测和重传的总开销只有2001位,相比海明码需要的10000位显然要高效。这种校检方案的困难在于单个检验位只能可靠地检测出1位错误。如果数据块因一个长的突发错误造成严重乱码,那么这种错误检测出来的概率只有1/2(数据位异或为偶为奇各占1/2),显然这是令人难以接受的。如果发送的每个数据块作为一个n位宽和k位高的长方形矩阵来处理,则检测出错误的概率可望得到很大提高,如果我们为每一行计算和发送一个校检位,只要每一行最多只有一个错误发生,那么我们就能可靠地检测出k位错误。
    • 为了提高对突发错误的检测能力,可以改变计算校检位的次序,即以不同于数据位发送的次序来计算校检位。这种处理方式就是所谓的交错校检。在这种情况下,我们将为n列中的每列计算校检位,按k行发送全部的数据位,发送次序是从上到下发送每一行,行内数据为通常按照从左到右的次序发送。在最后一行,发送n个校检位。这种传输如图,其中n=7,k=7。
      在这里插入图片描述
    • 交错校检是一种将检测(或纠正)单个错误的编码转换成能检测(或纠正)突发错误的通用性技术。在图中,放发生一个长度为n=7的突发错误,出错的位恰好分散在不同的列(突发错误并不隐含着所有的位都出错),n列中至多只有1位收到影响,因此这些列中的校检位将能检测到该错误。这种方法对于nk长度的数据块使用了n个校检位就能检测出一个长度小于等于n的突发错误。但是当一个长度为n+1或者一列中的两位都反转,那么这样的错误无法检测出来。如果数据块被一个长突发错误或多个短突发错误所扰乱,该n列中任何一列有正确校检位的概率偶尔有1/2,所以一个不该接收而被接受的坏块的概率是(1/2)n

    校检和

    • 第二类检错码是校检和,与一组奇偶校检位相关。校检和这个词通常用来指与信息相关的一组校检位,不管这些校检位是如何计算出来的。一组奇偶校检位是校检和的一个例子。然而,还有其他种类的校检和,强大的校检和基础是对消息中的数据位进行求和计算。校检和通常放置在消息的末尾,作为求和功能的补充。这样一来,通过对整个接收到的码字(包含了数据位和校检和)进行求和计算就能检测出错误。如果计算结果是0,则没有检测出错误。
    • 校检和的一个例子是16位的Internet校检和,作为IP协议的一部分用在所有Internet数据包中。该校验和是按16位字计算得出的消息位总和。由于此方法针对字而不是像奇偶校检那样针对位进行操作,因此奇偶校检没能检测出的错误此时仍然影响着校检和,从而能被检测出来。例如,如果两个字的最低位都从0错误变成了1,上述的奇偶校检将无法检测这个错误,但是两个1增加到16位校检和中将产生不同的结果,这个错误将被检测出来。
    • Internet校检和是以补码运算而非216模加运算得到的。在补码运算中,负数是其正数的按位补。现代计算机采用双补算法,负数是该数的补码加1。在双补计算机中,一个数的补码等价于模216加,并且任何高序位溢出将被放回低序位上。该算法为校检和提供了更均匀的数据覆盖面。否则两个高序位可能因相加、溢出而被丢失,从而对校检和没有任何作用。这种双补算法还有另一个好处,0的补码有两种表示方法:全0和全1。这就允许一个值(例如全0)表示没有校检和而不需要用额外一个字段作特别说明。
    • Internet校检和在某些情况下提供的保护很弱,正因为它只是一个简单的和。它检测不出0数据的增加或删除,也检测不出被替换的那部分,而且对两个数据包拼接起来的消息只有弱保护作用。这些错误在随机过程中似乎不太可能发生,但恰恰是一种发生在有缺陷硬件上的错误。一个更好的选择是Fletcher校检和。它包括一个位置组件,将数据和其位置的乘积添加到总和中。这样能对数据位置的变化提供更强大的检测作用。

    循环冗余校检码

    • 也称多项式编码。多项式编码的基本思想是:将位串看成是系数为0或1的多项式。一个k帧看做是一个k-1次多项式的系数列表,该多项式有k项,从Xk-1到X0。例如,110001有6位,因此代表了一个由6项的多项式,其系数分别为1、1、0、0、0和1:即1X5+1X41X3+1X2+1X1+1X0
    • 多项式的算术运算遵守代数域理论规则,以2为模来完成。加法没有进位,减法没有借位,等同于异或。使用多项式编码时,发送方和接收方必须预先商定一个生成多项式G(x)。生成多项式的最高位和最低位系数必须是1。假设1帧有m位,它对应于多项式M(x),为了计算它的CRC,该帧必须比生成多项式长。基本思想是在帧尾附加一个校检和,使得附加之后帧所对应的多项式能够被G(x)除尽。当接收方收到了带校检和的帧之后,它试着用G(x)去除它。如果有余数的话,则表明传输过程中有误。
    • 计算CRC的算法如下:(1)假设G(x)的阶为r。在帧的低位加上r个0位,使得该帧包含m+r位,对应多项式为XrM(x)。(2)利用模2除法,用对应于G(x)的位串去除对应于XrM(x)的位串。(3)利用模2减法,从对应于XrM(x)的位串中减去(相等于异或)余数(总是小于等于r位)。结果就是将被传输的带校检和的帧。它的多项式不妨设为T(x)。图中显示了多项式为G(x)=X4+X+1计算1101011111校检和的情形。在这里插入图片描述
    • 显然T(x)可以被G(x)除尽。在任何一种除法中,如果将被除数减掉余数,则剩下的差值一定可以被除数除尽。想象一下在传输过程中发生了一个错误,因此接收方收到的不是T(x),而是T(x)+E(x),其中E(x)对应的每位1都变反了。如果E(x)中有k个1,则表明发生了k个错误。单个突发错误可以这样描述:初始位是1,然后是0和1的混合,最后一位也是1,所有其他位都是0。接收方在收到了带校检和的帧之后,用G(x)来除它,即[T(x)+E(x)]/G(x)。T(x)/G(x)是0,因此计算结果简化为E(x)/G(x)。如果错误恰好发生在作为因子的G(x)多项式中,那么将无法检测到,其他所有错误版本都能够被检测出来。如果只有一位发生错误,即E(x)=xi,这里 i 决定了错误发生在哪一位上。如果G(x)包含两项或者更多项,则它永远也除不尽E(x),所以,所有的一位错误都将被检测到。
    • 如果有两个独立的一位错误,则E(x)=xi+xj,这里 i > j 。如果我们假定G(x)不能被x除尽,则所有的双位错误都能够检测出来的充分条件是:对于任何小于等于i - j 最大值(即小于等于最大帧长)的k值,G(x)都不能除尽xk+1。简单地说,低阶的多项式可以保护长帧。例如,对于任何 k <32768,x15+x14+1都不能除尽xk+1。如果有奇数个位发生了错误,则E(x)包含奇数项(比如x5+x2+1,但不能是x2+1)。有意思的是,在模2系统中,没有一个奇数项多项式包含x+1因子。因此,以x+1作为G(x)的一个因子,我们就可以捕捉到所有包含奇数个位变反的错误情形。
    • 最后,也是最重要的,带r个校检位的多项式编码可以检测到所有长度小于等于r的突发错误。长度为k的突发错误可以用xi(xk-1+xk-2+……+1)来表示,这里 i 决定了突发错误的位置距离帧的最右端有多远。如果G(x)包含一个x0的项,则它不可能有xi因子,所以,如果括号内表达式的阶小于G(x)的阶,则余数永远不可能为0。如果突发错误的长度为r+1,则当且仅当突发错误与G(x)一致时(括号表达式与G(x)相同),错误多项式除以G(x)的余数才为0。根据突发错误的定义,第一位和最后一位必须为1,所以它是否与G(x)匹配取决于其他 r-1 个中间位。如果所有的组合被认为是等概率的话,则这样一个不正确的帧被当做有效帧接收的概率是1/2r-1。同样可以证明,当一个长度大于r+1位的突发错误发生时,或者几个短突发错误发生时,一个坏帧这当做有效帧通过检测的概率为1/2r,这里假定所有的帧模式都是等概率的。
    • 一些特殊帧已经成为国际标准,其中一个被IEEE 802用在以太网示例中,多项式为:x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x1+1
    • 除了其他优良特性外,该多项式还能检测到长度小于等于32的所有突发错误,以及影响到奇数位的全部突发错误,20世纪80年代后它得到了广泛应用。Castagnoli (1993) 和 Koopman (2002)采用穷尽计算搜索,发现了最好的CRC。这些CRC针对典型消息长度获得的海明距离为6,而IEEE标准的CRC-32的海明距离只有4。虽然计算CRC所需的运算看似复杂,但硬件上通过简单的移位寄存器电路很容易计算和验证CRC,实际上,这样的硬件几乎一直被使用着。数十种网络标准采用了不同的CRC,包括几乎所有的局域网(如以太网,802.11)和点到点链路(SONET上的数据包)。
    展开全文
  •  JPCAP实际上并非一个真正去实现对数据链路层的控制,而是一个中间件,JPCAP调用wincap/libpcap,而给JAVA语言提供一个公共的接口,从而实现了平台无关性。官方网站上声明,JPCAP支持FreeBSD 3.x, Linux Red...

    众所周知,Java语言虽然在TCP/UDP传输方面给予了良好的定义,但对于网络层以下的控制,却是无能为力的。JPCAP扩展包弥补了这一点。

         JPCAP实际上并非一个真正去实现对数据链路层的控制,而是一个中间件,JPCAP调用wincap/libpcap,而给JAVA语言提供一个公共的接口,从而实现了平台无关性。在官方网站上声明,JPCAP支持FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft windows 2000/XP等系统。

         使用Jpcap能做的事:

    1.Jpcap是直接抓取经过数据链路层的数据包。 因此可以自己写IP数据包直接发送给数据链路层。

    2.Jpcap会对抓取到的数据包进行一定程序的解析,根据数据包内容,将数据包封装为对应的对象()。

    3.根据用户设定的信息,过滤数据包(其实就是在解析的时候,对不需要的数据直接丢弃,不解析)

    4.Jpcap 只是直接从数据链路层上读取数据,并向数据链路层中发送数据,因此,Jpcap并不能操作 其他程序从数据链路层中读数据或者向网卡中发送数据。(IP层协议程序)

    二话不说首先我们先上项目结构

    所需要的jar包我已打包好

    0、客户端  InfraredSensorClient.java

    package com.airtimes.netty.network.client;
    
    
    import com.airtimes.netty.network.client.handler.InfraredSensorClientHandler;
    
    import io.netty.bootstrap.Bootstrap;
    
    import io.netty.channel.ChannelFuture;
    
    import io.netty.channel.ChannelInitializer;
    
    import io.netty.channel.ChannelOption;
    
    import io.netty.channel.EventLoopGroup;
    
    import io.netty.channel.nio.NioEventLoopGroup;
    
    import io.netty.channel.socket.SocketChannel;
    
    import io.netty.channel.socket.nio.NioSocketChannel;
    
    
    /**
     * 
     * 服务器持续监听宿主机的网卡信息、并时时返回报警中心协议在网卡中交换的数据
     * 客户端接收服务器端数据,打印出服务器送的消息
     * 
     */
    
    public class InfraredSensorClient {
    
        public static void main(String[] args)
            throws Exception {
    
            // 创建EventLoopGroup线程池 Netty内部都是通过线程在处理数据
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                // 创建客户端连接基类 用于连接服务器
                Bootstrap bootstrap = new Bootstrap(); // (1)
                // 将 EventLoopGroup 加入线程池
                bootstrap.group(workerGroup); // (2)
                // 使用changlefactory 创建通信频道
                bootstrap.channel(NioSocketChannel.class); // (3)
                // Socket网路属性
                bootstrap.option(ChannelOption.SO_KEEPALIVE, true); // (4)
                // channel处理类
                bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                    // 初始化channel
                    @Override
                    public void initChannel(SocketChannel channel)
                        throws Exception {
    
                        channel.pipeline().addLast(new InfraredSensorClientHandler());
    
                    }
    
                });
    
                // Start the client.
                // 启动客户端
                ChannelFuture f = bootstrap.connect("127.0.0.1", 9090).sync(); // (5)
    
                // Wait until the connection is closed.
                // 等待连接关闭
                f.channel().closeFuture().sync();
    
            }
            finally {
                workerGroup.shutdownGracefully();
            }
    
        }
    }

                1、客户端Handler  InfraredSensorClientHandler .java

    package com.airtimes.netty.network.client.handler;
    
    
    import java.io.IOException;
    
    import com.airtimes.netty.network.response.Message;
    import com.airtimes.netty.network.tools.Util;
    import com.fasterxml.jackson.core.JsonParseException;
    import com.fasterxml.jackson.databind.JsonMappingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.util.ReferenceCountUtil;
    
    
    public class InfraredSensorClientHandler extends ChannelInboundHandlerAdapter {
    
        private static final Util util = new Util();
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
    
            try {
    
                ByteBuf m = (ByteBuf)msg;
                String response = util.convertByteBufToString(m);
                System.out.println("收到来自服务器json信息: " + response + "\n");
                ObjectMapper objectMapper = new ObjectMapper();
                Message ClientMessage = objectMapper.readValue(response, Message.class);
                System.out.println("收到来自服务器对象信息: " + ClientMessage.toString() + "\n");
    
            }
            catch (JsonParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch (JsonMappingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            finally {
    
                ReferenceCountUtil.release(msg);
    
            }
    
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }

               2、服务器端  InfraredSensorServer  .java

    package com.airtimes.netty.network.server;
    
    
    import com.airtimes.netty.network.server.handler.InfraredSensorServerHandler;
    
    import io.netty.bootstrap.ServerBootstrap;
    
    import io.netty.channel.ChannelFuture;
    
    import io.netty.channel.ChannelInitializer;
    
    import io.netty.channel.ChannelOption;
    
    import io.netty.channel.EventLoopGroup;
    
    import io.netty.channel.nio.NioEventLoopGroup;
    
    import io.netty.channel.socket.SocketChannel;
    
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    
    import io.netty.handler.timeout.ReadTimeoutHandler;
    
    import io.netty.handler.timeout.WriteTimeoutHandler;
    
    
    public class InfraredSensorServer {
    
        public static void main(String[] args) {
    
            // EventLoop 代替原来的 ChannelFactory
    
            EventLoopGroup bossGroup = new NioEventLoopGroup();
    
            EventLoopGroup workerGroup = new NioEventLoopGroup();
    
            try {
    
                ServerBootstrap serverBootstrap = new ServerBootstrap();
    
                serverBootstrap.group(bossGroup, workerGroup)
    
                    .channel(NioServerSocketChannel.class)
    
                    .childHandler(new ChannelInitializer<SocketChannel>() {
    
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
    
                            ch.pipeline().addLast(
    
                                new InfraredSensorServerHandler(),
    
                                new WriteTimeoutHandler(10),
    
                                // 控制写入超时10秒构造参数10表示如果持续10秒钟都没有数据写了,那么就超时。
    
                                new ReadTimeoutHandler(10)
    
                    );
                        }
    
                    }).option(ChannelOption.SO_KEEPALIVE, true);
    
                ChannelFuture f = serverBootstrap.bind(9090).sync();
    
                f.channel().closeFuture().sync();
    
            }
            catch (InterruptedException e) {
    
            }
            finally {
    
                workerGroup.shutdownGracefully();
    
                bossGroup.shutdownGracefully();
    
            }
    
        }
    }

         3、服务器端Handler  InfraredSensorServerHandler .java

    package com.airtimes.netty.network.server.handler;
    
    
    import java.io.IOException;
    import java.util.Date;
    import java.util.UUID;
    
    import com.airtimes.netty.network.response.Message;
    import com.airtimes.netty.network.tools.TranscodingUtil;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import jpcap.JpcapCaptor;
    import jpcap.NetworkInterface;
    import jpcap.packet.EthernetPacket;
    import jpcap.packet.IPPacket;
    import jpcap.packet.Packet;
    
    
    public class InfraredSensorServerHandler extends ChannelInboundHandlerAdapter {
    
        private static final int caplen = 65535;
    
        private static final boolean promiscCheck = true;
    
        private static int f = 0;
    
        private static int i = 0;
    
        // ChannelHandlerContext通道处理上下文
        @Override
        public void channelActive(final ChannelHandlerContext ctx)
            throws InterruptedException,
            JsonProcessingException { // (1)
    
            // 第一步绑定网络设备
            NetworkInterface[] devices = JpcapCaptor.getDeviceList();
            for (NetworkInterface n : devices) {
                System.out.println("获取到本地网卡信息列表" + n.name + "     |     " + n.description);
    
            }
            System.out.println("-----------------------------------------------------------------");
    
            JpcapCaptor jpcap = null;
    
            try {
                jpcap = JpcapCaptor.openDevice(devices[2], caplen, promiscCheck, 50);
                // 指定筛选条件
                jpcap.setFilter("src 192.168.208.200 && len > 220 && len < 300 ", true);
                /* jpcap.setFilter("src 192.168.208.200 or 192.168.208.199  and len < 230 ",true); */
                // 0 或 1
            }
            catch (IOException e) {
                e.printStackTrace();
            }
    
            // 第二步抓包
            while (true) {
                Packet packet = jpcap.getPacket();
                if (packet instanceof IPPacket && ((IPPacket)packet).version == 4) {
                    
                    i++ ;
                    IPPacket ip = (IPPacket)packet;// 强转
    
                    System.out.println("-------------------------------------第" + i + "次抓取数据包参数信息--------------------------------------------");
    
                    System.out.println("版本:" + ip.version);
                    System.out.println("优先权:" + ip.priority);
                    System.out.println("区分服务:最大的吞吐量: " + ip.t_flag);
                    System.out.println("区分服务:最高的可靠性:" + ip.r_flag);
                    System.out.println("长度:" + ip.length);
                    System.out.println("标识:" + ip.ident);
                    System.out.println("DF:Don't Fragment: " + ip.dont_frag);
                    System.out.println("NF:Nore Fragment: " + ip.more_frag);
                    System.out.println("片偏移:" + ip.offset);
                    System.out.println("生存时间:" + ip.hop_limit);
    
                    String protocol = "";
                    switch (new Integer(ip.protocol)) {
                        case 1:
                            protocol = "ICMP";
                            break;
                        case 2:
                            protocol = "IGMP";
                            break;
                        case 6:
                            protocol = "TCP";
                            break;
                        case 8:
                            protocol = "EGP";
                            break;
                        case 9:
                            protocol = "IGP";
                            break;
                        case 17:
                            protocol = "UDP";
                            break;
                        case 41:
                            protocol = "IPv6";
                            break;
                        case 89:
                            protocol = "OSPF";
                            break;
                        default:
                            break;
                    }
    
                    System.out.println("协议:" + protocol);
                    System.out.println("源IP " + ip.src_ip.getHostAddress());
                    System.out.println("目的IP " + ip.dst_ip.getHostAddress());
                    /*
                     * System.out.println("源主机名: " + ip.src_ip); System.out.println("目的主机名: " + ip.dst_ip);
                     */
                    TranscodingUtil transcodingUtil = new TranscodingUtil();
                    String hexString = transcodingUtil.toHexString(ip.data);
                    String hexStringToString = transcodingUtil.hexStringToString(hexString);
    
                    String hexStringHead = transcodingUtil.toHexString(ip.header);
                    String StringHead = transcodingUtil.hexStringToString(hexStringHead);
    
                    /**********************
                     * 0-31
                     * ******************************************/
                    String substring0 = hexString.substring(0, 32);
                    System.out.println("0-31频段数据UTF_8 :" + transcodingUtil.hexStringToStringUTF(substring0));
                    System.out.println("0-31频段数据GBK   :" + transcodingUtil.hexStringToStringGBK(substring0));
                    System.out.println("0-31频段数据ISO   :" + transcodingUtil.hexStringToStringISO(substring0));
                    System.out.println("0-31频段数据GB2312:" + transcodingUtil.hexStringToString(substring0));
                    System.out.println("0-31频段数据BIG5  :" + transcodingUtil.hexStringToStringBIG(substring0));
                    System.out.println("0-31频段数据UNICOD:" + transcodingUtil.hexStringToStringUNICODE(substring0));
                    System.out.println("0-31频段数据十进制      :" + transcodingUtil.transcoding(substring0));
                    System.out.println("0-31频段数据十六进制  :" + substring0);
                    System.out.println("---------------------------------------------------------------------------------");
    
                    /**********************
                     * 32-39 错误类型
                     * B7C0 C7F8 B8D0 D3A6 C6F7 B7C0 B2F0 BBD6 B8B4 防拆恢复
                     * ******************************************/
                    String substring = hexString.substring(32, 40);
                    System.out.println("32-39频段数据UTF_8 :" + transcodingUtil.hexStringToStringUTF(substring));
                    System.out.println("32-39频段数据GBK   :" + transcodingUtil.hexStringToStringGBK(substring));
                    System.out.println("32-39频段数据ISO   :" + transcodingUtil.hexStringToStringISO(substring));
                    System.out.println("32-39频段数据GB2312:" + transcodingUtil.hexStringToString(substring));
                    System.out.println("32-39频段数据BIG5  :" + transcodingUtil.hexStringToStringBIG(substring));
                    System.out.println("32-39频段数据UNICOD:" + transcodingUtil.hexStringToStringUNICODE(substring));
                    System.out.println("32-39频段数据十进制      :" + transcodingUtil.transcoding(substring));
                    System.out.println("32-39频段数据十六进制  :" + substring);
                    System.out.println("---------------------------------------------------------------------------------");
    
                    /**********************
                     * 40-78
                     * B7C0 C7F8 B8D0 D3A6 C6F7 B7C0 B2F0 BBD6 B8B4 防拆恢复
                     * ******************************************/
                    String substring6 = hexString.substring(40, 78);
                    System.out.println("40-78频段数据UTF_8 :" + transcodingUtil.hexStringToStringUTF(substring6));
                    System.out.println("40-78频段数据GBK   :" + transcodingUtil.hexStringToStringGBK(substring6));
                    System.out.println("40-78频段数据ISO   :" + transcodingUtil.hexStringToStringISO(substring6));
                    System.out.println("40-78频段数据GB2312:" + transcodingUtil.hexStringToString(substring6));
                    System.out.println("40-78频段数据BIG5  :" + transcodingUtil.hexStringToStringBIG(substring6));
                    System.out.println("40-78频段数据UNICOD:" + transcodingUtil.hexStringToStringUNICODE(substring6));
                    System.out.println("40-78频段数据十进制      :" + transcodingUtil.transcoding(substring6));
                    System.out.println("40-78频段数据十六进制  :" + substring6);
                    System.out.println("---------------------------------------------------------------------------------");
    
                    /**********************
                     * 79-103
                     * 
                     * ******************************************/
                    String substring4 = hexString.substring(79, 103);
                    System.out.println("79-103频段数据UTF_8 :" + transcodingUtil.hexStringToStringUTF(substring4));
                    System.out.println("79-103频段数据GBK   :" + transcodingUtil.hexStringToStringGBK(substring4));
                    System.out.println("79-103频段数据ISO   :" + transcodingUtil.hexStringToStringISO(substring4));
                    System.out.println("79-103频段数据GB2312:" + transcodingUtil.hexStringToString(substring4));
                    System.out.println("79-103频段数据BIG5  :" + transcodingUtil.hexStringToStringBIG(substring4));
                    System.out.println("79-103频段数据UNICOD:" + transcodingUtil.hexStringToStringUNICODE(substring4));
                    System.out.println("79-103频段数据十进制      :" + transcodingUtil.transcoding(substring4));
                    System.out.println("79-103频段数据十六进制  :" + substring4);
                    System.out.println("---------------------------------------------------------------------------------");
    
                    /*********
                     104-119
                     * ************************************************/
                    String substring2 = hexString.substring(104, 155);
                    // 月份
                    String month = hexString.substring(108, 110);
                    String transmonth = transcodingUtil.transcoding(month);
                    // 日份
                    String day = hexString.substring(110, 112);
                    String transday = transcodingUtil.transcoding(day);
                    // 时间
                    String hour = hexString.substring(112, 114);
                    String transhour = transcodingUtil.transcoding(hour);
                    // 分钟
                    String min = hexString.substring(114, 116);
                    String transmin = transcodingUtil.transcoding(min);
    
                    // 秒
                    String sec = hexString.substring(116, 119);
                    Integer transec = null;
                    if (Integer.parseInt(transcodingUtil.transcoding(sec)) > 6) {
    
                        transec = Integer.parseInt(transcodingUtil.transcoding(sec)) - 6;
                        System.err.println("D" + transec);
                    }
                    else {
    
                        transec = Integer.parseInt(transcodingUtil.transcoding(sec));
                        System.err.println("X" + transec);
                    }
    
                    System.out.println("104-119频段数据UTF_8 :" + transcodingUtil.hexStringToStringUTF(substring2));
                    System.out.println("104-119频段数据GBK   :" + transcodingUtil.hexStringToStringGBK(substring2));
                    System.out.println("104-119频段数据ISO   :" + transcodingUtil.hexStringToStringISO(substring2));
                    System.out.println("104-119频段数据GB2312:" + transcodingUtil.hexStringToString(substring2));
                    System.out.println("104-119频段数据BIG5  :" + transcodingUtil.hexStringToStringBIG(substring2));
                    System.out.println("104-119频段数据UNICOD:" + transcodingUtil.hexStringToStringUNICODE(substring2));
                    System.out.println("104-119频段数据拼接后时间序列:" + transmonth + "-" + transday + " " + transhour + ":"
                                       + transmin + ":" + transec);
                    System.out.println("104-119频段数据十进制      :" + transcodingUtil.transcoding(substring2));
                    System.out.println("104-119频段数据—月份      :" + transmonth + "月");
                    System.out.println("104-119频段数据—日期      :" + transday + "日");
                    System.out.println("104-119频段数据—小时      :" + transhour + "时");
                    System.out.println("104-119频段数据—分钟      :" + transmin + "分钟");
                    System.out.println("104-119频段数据— 秒       :" + transec + "秒");
                    System.out.println("当前时间戳:" + System.currentTimeMillis());
                    System.out.println("104-119频段数据十六进制  :" + substring2);
                    System.out.println("---------------------------------------------------------------------------------");
    
                    /*********
                     120-197
                    * ************************************************/
    
                    String substring5 = hexString.substring(120, 197);
    
                    System.out.println("120-197频段数据UTF_8 :" + transcodingUtil.hexStringToStringUTF(substring5));
                    System.out.println("120-197频段数据GBK   :" + transcodingUtil.hexStringToStringGBK(substring5));
                    System.out.println("120-197频段数据ISO   :" + transcodingUtil.hexStringToStringISO(substring5));
                    System.out.println("120-197频段数据GB2312:" + transcodingUtil.hexStringToString(substring5));
                    System.out.println("120-197频段数据BIG5  :" + transcodingUtil.hexStringToStringBIG(substring5));
                    System.out.println("120-197频段数据UNICOD:" + transcodingUtil.hexStringToStringUNICODE(substring5));
                    System.out.println("120-197频段数据十进制      :" + transcodingUtil.transcoding(substring5));
                    System.out.println("120-197频段数据十六进制  :" + substring5);
                    System.out.println("---------------------------------------------------------------------------------");
    
                    /*********
                     198-220
                     * ************************************************/
                    String substring3 = hexString.substring(198, 310);
                    System.out.println("198-+频段数据UTF_8 :" + transcodingUtil.hexStringToStringUTF(substring3));
                    System.out.println("198-+频段数据GBK   :" + transcodingUtil.hexStringToStringGBK(substring3));
                    System.out.println("198-+频段数据ISO   :" + transcodingUtil.hexStringToStringISO(substring3));
                    System.out.println("198-+频段数据GB2312:" + transcodingUtil.hexStringToString(substring3));
                    System.out.println("198-+频段数据BIG5  :" + transcodingUtil.hexStringToStringBIG(substring3));
                    System.out.println("198-+频段数据UNICOD:" + transcodingUtil.hexStringToStringUNICODE(substring3));
                    System.out.println("198-+频段数据十进制      :" + transcodingUtil.transcoding(substring3));
                    System.out.println("198-+频段数据十六进制  :" + substring3);
    
                    /*********
                                                      获取主机mac地址
                    *************************************************/
                    EthernetPacket datalink = (EthernetPacket)ip.datalink;
                    StringBuffer srcMacStr = new StringBuffer();
                    int count = 1;
                    for (byte b : datalink.src_mac) {
                        String string = Integer.toHexString(b & 0xff);
                        if (string.length() == 1) {
                            string = "0" + string;
                        }
                        srcMacStr.append(string);
                        if (count++ != datalink.src_mac.length) srcMacStr.append(":");
                    }
                    System.out.println("获取主机mac地址为: " + srcMacStr);
                    System.out.println("00:74:9c:e5:e0:01".equals(srcMacStr.toString()));
    
                    /*********
                     *  数据汇总
                     *************************************************/
                    
                    
                    
                    String romateName ="3030315FB1A8BEAFD6F7BBFA";
                    
                    String transRomateName = transcodingUtil.hexStringToStringGBK(romateName);
                    System.out.println(transRomateName);
    
                    System.out.println("*************************数据汇总***************************************");
    
                    System.out.println("数据包十六进制:" + hexString);
                    System.out.println("数据包十六进制字符串数据包:" + hexStringToString);
                    System.out.println("数据去向:" + ip.datalink);
                    System.out.println("限制:" + ip.hop_limit);
                    System.out.println("报文头十六进制:" + hexStringHead);
                    System.out.println("报文头十进制:" + transcodingUtil.transcoding(hexStringHead));
                    System.out.println("报文头十六进制字符串:" + StringHead);
                    System.out.println("ip对象包:" + ip);
                    /* System.err.println("报文头中时间戳序列 :"+transcodingUtil.stampToDate(ip.sec)); */
                    /* System.out.println("ip对象包:"+ip.sec); */
                    System.out.println("系统时间:" + new Date());
                    
                    f++ ;
    
                    System.out.println("----------------------------本次抓取数据结束--累计" + f  + "---------------------------------------------------");
                    System.out.println();
    
                    /*********
                     *	将消息发送客户端汇总
                     *************************************************/
                    Message message = new Message();
                    message.setCurrentTime(new Date(System.currentTimeMillis()).toLocaleString());
                    message.setEventDescription(transcodingUtil.hexStringToStringGBK(substring6).trim());
                    message.setSystemCode(transcodingUtil.hexStringToStringGBK(substring));
                    message.setEventId(UUID.randomUUID().toString());
                    message.setSrcMac(srcMacStr.toString());
                    
                    ObjectMapper objectMapper = new ObjectMapper();
                    String json = objectMapper.writeValueAsString(message);
                    
                    /*String test = transcodingUtil.hexStringToString(substring);*/
                    ctx.writeAndFlush(Unpooled.wrappedBuffer(json.getBytes()));
    
                }
            }
    
        }
    
        @Override
    
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    
            cause.printStackTrace();
    
            ctx.close();
    
        }
    }

    4、消息实体 Message.java

    package com.airtimes.netty.network.response;
    
    public class Message {
        
        /**
         * 事件id
         */
        private String eventId;
        
        /**
         * src_mac
         */
        
        private String srcMac;
        
        /**
         * 系统码
         */
        private String systemCode;
        
        /**
         * 事件描述
         */
        private String eventDescription;
        
        /**
         * 子系統
         */
        private Integer masterArea;
        
        /**
         * 防区
         */
        private Integer slaverId;
        
        /**
         * 事件时间
         */
        private String currentTime;
    
        public String getEventId() {
            return eventId;
        }
    
        public void setEventId(String eventId) {
            this.eventId = eventId;
        }
    
        public String getSystemCode() {
            return systemCode;
        }
    
        public void setSystemCode(String systemCode) {
            this.systemCode = systemCode;
        }
    
        public String getEventDescription() {
            return eventDescription;
        }
    
        public void setEventDescription(String eventDescription) {
            this.eventDescription = eventDescription;
        }
    
        public Integer getMasterArea() {
            return masterArea;
        }
    
        public void setMasterArea(Integer masterArea) {
            this.masterArea = masterArea;
        }
    
        public Integer getSlaverId() {
            return slaverId;
        }
    
        public void setSlaverId(Integer slaverId) {
            this.slaverId = slaverId;
        }
    
        public String getCurrentTime() {
            return currentTime;
        }
    
        public void setCurrentTime(String currentTime) {
            this.currentTime = currentTime;
        }
    
        public String getSrcMac() {
            return srcMac;
        }
    
        public void setSrcMac(String srcMac) {
            this.srcMac = srcMac;
        }
    
        @Override
        public String toString() {
            return "Message [eventId=" + eventId + ", srcMac=" + srcMac + ", systemCode=" + systemCode
                   + ", eventDescription=" + eventDescription + ", masterArea=" + masterArea + ", slaverId=" + slaverId
                   + ", currentTime=" + currentTime + "]";
        }
    
        
    }
    

    5、工具类转换 TranscodingUtil .java 

    package com.airtimes.netty.network.tools;
    
    
    import java.math.BigInteger;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    
    public class TranscodingUtil {
    
        /**
         * 接收的byte包转换成16进制字符串
         * 
         * @param req
         * @return
         */
        public static String toHexString(byte[] req) {
            // 进行编码转译 byte --> hexString (字母大写)
            String str = "";
            for (int i = 0; i < req.length; i++ ) {
                String hex = Integer.toHexString(req[i] & 0xFF);
                if (hex.length() == 1) {
                    hex = "0" + hex;
                }
                str += hex.toUpperCase();
            }
            return str;
        }
    
        /**
         * 接收16进制字符串转换成10进制字符串
         * 
         * @param string
         * @return coding
         */
        public static String transcoding(String string) {
    
            // 传过来字符串的16进制字符个数
            int length = string.length() / 2;
    
            // 将字符串存进数组
            String[] s = new String[length];
            for (int i = 0; i < length; i++ ) {
                String ss = string.substring(i * 2, 2 + i * 2);
                s[i] = String.valueOf(ss);
            }
    
            // 定义接收转译之后的数组
            String[] str = new String[s.length];
            // 进行16进制向10进制转换
            for (int j = 0, k = s.length; j < k; j++ ) {
                str[j] = new BigInteger(s[j], 16).toString(10);
            }
    
            // 将转移后的数组转换为字符串
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < str.length; i++ ) {
                sb.append(str[i]);
            }
            String coding = sb.toString();
    
            return coding;
        }
    
        /**
         * 16进制字符串转换为字符串
         *
         * @param s
         * @return
         */
        public static String hexStringToString(String s) {
            if (s == null || s.equals("")) {
                return null;
            }
            s = s.replace(" ", "");
            byte[] baKeyword = new byte[s.length() / 2];
            for (int i = 0; i < baKeyword.length; i++ ) {
                try {
                    baKeyword[i] = (byte)(0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                s = new String(baKeyword, "GB2312");
                new String();
            }
            catch (Exception e1) {
                e1.printStackTrace();
            }
            return s;
        }
    
        public static String hexStringToStringUTF(String s) {
            if (s == null || s.equals("")) {
                return null;
            }
            s = s.replace(" ", "");
            byte[] baKeyword = new byte[s.length() / 2];
            for (int i = 0; i < baKeyword.length; i++ ) {
                try {
                    baKeyword[i] = (byte)(0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                s = new String(baKeyword, "utf-8");
                new String();
            }
            catch (Exception e1) {
                e1.printStackTrace();
            }
            return s;
        }
    
        public static String hexStringToStringGBK(String s) {
            if (s == null || s.equals("")) {
                return null;
            }
            s = s.replace(" ", "");
            byte[] baKeyword = new byte[s.length() / 2];
            for (int i = 0; i < baKeyword.length; i++ ) {
                try {
                    baKeyword[i] = (byte)(0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                s = new String(baKeyword, "gbk");
                new String();
            }
            catch (Exception e1) {
                e1.printStackTrace();
            }
            return s;
        }
    
        public static String hexStringToStringISO(String s) {
            if (s == null || s.equals("")) {
                return null;
            }
            s = s.replace(" ", "");
            byte[] baKeyword = new byte[s.length() / 2];
            for (int i = 0; i < baKeyword.length; i++ ) {
                try {
                    baKeyword[i] = (byte)(0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                s = new String(baKeyword, "GB18030");
                new String();
            }
            catch (Exception e1) {
                e1.printStackTrace();
            }
            return s;
        }
    
        public static String hexStringToStringBIG(String s) {
            if (s == null || s.equals("")) {
                return null;
            }
            s = s.replace(" ", "");
            byte[] baKeyword = new byte[s.length() / 2];
            for (int i = 0; i < baKeyword.length; i++ ) {
                try {
                    baKeyword[i] = (byte)(0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                s = new String(baKeyword, "big5");
                new String();
            }
            catch (Exception e1) {
                e1.printStackTrace();
            }
            return s;
        }
    
        public static String hexStringToStringUNICODE(String s) {
            if (s == null || s.equals("")) {
                return null;
            }
            s = s.replace(" ", "");
            byte[] baKeyword = new byte[s.length() / 2];
            for (int i = 0; i < baKeyword.length; i++ ) {
                try {
                    baKeyword[i] = (byte)(0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                s = new String(baKeyword, "unicode");
                new String();
            }
            catch (Exception e1) {
                e1.printStackTrace();
            }
            return s;
        }
    
        public static String[] bytesToHexStrings(byte[] src) {
    
            if (src == null || src.length <= 0) {
    
                return null;
    
            }
    
            String[] str = new String[src.length];
    
            for (int i = 0; i < src.length; i++ ) {
    
                String hex = Integer.toHexString(src[i] & 0xFF);
                if (hex.length() == 1) {
    
                    hex = "0" + hex;
                }
    
                hex.join(",", str);
    
                /* string.Join(",", s); */
            }
    
            return str;
    
        }
    
        public static String toString(byte[] a) {
            if (a == null) return "null";
            int iMax = a.length - 1;
            if (iMax == -1) return "[]";
    
            StringBuilder b = new StringBuilder();
            b.append('[');
            for (int i = 0;; i++ ) {
                b.append(a[i]);
    
                if (i == iMax) return b.append(']').toString();
                b.append(", ");
            }
        }
    
        /*
         * 将时间戳转换为时间
         */
        public static String stampToDate(Long s) {
            String res;
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            long lt = new Long(s);
            Date date = new Date(lt);
            res = simpleDateFormat.format(date);
            return res;
        }
    
      public String convertByteBufToString(ByteBuf buf) {
    
            String str;
    
            if (buf.hasArray()) { // 处理堆缓冲区
                str = new String(buf.array(), buf.arrayOffset() + buf.readerIndex(), buf.readableBytes());
            }
            else { // 处理直接缓冲区以及复合缓冲区
                byte[] bytes = new byte[buf.readableBytes()];
                buf.getBytes(buf.readerIndex(), bytes);
                str = new String(bytes, 0, buf.readableBytes());
            }
            return str;
        }
    
        public String Object2Json(Object obj) {
            JSONObject json = JSONObject.fromObject(obj);// 将java对象转换为json对象
            String str = json.toString();// 将json对象转换为字符串
    
            return str;
        }
    
    }
    

    6、运行结果:服务器端启动后台效果

     7、运行结果:客户端接收到服务器端报警信息

     8、运行结果:服务器端启动后台效果

     本文主要是对防区红外线设备、的报警信息通过报警主机之间通信截获报警数据、数据类型、防区报警、以及报警级别数据、进行捕捉解析转发、使用Netty Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序、当有客户端请求连接时、客户端将会收到服务器广播的异常信息、文中主要是以Json字符串类型、

     

    本文出自 “想学Python的Java程序猿” 博客,请务必保留此出处郑重声明转载请注明原地址、谢谢https://blog.csdn.net/qq_37606336/article/details/85045785

     

    展开全文
  • 在数据链路层会把数据进行封闭,封装后的数据叫作帧。帧的组成如下: 前导码(8字节)+目的MAC地址(6字节)+源MAC地址(6字节)+类型(2字节)+数据包(46~1500字节)+FCS(字节) MAC地址是由48位组
  • 最近进行一些路由软件的编程,发现自己对数据报格式并不是十分清楚,所以就查阅了相关资料,总结如下,供大家参考,也可以称为自己日后的工具。 图中括号中的数字代表的是当前域所占的空间大小,单位是...
  • 交换机 数据链路层解析了解数据链路层以太网和数据帧了解交换机的工作原理 了解数据链路层 以太网和数据帧 了解交换机的工作原理 1.了解数据链路层 数据链里层位于网络层和物理层之间,它的主要功能有 链路的建立 ...
  • 3.0 数据链路层概述

    2021-02-08 08:53:58
    帧:在数据链路层上传输的数据包,即数据链路层以帧为单位传输和处理数据。 2.数据链路层的三个重要问题:封装成帧、差错检测、可靠传输。 封装成帧:将数据链路层给网络层交付的协议数据单元,添加帧头和帧...
  • 数据链路层之以太网

    2019-02-17 21:23:32
    由上图可知,在 IP 数据包交付的过程中,在数据链路层会对数据包进行添加报头信息。此时就要引出一个新的概念——以太网。那么以太网是什么呢? 以太网不是一种具体的网络,而是一种技术标准。它既...
  • 数据链路层 物理层

    2019-04-29 23:12:53
    数据链路层功能 组帧(fraimg): 将高层网络层数据包封装成数据帧,增加首部,尾部 帧同步 从物理层的比特流中识别出数据帧 链路接入 若果是共享介质,需要解决信道接入 帧首部的MAC地址,用于识别数据帧的源和...
  • 数据链路层

    2012-07-31 13:10:04
    OSI 数据链路层的功能是:使网络层数据包做好传输准备以及控制对物理介质的访问。 数据链路层执行以下两种基本服务: 1) 允许... 数据链路层的术语: l 帧 — 数据链路层 PDU l 节点 — 第 2 层对连接到公共介...
  • 数据链路层 1.数据发送模型 数据链路层使用的信道主要有两种类型 : 点对点信道 : 使用一对一点对点的通信方式 广播信道 : 使用一对多的广播通信方式,因此过程比较复杂 链路 : 点到点的物理路段 数据链路 : 除了...
  • 数据链路层之以太网协议

    万次阅读 2018-06-06 17:22:48
    在 IP 数据包交付的过程中,在数据链路层会对数据包进行添加报头信息。以太网协议就是数据链路层的一个重要协议。这样说其实不太准确,因为以太网协议是一个规定数据链路层及物理层的协议,不能说它是数据链路层的一...
  • 工作在数据链路层的主要设备:交换机、网卡。 交换机原理: 1.收到数据帧后,学习帧中的源MAC地址,记录MAC表。 2.交换机查询MAC地址表,若无相应母大MAC对应端口,进行广播。 3.交换机MAC地址默认老化时
  • 数据成帧:①当网络层封装的数据包到达数据链路层时,数据链路层协议需要给数据包添加上头部和尾部,这个封装之后的结构称为数据帧(Frame);②数据帧就是物理层执行编码转换的数据。 错误校验:由于信号物理层...
  • CN.StudyLog.Ch4.Datalink Layer.数据链路层数据链路层的基本功能封装成帧透明传输 数据链路层的主要内容:数据通过不同的数据链路时,使用的协议和通讯机制。 数据链路:接集线器的网,路由器与路由器相连的链路...
  • 数据链路层协议

    千次阅读 2016-08-24 19:42:25
    数据链路层协议主要来介绍一下ARP协议和RARP协议 1. ARP协议,工作网络层的设备如路由器可以通过识别数据包发送和接受的IP地址来判断数据发送的原主机和目标主机,数据包在实际的传输过程中网络链路层的传输是...
  • 本章中,我们将讨论数据链路层并讨论如何准备数据包以便通过不同类型的媒体和物理网络组件进行传输,我们还将介绍一些以太网概念,最后查看物理层。 数据链路层 数据链路层是OSI模型中的第二层。其功能是...
  • 2、应用层、运输层、网络层、数据链路层、物理层的区别与功能 3、转发器、集线器、网桥、交换机、路由器、网关的功能与区别 一、二:不同传输单位与各层级的区别和作用: 1、应用层:   概念:通过应用进程间的...
  • 最近进行一些路由软件的编程,发现自己对数据报格式并不是十分清楚,所以就查阅了相关资料,总结如下,供大家参考,也可以称为自己日后的工具。
  • 最近进行一些路由软件的编程,发现自己对数据报格式并不是十分清楚,所以就查阅了相关资料,总结如下,供大家参考,也可以称为自己日后的工具。 图中括号中的数字代表的是当前域所占的空间大小,单位是bit位。...
  • 网络层等在数据链路层用MAC地址作为通信目标,数据包到达网络层等往数据链路层发送的时候,首先回去ARP缓存表去查找ip对应的MAC地址,如果查到了,就将此ip对应的MAC地址封装到链路层数据包的包头。如果缓存中没有...
  • 以及数据链路层数据可能会遇到那些问题呢?搞清楚这些问题之前,先让我们看下面经典的数据链路层数据发送模型。 主机H1与主机H2进行网络通信,主机H1发送一个数据包给H2,这个数据包经历若干路由器最终到达主机H2。...
  • 请问在数据链路层传输数据包,不管是路由器还是交换机,是否收到一个报文都有一个解封装后再封装的过程,还是如果是交换机二层转发,只是将收到的报文直接转发,不封装,也不解封装,请问哪种才是对的,请知道的告诉...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,630
精华内容 652
关键字:

在数据链路层数据包