精华内容
下载资源
问答
  • 数据封装和解封装

    2021-07-21 16:26:43
    数据封装和解封装封装解封装TCP/IP分层封装数据封装和解封装的过程说明 封装 在原始数据的基础上加入一些额外的信息 解封装 去掉额外的信息,留下原始数据 TCP/IP分层封装 物理层:比特率 数据链路层:数据帧 网络层...

    封装

    在原始数据的基础上加入一些额外的信息

    解封装

    去掉额外的信息,留下原始数据

    TCP/IP分层封装

    物理层:比特率
    数据链路层:数据帧
    网络层:数据包
    传输层:数据段
    应用层:数据
    在这里插入图片描述

    数据封装和解封装的过程

    在这里插入图片描述
    在这里插入图片描述

    说明

    图片来源于网络

    展开全文
  • 百度的数据(应用层) TCP/UDP(传输层) IP(网络层) E2(网络接口处) 封装由上到下 解封装由下到上

    数据包
    数据的封装和解封装
    • 百度的数据(应用层)
    • TCP/UDP(传输层)
    • IP(网络层)
    • E2(网络接口处)
    封装由上到下
    解封装由下到上
    展开全文
  • sk buff封装和解封装网络数据包的过程详解

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                    可以说sk_buff结构体是Linux网络协议栈的核心中的核心,几乎所有的操作都是围绕sk_buff这个结构体进行的,它的重要性和BSD的mbuf类似(看过《TCP/IP详解 卷2》的都知道),那么sk_buff是什么呢?
           sk_buff就是网络数据包本身以及针对它的操作元数据。
           想要理解sk_buff,最简单的方式就是凭着自己对网络协议栈的理解封装一个直到以太层的数据帧并且成功发送出去,个人认为这比看代码/看文档或者在网上搜资料强多了。当然,网上已经有了大量的这方面的文章,但是我认为很多都太复杂了,它们都细化到了sk_buff结构体的每一个指针字段,并且还都画出了图,但一般都逃不过《深入理解Linux网络技术内幕》这本书的圈子。试想,如果以后内核版本升级了,字段新增了或者名字变了,怎么办?这些文章包括那本经典的《ULN》还能有帮助吗?
           因此,本文绝不深入到sk_buff的细节,但是相信这种简单的方式可以让自己在多年以后早已忘了什么是Linux协议栈的情况下,瞬间理解Linux是如何通过sk_buff封装数据包的。我们从网络的分层模型开始。

    网络分层模型

    这是一切的本质。网络被设计成分层的,所以网络的操作就可以称作一个“栈”,这就是网络协议栈的名称的由来。在具体的操作上,数据包最终形成的过程就是一层一层封装的过程,在栈上形成一段连续的数据,我们可以称作是一层一层的push操作。同样的,数据包的解封装的过程,则可以认为是一层一层的pop操作。

    sk_buff的操作

    要想形成一个最终的数据包,即以太帧(不考虑其它的链路层)。要进行以下的操作:
    1.分配一个skb结构体
    2.分配数据包的数据区
    3.在skb数据区定位应用层起始位置
    4.拷贝数据到应用层(假设应用层协议没有在socket接口之上被封装)
    5.在skb数据区定位传输层起始位置
    6.设置传输层头部字段
    7.在skb数据区定位IP层起始位置
    8.设置IP层头部字段
    9.在skb数据区定位以太层起始位置
    10.设置以太头部字段
    可以看出基本的模式,即“定位/设置”两步骤操作,有点区别的是应用层操作,这是因为应用层的操作一般都是在socket接口之上完成的。但是既然本文讲述的是skb的通用操作,就不再区分这个了。

    skb的核心操作

    在上面一小节,我们展示了skb的封装逻辑,但是具体到接口层面,就涉及到了skb的核心操作。

    1.分配skb

    这个是由alloc_skb完成的,完成同一任务的接口形成一个接口族,但是alloc_skb是最基本的接口。

           该alloc_skb接口完成两件事,即分配skb结构体以及skb数据包缓冲区,设置初始值。size参数表示skb的数据包缓冲区的大小,这个大小包括所有层的总和。如果该函数成功返回,那么就相当于你已经有了一个大小为size的空数据包缓冲区以及操作该数据包缓冲区的skb元数据。如下图所示:




    2.初始定位(skb_reserve)

    skb的逐层封装的关键在于写指针的定位,即这一层从哪个位置开始写。从协议封装的压栈形象来看,这个定位应该是顺序有规律的。初始定位十分重要,后面的定位就是例行公事了。初始定位当然是定位到应用层的末端,从这里开始,逐层将协议头push到skb的数据包缓冲区。初始定位图示如下:




    3.拷贝应用层数据(skb_push/copy)

    当skb分配好了之后,需要将协议“栈”的位置定位在数据包的“最低处”,这是初始定位,这样才可以把每一层的数据或者协议头push到栈上,这个操作由skb_reserve来完成。应用层数据已经在socket之上封装好了,那么就把skb的数据包缓冲区写指针定位到应用数据的开始处,此时的写指针在应用层缓冲区的末尾,因此需要使用skb_push操作将写指针定位到应用层开始处,这等于说压入了应用层栈帧。
           skb_push接口是将一个协议栈帧压入协议栈的接口,它返回一个position,该position就是skb数据包的写指针,告诉调用者,这里开始按照你的封装逻辑封装数据包,写多少字节呢?由skb_push的参数n指示。应用层的压栈操作如下图所示:




    将应用层栈帧压入协议栈之后,就可以在写指针位置开始,往后连续写n字节的应用层数据了,一般而言,这些数据来自socket。

    4.设置传输层头部

    和应用层的操作类似,这次需要把传输层栈帧压入协议栈中,如下图所示:




    接下来就可以愉快地在skb_push返回的位置设置传输层头部了,UDP,TCP,就看你对传输层的理解了。设置传输层头部其实就是在skb_push返回的位置开始写数据,写入的长度由skb_push的参数指定,即n。

    5.设置IP层头部

    和应用层以及传输层操作类似,这次需要把IP层的栈帧压入协议栈中,如下图所示:




    接下来就可以愉快地在skb_push返回的位置设置IP层头部了,如何设置,就看你对IP层的理解了。由于只是演示skb如何封装,因此没有涉及IP层相当重要的IP路由过程。

    6.设置以太帧头部

    这个就不说了,和上述的类似...如下图所示:




    到此为止,我封装了一个完整的以太帧,可以直接通过dev_queue_xmit发送的那种。一路下来,你会发现,skb数据包缓冲区以“压栈(push)”的方式逐渐被填充,每一层,都是通过skb_push接口压入一个栈帧,返回写指针,然后按照该层的协议逻辑从写指针开始写入栈帧长度的数据。
           在skb_push返回的那一刻,一个栈帧被压入了协议栈,然后该栈帧还仍未被写入数据,也就是说还没有完成封装过程,具体的封装过程由调用者自己实现。
           skb_push导致了skb数据包缓冲区写指针位置的前推,连带的改变了好几个变量,首先数据包的长度增加了n个字节,其次缩小了headroom的空间,然后通过reset_XXX_header的调用,skb记住了某层协议头在数据包中的位置(这点特别重要!比如在TSO/UFO的情况下,网卡驱动需要协议头的位置信息,用以计算校验值,所以虽然skb不记住协议头的位置,一个数据包也能完成封装,但是对于协议栈的完整实现而言,却是不正确的做法,毕竟网卡计算校验码已经成了一种事实上的标准[即便它违背了严格的分层原则!])

    7.在应用数据后面追加PADDING

    目前为止,从最后的图示上可以看到,在skb数据包缓冲区中,还有两块区域没有使用,一个headroom,一个是tailroom,这些是干什么用的呢?作为一个练习的例子,由于存在某种对齐原则,在封装完成后,我需要在数据包的最后追加一些填充,或者说我需要在最前面加一个前导码,或者最常见的,我要在数据包的最后加一个纠错码,此时应该怎么办呢?

           这个时候就需要headroom或者tailroom了,以在数据包最后追加数据为例,请看下图:




    实际上,skb_put的操作就是,在数据包的末尾追加数据。至于说headroom如何使用,我就不多说了,其实还是skb_push,headroom有什么用呢?前导码,X over Y封装,不一而足。

    实际的例子

    下面我给出一个实际的例子,封装一个以太帧,然后发送出去:
        skb = alloc_skb(1500, GFP_ATOMIC);    skb->dev = dev;    // 例行填充skb元数据    /* 保留skb区域 */    skb_reserve (skb, 2 + sizeof(struct ethhdr) +            sizeof(struct iphdr) +            sizeof(struct udphdr) +            sizeof(app_data));    /* 构造数据区 */    p = skb_push(skb, sizeof(app_data));    memcpy(p, &app_data[0], sizeof(app_data));    p = skb_push(skb, sizeof(struct udphdr));    udphdr = (struct udphdr *)p;      // 填充udphdr字段,略    skb_reset_transport_header(skb);    /* 构造IP头 */    p = skb_push(skb, sizeof(struct iphdr));    iphdr = (struct iphdr*)p;    // 填充iphdr字段,略    skb_reset_network_header(skb);    /* 构造以太头 */    p = skb_push(skb, sizeof(struct ethhdr));    ethhdr = (struct ethhdr*)p;    // 填充ethhdr字段,略    skb_reset_mac_header(skb);    /* 发射 */    dev_queue_xmit(skb);

    解封装的过程和封装的过程相反,解封装的过程是协议栈栈帧逐层pop的过程,但是Linux协议栈并没有用栈的术语来定义接口名字,而是使用了push的反义词,即pull来定义的,skb_pull就是核心接口,和skb_push严格相对。我就不再一一画图了。

    按照接口编码而不是按照实现编码

    这好像是Effective C++里面的一条,同样也适合于skb的操作场景。典型的就是“如何让skb记住IP层协议头,传输层协议头,mac头的位置”,接口是:
    skb_reset_mac_headerskb_reset_network_headerskb_reset_transport_header
    调用时机为skb_push返回的当时。曾几何时,我按照下面的方式设置了协议头的位置:
        /* 构造IP头 */    p = skb_push(skb, sizeof(struct iphdr));    iphdr = (struct iphdr*)p;    // 填充iphdr字段,略    //skb_reset_network_header(skb);    skb->network_header = p;
    有错吗?咋一看是没错的,但是却报错了:
    protocol 0008 is buggy, dev eth2
    这是怎么回事?原因就在于skb纪录的协议头位置是错误的!难道以上的设置skb的network_header字段的方式有何不妥吗?当然不妥!这就是没有按照接口编码的恶果。
           原因在于,系统设置skb的network_header字段的方式有两种,通过一个宏来识别:NET_SKBUFF_DATA_USES_OFFSET。也就是说,可以通过相对于skb的head指针的偏移来定位协议头的位置,也可以通过绝对地址来定位,具体使用哪一种取决于系统有没有定义NET_SKBUFF_DATA_USES_OFFSET宏,以上的skb->network_header = p明显是通过绝对地址来定位的,一旦系统定义了NET_SKBUFF_DATA_USES_OFFSET宏,肯定就不对了。既然宏定义在编译期确定,那么通过定义接口就可以在编译期唯一确定一种实现,程序员不必在乎是否定义了NET_SKBUFF_DATA_USES_OFFSET宏,这就是通过接口编程的益处。如果基于skb的实现来编程,你不得不针对所有的情况编写好几套实现,而以上错误的实现只是其中一种,而且还用错了场景!这是多么痛的领悟!
           NET_SKBUFF_DATA_USES_OFFSET宏是一个细节问题,如果使用接口编程便不必关注这个细节,否则你就必须搞清楚系统为何这么设计,即便这并不是你所关注的!为何呢?
           由于指针的长度大小在32位系统和64位系统中是不一样的,所以按理说skb中的指针型的元数据大小也会不同,且64位系统的将会是32位系统的两倍,为了平滑掉这个差别,使元数据大小一致,就必须让64位系统的对应指针类型变为4个字节,而这是不可能的。因此在64位系统中,使用偏移来定位元数据,而偏移的类型为固定不变的unsigned int,即4个字节。为了支持上述说法,skb中加入了一个新的层次,即定义了一种新的数据类型sk_buff_data_t,该类型在编译期确定:
    #if BITS_PER_LONG > 32#define NET_SKBUFF_DATA_USES_OFFSET 1#endif#ifdef NET_SKBUFF_DATA_USES_OFFSETtypedef unsigned int sk_buff_data_t;#elsetypedef unsigned char *sk_buff_data_t;#endif
    节约空间之外,对于和大小相关的操作,接口实现也更加统一。这就是细节,而这些细节并不是玩网络协议栈的人所要关注的,不是吗?这完全是系统实现的层面,和业务逻辑是无关的。

    为何未竟全功

    本文讲述到此为止。事实上,sk_buff还有更多的,相当多的细节,但是不能再一一描述了,因为那样就违背了本文一开始的初衷,即用最简单的方式揭露本质,如果一一描述了,那么本文将成为一个文档而非一篇感悟,时隔多年以后,相信自己也不会看下去的。
           关于sk_buff还有超级多的内容,仅仅结构体里面丰富字段的含义就够折腾好久的了,加上它如何配合Linux各层协议的实现,内容就更加丰富了。不过最基本的,就是本文讲述的,你得知道数据是怎样塞到一个skb并封装成一个可以被网卡实际发送的数据包的。好了,基本就是这些。最后我来总结一下本文提到的几个接口:
    alloc_skb:分配一个skb;
    skb_reserver:写指针向后移动到一个位置p,确定为数据包尾部,自始,写指针开始从该位置前移封装数据包;
    skb_push:写指针前移n,更新数据包长度,从它返回的位置可以写n个字节数据-即封装n字节的协议;
    skb_put:写指针移动到数据包尾部,返回尾部指针,可以从此位置写n字节数据,同时更新尾指针和数据包长度;
    ...
               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • 互联数据包封装/解封装过程(OSI) 一言不合来张图: 注意:1.mac地址只在本地有效,通过路由器传输过程中,mac地址会发生改变2.路由器根据路由表识别目标IP地址网段信息,确认是否可以进行转发,或是进行数据包的丢弃...

    一.互联数据包封装/解封装过程(OSI)

    一言不合来张图:
    互联数据包封装和解封装过程

    注意:
    1.mac地址只在本地有效,通过路由器传输过程中,mac地址会发生改变
    2.路由器根据路由表识别目标IP地址网段信息,确认是否可以进行转发,或是进行数据包的丢弃

    二.TCP/IP协议簇(DOD)

    互联数据包封装和解封装过程

    互联数据包封装和解封装过程

    1.tcp和udp对比:

    tcp:传输控制协议(属于面向连接网络协议)同步传输——“在线”速度慢,安全
    如:WEB浏览器 电子邮件 文件传输程序
    udp:用户数据报协议(属于无连接传输协议)异步传输——“离线”速度快,安全性差
    如:DNS 视频流 ip语音(VOIP)

    2.端口号由来:

    2的16次方=65536
    65536-0(端口号)=65535个端口号

    3.随机端口号默认区间:

    cat /proc/sys/net/ipv4/ip_local_port_range  查看命令

    4.TCP报文控制位:

    SYN:请求建立连接 1(生效)
    ACK:确认控制字段(确认收到信息无论对错)1(生效)
    FIN:请求断开连接

    5.TCP报文三次握手/四次挥手:

    三次握手
    (1)syn(seq=x)—(2)ack(ack=x+1)/syn(seq=y)—(3)ack(ack=y+1)
    互联数据包封装和解封装过程
    四次挥手 (tcp处于半关闭状态,所以需要四次)
    (1)fin/ack——(2)ack1——(3)fin/ack——(4)ack1
    互联数据包封装和解封装过程

    6.数据拆分重组过程:

    序列号+确认号
    seq序列号:拆分数据加序列号以便重组
    ack确认号:确认和需发送的数据,以保证数据传输无误

    转载于:https://blog.51cto.com/13520779/2061657

    展开全文
  • 数据的封装和解封装的过程 转载于:https://blog.51cto.com/lorna8023/412673
  • 但我们在网络上传输数据时,本机会从高层到底层将数据包进行几次封装,结果过程则是相反的顺序。如下图所示: 封装 首先在应用层,浏览器会将请求数据封装为HTTP协议数据包,在原本数据包中加入HTTP头。 传输层:...
  • 网络协议之数据的封装和解封装

    千次阅读 2020-02-28 21:12:17
    3、在传输层封装了端口号、数据传送到网络层对数据包进一步封装,为该数据包添加了IP头、其中包含源ip和目的ip,这样在数据传输的过程中数据就可以找到对应的目的主机 4、经过以上的封装、数据就已经到达了设备的...
  • 数据来源于: https://blog.51cto.com/13817711/2456633
  • sk_buff封装和解封装网络数据包的过程详解

    万次阅读 多人点赞 2015-02-08 15:00:08
    可以说sk_buff结构体是Linux网络协议栈的核心中的核心,几乎所有的操作都是围绕sk_buff这个结构体进行的,它的重要性和... 想要理解sk_buff,最简单的方式就是凭着自己对网络协议栈的理解封装一个直到以太层的数据帧并
  • 详解OSI参考模型及其数据封装和解封装 一、计算机网络的发展 第一代:是以主机为中心的网络。 第二代:1964年baran提出“存储—转发”的概念 1966年david提出“分组”的概念。 是以通信子网为中心的网络 第三代:...
  • 在收到这个数据包以及将这个数据包转发出去的时候整个过程是怎样的,比如以太网帧,二层交换机收到此帧后,是直接看目的mac地址,然后转发,还是先将二层数据链路包解封装,记录他的源mac和目的mac,然后在封装,再...
  • def parseLen(pkt): ret = 0 offset = 0 while True: num = struct.unpack("B", pkt[offset:offset + 1])[0] ret = ret * 128 + num % 128 offset += 1 if num 128:
  • 封装封装过程相反,封装是从应用层到物理层一层层加报头,解封装是在物理层到应用层的过程一层层去报头,我们可以想象层收到快递的过程,或者读取文件的过程。 每一层的名字: 在每一层的报头名字是都是不...
  • 这就是数据链路层的封装。 之所以要加上头和尾是因为物理层只负责电信号,因此必须要有一个特殊的电信号来告诉物理层这是一个帧的开始和结尾。 一般头和尾的电信号是连续的10101010形式,当物理层接收到信号后,...
  • 封装的过程和封装的过程相反,解封装的过程是协议栈栈帧逐层pop的过程,但是Linux协议栈并没有用栈的术语来定义接口名字,而是使用了push的反义词,即pull来定义的,skb_pull就是核心接口,和skb_push严格相对。...
  • test
  • 编译生成ffmpeg的动态链接库之后,先试着跑一下读取文件,接封装解压缩的例程吧。 1、如果你在./configure的时候采用默认安装路径(即没有配置–prifix=)。则可到/usr/local/share/ffmpeg/examples获取demuxing_...
  • 封装和解封装 1)简介   信息从一台计算机产生,经过TCP/IP模型加工处理再传输到目的计算机。而这个加工和传输的过程被叫做数据的封装(发送)和数据解封装(接收)。同层之间协议相同,下层为上层提供服务支持。 ...
  • 应用层(上层数据) 在应用层,二进制数据将经历复杂的解码过程,以还原发送者所传输的原始信息 传输层(tcp-上层数据) 传输层,首先要根据TCP头部判断数据段送往哪个应用层协议或应用程序,然后将之前被分组...
  • 这18个字节包括: 发送者:源地址 6个 接收者:目标地址 6 数据类型: 6 分层网络模型的优点: 降低网络传输复杂性 2.TCP/IP协议簇协议栈(模型): 应用层:包括了会话层和表示层,对数据进行封装和解封装。...
  • 基于SSZipArchive封装的带压缩和解压缩的文件管理工具
  • IPv4数据报的封装与解封装

    千次阅读 2017-05-11 22:15:10
    IPv4数据报的封装和解封装

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,772
精华内容 4,308
关键字:

封装和解封装的目的