今天本来打算弄组播接收,结果发现了一个有意思的事情。似乎印象里,很久以前也思考过这个问题。那就是如果我发送少于18个字节,那么会自动填充成18个。由于帧头有42个字节,所以就是60个字节。在WIRESHARK上显示是60个字节。
就是我发送1个字节,那么wireshark显示有60个字节。
于是我看代码,一步一步的跟踪,最后跟踪到变量ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];
ETH_TXBUFNB的宏定义有两处,官网程序现在stm32f4x7_ETH.H里面定义了
/* 5 ethernet driver transmit buffers are used (in a chained linked list)*/
#ifndef ETH_TXBUFNB
#define ETH_TXBUFNB 5 /* 5 Tx buffers of size ETH_TX_BUF_SIZE */
#endif
后来又在stm32f4x7_the_conf.h定义了
/*This define allow to customize configuration of the Ethernet driver buffers */
#define CUSTOM_DRIVER_BUFFERS_CONFIG
#ifdef CUSTOM_DRIVER_BUFFERS_CONFIG
/* Redefinition of the Ethernet driver buffers size and count */
#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */
#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */
#define ETH_RXBUFNB 4 /* 4 Rx buffers of size ETH_RX_BUF_SIZE */
#define ETH_TXBUFNB 4 /* 4 Tx buffers of size ETH_TX_BUF_SIZE */
#endif
这里如果把
ETH_TXBUFNB 宏定义值为1,那么板卡采用接收后发送的话,就会出现接收一帧发送两帧的情况,原因不明。
好,这里似乎有点跑题了。
然后就在网上找LWIP和60这两个关键字没找到。
然后乱找CSDN一般文章“
wireshark抓包怎么54字节也行,不是最少60吗 http://bbs.csdn.net/topics/390590266
(转载的)
根据rfc894的说明,以太网封装IP数据包的最大长度是1500字节,也就是说以太网最大帧长应该是以太网首部加上1500,再加上7字节的前导同步码和1字节的帧开始定界符,具体就是:7字节前导同步码 + 1字节帧开始定界符 + 6字节的目的MAC + 6字节的源MAC + 2字节的帧类型 + 1500 + 4字节的FCS。
按照上述,最大帧应该是1526字节,但是实际上我们抓包得到的最大帧是1514字节,为什么不是1526字节呢?
原因是当数据帧到达网卡时,在物理层上网卡要先去掉前导同步码和帧开始定界符,然后对帧进行CRC检验,如果帧校验和出错,就丢弃此帧。如果校验和正确,就判断帧的目的硬件地址是否符合自己的接收条件(目的地址是自己的物理硬件地址、广播地址、可接收的多播硬件地址等),如果符合,就将帧交给“设备驱动程序”做进一步处理。这时我们抓包的软件才能抓到数据,因此,抓包软件抓到的是去掉前导同步码、帧开始分界符、FCS之外的数据,其最大值是6 + 6 + 2 + 1500 = 1514。
以太网规定,以太网帧数据域部分最小为46字节,也就是以太网帧最小是 6 + 6 + 2 + 46 + 4 = 64。除去4个字节的FCS,因此,抓包时就是60字节。当数据字段的长度小于46字节时,MAC子层就会在数据字段的后面填充以满足数据帧长不小于64 字节。由于填充数据是由MAC子层负责,也就是设备驱动程序。不同的抓包程序和设备驱动程序所处的优先层次可能不同,抓包程序的优先级可能比设备驱动程序更高,也就是说,我们的抓包程序可能在设备驱动程序还没有填充不到64字节帧的时候,已经捕获了数据。因此不同的抓包工具抓到的数据帧的大小可能不同。(比如,wireshark抓到的可能没有填充数据段,而sniffer抓到的就有填充数据段),(不过 根据我的观察wireshark不同的版本抓获的最小数据包的大小好像有60字节也有54字节的情况.....)
=============================
于是我就观察wireshark 发现 那个小飞机网络工具发送的帧可以长度可以小于60个字节,而我用STM32发送的最小60个字节。
于是查看wireshark,发现wireshark的echo可以识别发送的字符,就是我只发送一个字符,那么echo 就只是显示1个字符。仔细观察一下,有个关键字padding。
原来是填充的,就是自动补齐的。
然后找找网上的padding的内容。
在STM32的程序里面发现了这个函数
/**
* @brief Enables or disables the DMA Tx Desc padding for frame shorter than 64 bytes.
* @param DMATxDesc: pointer on a DMA Tx descriptor
* @param NewState: new state of the specified DMA Tx Desc padding for frame shorter than 64 bytes.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void ETH_DMATxDescShortFramePaddingCmd(ETH_DMADESCTypeDef *DMATxDesc, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
/* Enable the selected DMA Tx Desc padding for frame shorter than 64 bytes */
DMATxDesc->Status &= (~(uint32_t)ETH_DMATxDesc_DP);
}
else
{
/* Disable the selected DMA Tx Desc padding for frame shorter than 64 bytes*/
DMATxDesc->Status |= ETH_DMATxDesc_DP;
}
}
然后添加这个函数在程序的各个地方来试图禁用PADDING功能,无奈都没有成功。
最后在函数low_level_output发现了一个注释:
/* Note: padding and CRC for transmitted frame
are automatically inserted by DMA */
看样子padding真是由硬件决定的。怪不得我用在线断点调试没有弄出来。
那么如何禁用padding? 还是不太清楚。
网上关于padding的文章有好多,这篇值得关注一下:
搞懂CSMA/CD,你就明白为什么以太网最小帧是64字节。