|
在我的协议里,是基于分层处理的。原因是分层结构更加明朗,用户在写用户程序的时候,可以直接调用相关的API函数即可。这样用户进程就融入到协议栈里去了。
我们来看看TCP/IP基本的链路层。
以太网封装格式
----------------------------------------------------------------------------------------------
| 目的地址 | 源地址 | 帧类型 | 应用数据 | CRC |
----------------------------------------------------------------------------------------------
6 6 2 46~1500 4
链路层对应着物理层,这时我们可以设计一个基本的数据结构:以太网帧。
/* 以太网帧头部 */
typedef struct{
U8 destination[MAC_LENGTH]; //目的MAC地址
U8 source[MAC_LENGTH]; //源MAC地址
U16 pro_type; //链路层协议类型(占用2个字节,见Arp.h)
} ETHER_HEAD;
/* 以太网帧 */
typedef struct{
ETHER_HEAD e;
U8 edata[ETHER_MTU]; // 数据包
U32 crc; // CRC校验
} ETHER_FRAME;
这两个结构分别表示整个以太网帧和以太网帧的头部。
其中ETHER_FRAME这个数据结构直接对应着网卡驱动程序的一个缓冲节点。缓冲区是一个环形的双向链表,每个节点的长度就是一个以太网帧。这在以后讲驱动时在说。。
这时我们可以设计一个链路层的函数,用来封装一个以太网帧。
U16 make_ethernet(ETHER_FRAME __FAR *efp, U8 *srce, U8 *dest, U16 pcol, U16 dlen)
{
U16 len;
efp->e.pro_type = CHGW(pcol); //链路层协议类型,IP或者ARP
memcpy(efp->e.destination, dest, MAC_LENGTH); //目的地址
memcpy(efp->e.source, srce, MAC_LENGTH); //源地址
len = dlen + sizeof(ETHER_HEAD); //数据部分加上以太网的头部
return len;
}
该函数的作用是在以太网帧里面填入源,目的IP和MAC地址,最后填入数据部分,打包成一个以太网帧,放在缓冲区efp里面。 |
|