百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

手把手教你手撸通讯协议(二)-网络的基础

cac55 2025-03-12 12:31 19 浏览 0 评论

经过上一篇的手把手教你手撸通讯协议(一) S7协议解析 中,大家有没有发现缺了很大一部分篇幅,而且也只讲到了UDP的包头;由于UDP是让大家简单的看到以太网的工作方式,接下去我们通过开源的LwIP协议栈来好好了解以太网的真实工作方式,我将会在这一期结束的时候,给大家实现一个基于STM32的modbusTCP 主站的小demo

第一节:协议层简介

首先我们还是根据标准的TCP/IP协议栈来分析传输层和链路层的网络是怎样打包的;

首先我们先了解几个基础协议及网络分层:

网络接口层:定义数据帧(对电信号0/1进行的特定分组)、确认主机的物理地址(MAC地址),通过传输介质在网络上传输数据帧。网络接口有不同的实现方式,比如可以通过有线或无线的方式收发数据帧,不同的实现方式意味着不同的帧结构、传输速率等。

网络层:定义网络地址(IP地址)、区分网段、对于子网内的数据包进行MAC寻址、对于不同子网的数据包进行路由,实现网络中主机到主机的通信。主要有PPP协议、SLIP协议、ARP、ipv4等基础协议。

传输层:定义端口(Port)、标识应用程序身份、实现端口到端口的通信,TCP协议可以保证数据传输的可靠性。

应用层:定义数据格式并按照对应的格式解读数据(下层传送过来的是字节流,不能很好的被程序识别)。应用层定义了各种各样的协议来规范数据格式,常见的有 HTTP、FTP、SMTP 等。


第二节:数据包及内存存储结构

由上面的基础知识、我们根据理论知识,我们根据LwIP来进行学习。其实其实网络层级来说:TCP和UDP类似,但TCP需要实现可靠连接,网卡接收的数据包,有可能是成千上万字节,也有可能是几个字节,所以我们需要对其数据进行打包处理。

由于内存分配问题可以谈的很深、涉及到编译原理、字节对齐这些,篇幅有限,不展开。反正主要是两种方式、一种是链表、一种是内存池方式,各种系统中也都会讲到,我们主要从数据包开始说明:

struct pbuf {
  struct pbuf *next;
  void *payload;
  u16_t tot_len;
  u16_t len;
  u8_t  type;
  u8_t flags;
  u16_t ref;
};
typedef enum {
  PBUF_RAM, /* pbuf data is stored in RAM */
  PBUF_ROM, /* pbuf data is stored in ROM */
  PBUF_REF, /* pbuf comes from the pbuf pool */
  PBUF_POOL /* pbuf payload refers to RAM */
} pbuf_type;

这两个看上去是不是很熟悉,就是一个链表节点。分配完成后就是这样:

组成链表后的形式大概是这样的:

这个就是数据包在内存中存储的方式了。


第三节:网络接口

在 LWIP 中,是通过一个叫做 netif 的网络结构体来描述一个硬件网络接口的。

struct netif { 
struct netif *next; // 指向下一个 netif 结构的指针 
struct ip_addr ip_addr; // IP 地址相关配置 
struct ip_addr netmask; 
struct ip_addr gw; 
err_t (* input)(struct pbuf *p, struct netif *inp); //调用这个函数可以从网卡上取得一个 
// 数据包 
err_t (* output)(struct netif *netif, struct pbuf *p, // IP 层调用这个函数可以向网卡发送 
struct ip_addr *ipaddr); // 一个数据包 
err_t (* linkoutput)(struct netif *netif, struct pbuf *p); // ARP 模块调用这个函数向网 
// 卡发送一个数据包 
void *state; // 用户可以独立发挥该指针,用于指向用户关心的网卡信息 
u8_t hwaddr_len; // 硬件地址长度,对于以太网就是 MAC 地址长度,为 6 各字节 
u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; //MAC 地址 
u16_t mtu; // 一次可以传送的最大字节数,对于以太网一般设为 1500 
u8_t flags; // 网卡状态信息标志位 
char name[2]; // 网络接口使用的设备驱动类型的种类 
u8_t num; // 用来标示使用同种驱动类型的不同网络接口 
}; 

举个例子来实现一张网卡的初始化:

static struct netif enc28j60;//声明了一个 netif 结构的变量 enc28j60 
struct ip_addr ipaddr, netmask, gw; //声明了三个分别用于暂存 IP 地址、子网掩码和网关地址的变量
IP4_ADDR(&gw, 192,168,0,1); 
IP4_ADDR(&ipaddr, 192,168,0,60); 
IP4_ADDR(&netmask, 255,255,255,0); 
netif_init(); 
netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ethernetif_init, tcpip_input); 
netif_set_default(&enc28j60); 
netif_set_up(&enc28j60); 

err_t ethernetif_init(struct netif *netif) 
{ 
netif->name[0] = IFNAME0; //初始化变量 enc28j60 的 name 字段 
netif->name[1] = IFNAME1; // IFNAME 在文件外定义的,这里不必关心它的具体值  
netif->output = etharp_output; //IP 层发送数据包函数 
netif->linkoutput = low_level_output; // //ARP 模块发送数据包函数 
low_level_init(netif); //底层硬件初始化函数 
return ERR_OK; 
} 
static void low_level_init(struct netif *netif) 
{ 
netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置变量 enc28j60 的 hwaddr_len 字段 
netif->hwaddr[0] = 'F'; //初始化变量 enc28j60 的 MAC 地址 
netif->hwaddr[1] = 'O'; //设什么地址用户自由发挥吧,但是不要与其他 
netif->hwaddr[2] = 'R'; //网络设备的 MAC 地址重复。 
netif->hwaddr[3] = 'E'; 
netif->hwaddr[4] = 'S'; 
netif->hwaddr[5] = 'T'; 
netif->mtu = 1500; //最大允许传输单元 
//允许该网卡广播和 ARP 功能,并且该网卡允许有硬件链路连接 
netif->flags = NETIF_FLAG_BROADCAST | \ 
NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; 
enc28j60_init(netif->hwaddr); //与底层驱动硬件驱动程序密切相关的硬件初始化函数 
} 

这里我们完成了初始化一张名为enc28j60 的网卡。

接下去是网卡的接收和发送主要通过low_level_input 和 low_level_output这两个函数来实现。然后在操作系统中直接调用这两个函数就行了。

以UC/OSII的网卡数据接收为例:

第一步创建线程:

OSTaskCreate(ethernetif_input,(void *)&enc28j60, 
&T_ETHERNETIF_INPUT_STK[T_ETHERNETIF_INPUT_STKSIZE-1] 
ETH_IF_TASK_PRIO);  

第二步:数据包接收

void ethernetif_input(void *arg) //创建该进程时,要将某个网络接口结构的 netif 结构指 
{ //针作为参数传入 
struct eth_hdr *ethhdr; 
struct pbuf *p; 
struct netif *netif = (struct netif *)arg; 
while (1) 
{ 
p = low_level_input (netif); // 接收一个数据包 
if (p == NULL) // 如果数据包为空, 
continue; // 则循环结束,启动下次接收过程 
ethhdr = p->payload; // 取得数据包内数据 
switch (htons(ethhdr->type)) // 判断数据包类型 
{ // 只对 IP 数据包和 ARP 数据包进行处理 
case ETHTYPE_IP: // IP 数据包 
case ETHTYPE_ARP: // ARP 数据包 
if (netif->input(p, netif)!=ERR_OK) // 将数据包发送到上层应用函数 
{ 
pbuf_free(p); 
p = NULL; 
} 
break; 
default: 
pbuf_free(p); 
p = NULL; 
break; 
} //switch 
} //while 
} //main 函数 

至此,数据包的接收可算大功告成 。

第四节:网络层

接下去我们要进行网络层协议的讲解了:

(1) ARP:全称 Address Resolution Protocol,译作地址解析协议,是位于TCP/IP协议栈底层的协议。

ARP的协议包格式:

struct etharp_hdr { 
PACK_STRUCT_FIELD(struct eth_hdr ethhdr); // 14 字节的以太网数据报头 
PACK_STRUCT_FIELD(u16_t hwtype); // 2 字节的硬件类型 
PACK_STRUCT_FIELD(u16_t proto); // 2 字节的协议类型 
PACK_STRUCT_FIELD(u16_t _hwlen_protolen); // 两个 1 字节的长度字段 
PACK_STRUCT_FIELD(u16_t opcode); // 2 字节的操作字段 op 
PACK_STRUCT_FIELD(struct eth_addr shwaddr); // 6 字节源 MAC 地址 
PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); // 4 字节源 IP 地址 
PACK_STRUCT_FIELD(struct eth_addr dhwaddr); // 6 字节目的 MAC 地址 
PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); // 4 字节目的 IP 地址 
} PACK_STRUCT_STRUCT; 

我们可以把他转化成这个结构体。

接下去这个图我们可以看到ARP的工作流程:

说白了就是两个功能:通过ARP协议实现IP地址和MAC地址的映射,或者广播获取目标MAC地址。

嘿嘿嘿,这里有人知道为啥拨号叫PPPOE么?或者说校园网、闪讯这些校园网络是怎么进行独立收费的么。理解了这一层的协议,可以做很多事情噢。

(2)IP协议

IP层其实在上一章节也有讲到过。

最重要的是在网络标识那一标识位置确认了使用了什么协议。8位协议字段用来描述该IP数据包是来自于上层的哪个协议,如该值为1表示为ICMP协议,该值为2表示IGMP协议,该值为6表示TCP协议,该值为17表UDP协议。

前面我说们说到TCP包需要分包,接下去一个图可以很清晰的解释LwIP是怎么进行分包的:

这一层的东西太多了,不展开。IP层的讲解主要了解这些就够了。



再说个大家感兴趣东西:ping和tracert ,其实在连接过程中,就是用ICMP协议实现的,主要用来测试路径和时间。其实第一次接触hack也是从ICMP攻击开始的。中美黑客大战,我也是拿了脚本,贡献了一份力(ORZ)。

第五节:传输层

接下去我们说说传输层:这一层的东西很多很多,偷懒了。

这里先补充一点:在PLC还没分配IP地址时,我们是怎么找到设备并分配IP的?没有IP地址是怎么发现PLC或模块的地址的?

先以Rockwell的EtherNet/IP举个例子,由于Rockwell的CIP协议大多数功能都是基于标准以太网协议实现,所以可以很贴合现在这个系列。AB模块可以通过一个叫BOOTP的工具进行模块的发现和IP地址分配,很好理解,AB的PLC是使用BOOTP协议进行PLC或模块的发现的。为什么我们现在挺少听说BOOTP了呢?因为现在大家都在用DHCP的方式了Bootp其实是基于UDP协议进行设备发现的。其实我们上下位机的通讯基本靠TCP协议,而下位机之间的通讯基本是基于UDP进行通讯,UDP协议的本身协议特点可以实现模块之间的高速通讯,更适合用于现场网络,本系列主要以与上位机通讯为主,所以减少UDP这一块的解释。

西门子的协议对这一层的协议进行了一些修改,以下图为例(来自西门子官网)。这个下次再聊。


下级预告:本系列知识点的重点了:TCP的建立和断开

TCP的全称大家自行百度:主要功能是为上层提供一个可靠连接(虽然容易出线粘包问题)。

这里先给大家看一张图(别的地方截取过来的):这是TCP连接的状态转化。

对这一期就先到这边,TCP的内容留在下一期。

结尾

总结一下

Summary

1、LwIP协议栈主要用于嵌入式系统的以太网协开发。该协议栈为很轻量级的以太网协议栈,通过该协议栈的学习,可以很好的理解以太网是怎么工作的,采用该协议栈,我在很多项目中实现了MQTT、S7协议、ModbusTCP协议等工业协议的开发,还有一些私有协议的开发,很好的用于网络中间件的开发。

2、讲解了物理接口层、链路层、网络层、传输层的部分协议实现和打包方式。讲的比较简单,也是给大家一个可以参考的方向。


留两个问题


问题1:IP数据包失序后怎么处理?

问题2:TCP发生粘包问题如何处理(或者说S7协议、CIP协议等是怎么处理粘包问题)?


2022年2月

相关推荐

solidworks使用心得,纯干货!建议大家收藏

SolidWorks常见问题Q1:怎样修改,修复或删除已有SolidWorks软件的安装?A:在退出SolidWorks的状态下,于控制面板中双击添加或删除程序,选择Solidworks,单击更改或...

Camtasia Studio 8.0注册版汉化安装包

CamtasiaStudio简体中文版是由TechSmith开发的一款专业的屏幕录制完全解决套件!它提供了从屏幕录像到视频编辑、转换再到发布一系列全程完全解决方案!CamtasiaStudi...

#本站首晒# LG 29UM68 29寸21:9显示器开箱测评

作者:安隆隆一、前言08年初买的三星931BW,已经用了8年了。在它的陪伴下,我和奎爷、德雷克、隼龙、贝姐、里昂、苍老师、波老师一起度过了许多个日日夜夜,虽然现在还能正常使用,但确实该换了。二、选购...

DOTA2 6月19日更新 修复DOTA2重生Beta中的BUG

6月19日更新修复DOTA2重生Beta中的BUG,本期为你带来的是重生Beta中的一些BUG修复,昨天更新的Beta测试中有不少游戏BUG。北京时间6月19日dota2客户端再次更新,本次更新主要...

WPF界面开发小技巧分享——Splash Screen Manager

下载DevExpressv20.1完整版DevExpressv20.1汉化资源获取点击“了解更多”获取DevExpressv20.1完整版下载通过DevExpressWPFControls...

避免冲突!如何彻底卸载旧版本Office?

安装新版本Office软件之前,为了避免出现冲突,微软会建议用户卸载本机中所有的旧版本Office,但很多朋友发现有时使用常规的方法无法完全卸载它。下面我们就针对不同的场景,介绍几个彻底卸载旧版本Of...

真真假假设置“陷阱围墙”

要想保护好自己的电脑资料(宝藏),最好的办法当然就是彻底杜绝他人使用你的电脑了。不过在一些特殊的情况下,比如碍于情面不好意思直接拒绝朋友的借用要求,这时不妨采取一些更委婉的保护手段,可以试着制造一些看...

IT审计:CentOS 6/7中 安装Open-AudIT

IT审计大家可能都清楚,IT审计的主要目的是为了更好的控制IT的风险,有效的帮助企业规避风险。具体而言,IT审计是为了提高企业信息系统的安全性、可靠性以及开发、运营效率,使企业信息化得到健康、全面的发...

软网推荐:四位一体 请个多功能影像处理者

截图、录制屏幕视频、制作GIF动画、图片编辑,这些都是我们在日常影像处理中要接触到的任务。以往执行这些任务,我们往往需要分别借助于相应的软件来处理。其实,只需一款免费软件ShareX,就能完成上述诸多...

推荐一款显示器分屏软件,单显示器解决内容对照问题

如今的电脑显示器越来越大,在有办公或是娱乐的时候会需要将多个窗口的内容进行比照。在没有多个显示器的情况下衍生除了带鱼屏(21:9、34:9)解决了这个难题,但这样也意味这需要购买新的显示器,有没有办法...

Windows 各版本自带截图工具及好用外部截图工具全解析

一、Windows版本自带截图工具总览介绍Windows7、8、10、11以及WindowsServer2018、2019不同版本自带截图工具的情况。(一)Windows7自带截图...

想打开Win10隐藏的神秘功能吗,请打开操作系统的隐藏“模式”

出于某种原因,Windows10的一些最有用的功能被神秘地隐藏起来了,下面,我们将揭示如何找到操作系统的隐藏“模式”,在哪里可以找到它们、它们的作用以及如何充分利用它们。一、改进Windows的工作...

IT之家学院:为Win10/Win8.1/Win7截图操作配上个性音效

在Windows中我们常用的截图方法之一便是按键盘上的PrintScreen健或Alt+PrintScreen组合键,其中前者为截取整个屏幕,后者为截取当前活动窗口,不过这两个操作都没有任何提示。...

再见全家桶:微软Win10更新公布App删减名单

北京时间7月24日消息,目前Windows10Redstone3(秋季创意者更新)已经敲定在9月推送,传言其版本号为version1709。现在微软又带来一个好消息,就是Win10更新将会体大...

QQ截图不好用?这才是真正给力的截图方式

来源:太平洋电脑网大家都非常喜欢用QQ截图,然而QQ截图真的是最好的截图方法吗?未必!在很多场合当中,QQ截图的表现难以令人满意。今天,就来给大家介绍一些真正给力的截图方法!大家最常用的QQ截图,很多...

取消回复欢迎 发表评论: