野火LwIP应用开发实战指南:基于STM32
lwIP 2.1.0 Lightweight IP stack
lwip的版本号在哪个文件里能看到?
关于LWIP几篇不错的文章分享
LwIP应用开发实战指南
LwIP多TCP连接问题
lwIP TCP/IP 协议栈笔记之十: LwIP 数据流框架
wireshark过滤表达式&wireshark捕获ftp协议分析
Atmel AT04055: Using the lwIP Network Stack
Small TCP/IP stacks for micro controllers - Universiteit Twente
Huawei Lite OS Lw IP Developer Guide - UserManual.wiki
Developing applications on STM32Cube with LwIP TCP/IP stack
LWIP会卡在netconn_write
关于LWIP在应用中遇到的一个问题memp_malloc: out of memory in pool TCP_PCB
TCP连接的部分细节及边界情况分析
一个完整的TCP连接
服务端主动终止连接的情况分析
TCP包的seq和ack号计算方法
/**
* @brief modbus-tcp 初始化
* @note
* @param
* @retval None
*/
void MODBUS_TCP_init( void )
{
eMBDisable();
eMBTCPInit( lan_para.modbus_tcp_port );
/**启动FreeModbus*/
eMBEnable();
if ( g_modbus_tcp_task_handle )
{
osThreadTerminate( g_modbus_tcp_task_handle );
}
/* Create the Modbus Process handler thread */
osThreadDef( ModbusTcpServerConn, MODBUS_TCP_server_conn, osPriorityNormal, 0, 128 );
g_modbus_tcp_task_handle = osThreadCreate( osThread( ModbusTcpServerConn ), NULL );
}
/**
* @brief MODBUS_TCP_server_conn
* MODBUS_TCP 服务器监听任务
* @param arg 任务参数
* @return void
*/
static void MODBUS_TCP_server_conn( void const *arg )
{
struct netconn *conn, *newconn;
err_t err;
/* 创建一个TCP连接 */
conn = netconn_new( NETCONN_TCP );
if ( conn != NULL )
{
/* 绑定modbus端口号 */
err = netconn_bind( conn, IP_ADDR_ANY, lan_para.modbus_tcp_port );
if ( err == ERR_OK )
{
/* 进入监听模式 */
netconn_listen_with_backlog(conn,0);
// netconn_listen( conn );
while ( 1 )
{
/* 阻塞,直到有modbus tcp 连接请求 */
err = netconn_accept( conn, &newconn );
if ( err == ERR_OK )
{
if(MODBUS_TCP_conn_count<2)
{
/* 连接成功,创建新的线程处理modbus tcp连接请求 */
osThreadDef( ModbusProcessTask, MODBUS_process_task, osPriorityHigh, 0, 500 );
osThreadCreate( osThread( ModbusProcessTask ), newconn );
}
else
{
netconn_close( newconn );
netconn_delete( newconn );
}
}
}
}
}
}
/**
* @brief MODBUS TCP处理程序。
* @note MODBUS TCP处理程序
* @param None
* @retval None
*/
void MODBUS_process_task( void const *arg )
{
struct netconn *conn;
struct netbuf *buf;
err_t accept_err;
err_t err;
void *data;
u16_t len;
LWIP_UNUSED_ARG( arg );
MODBUS_TCP_conn_count++;
/* 当前控制端口连接 */
conn = ( struct netconn * )arg;
/* 开启连接保活检测 */
ip_set_option( conn->pcb.tcp, SOF_KEEPALIVE );
/* 阻塞,直到接收数据 */
while ( netconn_recv( conn, &buf ) == ERR_OK )
{
do
{
/* 提取数据指针 */
netbuf_data( buf, ( void ** )&data, &len );
if ( lan_para.modbus_tcp_enable )
{
if ( len > MB_TCP_BUF_SIZE )
{
ucTCPRequestLen = MB_TCP_BUF_SIZE;
memcpy( ucTCPRequestFrame, data, ucTCPRequestLen );
}
else
{
ucTCPRequestLen = len;
memcpy( ucTCPRequestFrame, data, ucTCPRequestLen );
}
// MY_DEBUG_LOG("MODBUS_process_task free stack size : %u\n",(int32_t)uxTaskGetStackHighWaterMark(NULL));
// osDelay(10);
/* 向 modbus poll发送消息 */
xMBPortEventPost( EV_FRAME_RECEIVED );
eMBPoll();
err = netconn_write( conn, pucTCPResponseFrame, ucTCPResponseLen, NETCONN_COPY);
if( ERR_OK != err )
{
MY_DEBUG_LOG( "netconn_write err : %u\n", err );
}
}
}
while( netbuf_next( buf ) >= 0 );
netbuf_delete( buf );
}
MODBUS_TCP_conn_count--;
netbuf_delete( buf );
/* Close connection and discard connection identifier. */
netconn_close( conn );
netconn_delete( conn );
vTaskDelete( NULL ); /* 断开连接时,删除自己 */
}
https://blog.csdn.net/weixin_39270987/article/details/112393250
https://blog.csdn.net/qq_33559992/article/details/112616178?l
STM32每个系列都会有唯一的一个芯片序列号(96位bit):
STM32F10X 的地址是 0x1FFFF7E8
STM32F20X 的地址是 0x1FFF7A10
STM32F30X 的地址是 0x1FFFF7AC
STM32F40X 的地址是 0x1FFF7A10
STM32L1XX 的地址是 0x1FF80050
芯片STM32F207
#define STM32_SERIAL0 (*(__IO uint32_t ) 0x1fff7a10)
#define STM32_SERIAL1 ((__IO uint32_t ) 0x1fff7a14)
#define STM32_SERIAL2 ((__IO uint32_t *) 0x1fff7a18)
读取96位的芯片ID,
MAC地址的第1字节的第8Bit(00-50-BA-…对应的00000000-01010000-10111010-…,加粗字体的Bit)标识这个地址是组播地址还是单播地址,0单播,1组播
uint32 McuSerNo[2],tmp[2];
uint8 MacAdress[6];
McuSerNo[0] = STM32_SERIAL0;
McuSerNo[1] = STM32_SERIAL1;
tmp[0] = McuSerNo[0] << 2 ;
tmp[1] = ((McuSerNo[0] >> 30) & 0x03 ) + (McuSerNo[1] << 2) & 0xfffffffc;
memcpy(MacAdress,tmp,6);
在TCP/IP的架构中,MAC地址扮演着非常重要的角色。在通信中,由MAC地址标识的主机网卡,作为主机身份的硬件地址。每块网卡被生产出来后,都会有一个全球唯一的编号来标识自己,不会重复,这个编号就是MAC地址,也就是网卡的物理地址。MAC地址是由48位的二进制数组成,即6个字节。在通信中是用16进制表示的。前24位是由生产厂家向IEEE标准组织申请的厂家代码,是固定的,但是它的第八位一定是0(48位中的第8位二进制数),因为网卡的物理地址,一定是单播地址,在IPv4的环境中,区分单播和组播地址就是校检第八位的二进制数字,0代表单播地址,1代表组播地址。那么表现在16进制中第一字节第二个数字一定是个偶数(十进制概念,不知道恰不恰当)。也就是说第二个数字一定是0、2、4、6、8、A、C、E其中的一个,那么区分单播或者组播地址就简单多了。
例:6C-62-6D-26-1E-29 它的二进制:
01101100-01100010-01101101-00100110-00011110-00101001 单播:第八位为0
如果你将MAC改为:61-62-6D-26-1E-29,这样的地址是组播地址,修改不会成功。
注:我们在写程序随机生成MAC地址时,切记MAC地址16进制中的第一个字节第二个数一定是偶数。
TFTP服务器的文件上传和下载速度有1MB/S左右,比FTP服务器要慢一半
理论上来说UDP传输速率会大于TCP,主要是因为TCP传输存在慢启动和用塞避免。当网络中存在冲突的时候,会大幅的降低传输速度,这个时候UDP就是抢占上来,而TCP会越来越慢。
虽然tftp是基于UDP的,ftp是基于tcp的,但是tftp的传输速度远不及tfp。tftp采用的是简单的停止等待协议,发出去的UDP包必须要等待对方的回答或者超时才能开始下一个UDP发送或者重传。而FTP只要对方有ACK表示有win空间就可以持续的发,所以FTP要比TFTP快很多。
LwIP上的SO_RCVTIMEO选项
Netconn receive timeout
LWIP + RTOS - 如何避免netconn永远阻塞线程?
开启连接保活(用于断网,客户端死机等问题)
TCP_TMR_INTERVAL的值会影响保活超时的时间,TCP_TMR_INTERVAL的值越大,需要的时间越长。
如果超过规定时间没有返回ACK,则netconn_recv会返回错误代码ERR_ABRT,此时可断开连接,清除占用的内存,退出任务。
#define LWIP_TCP_KEEPALIVE 1
#define TCP_KEEPIDLE_DEFAULT 5000UL//7200000UL /* Default KEEPALIVE timer in milliseconds */
#define TCP_KEEPINTVL_DEFAULT 1000UL//75000UL /* Default Time between KEEPALIVE probes in milliseconds */
#define TCP_KEEPCNT_DEFAULT 5U//9U /* Default Counter for KEEPALIVE probes */
/* 创建一个TCP连接 */
conn = netconn_new( NETCONN_TCP );
if ( conn != NULL )
{
/* 开启连接保活检测 */
ip_set_option( conn->pcb.tcp, SOF_KEEPALIVE );
/* 绑定modbus端口号 */
err = netconn_bind( conn, IP_ADDR_ANY, lan_para.modbus_tcp_port );
if ( err == ERR_OK )
{
/* 进入监听模式 */
// netconn_listen_with_backlog(conn,0);
netconn_listen( conn );
while ( 1 )
{
/* 阻塞,直到有modbus tcp 连接请求 */
err = netconn_accept( conn, &newconn );
if ( accept_err == ERR_OK )
{
while ( netconn_recv( newconn, &buf ) == ERR_OK )
{
}
netbuf_delete( buf );
/* Close connection and discard connection identifier. */
netconn_close( newconn );
netconn_delete( newconn );
}
}
}
}
设置netconn_recv接收超时(用于清除长时间无数据发送的连接)
如果超过规定时间没有接收到数据,则netconn_recv会返回错误代码ERR_TIMEOUT,此时可断开连接,清除占用的内存,退出任务。
/* 开启接收超时功能 */
#define LWIP_SO_RCVTIMEO 1
/* 设置接收超时 */
netconn_set_recvtimeout(conn,60000);
opt.h
/**
* MEM_SIZE: the size of the heap memory. If the application will send
* a lot of data that needs to be copied, this should be set high.
*/
#if !defined MEM_SIZE || defined __DOXYGEN__
#define MEM_SIZE (20*1024)
#endif
mem.c
#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
/*----- Default Value for H7 devices: 0x30044000 -----*/
#define LWIP_RAM_HEAP_POINTER 0x30044000
/**
* Zero the heap and initialize start, end and lowest-free
*/
void
mem_init(void)
{
struct mem *mem;
LWIP_ASSERT("Sanity check alignment",
(SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT - 1)) == 0);
/* align the heap */
ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
/* initialize the start of the heap */
mem = (struct mem *)(void *)ram;
mem->next = MEM_SIZE_ALIGNED;
mem->prev = 0;
mem->used = 0;
/* initialize the end of the heap */
ram_end = ptr_to_mem(MEM_SIZE_ALIGNED);
ram_end->used = 1;
ram_end->next = MEM_SIZE_ALIGNED;
ram_end->prev = MEM_SIZE_ALIGNED;
MEM_SANITY();
/* initialize the lowest-free pointer to the start of the heap */
lfree = (struct mem *)(void *)ram;
MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
if (sys_mutex_new(&mem_mutex) != ERR_OK) {
LWIP_ASSERT("failed to create mem_mutex", 0);
}
}
LWIP之Memp原理
LwIP内存分配
LwIP常见问题FAQ(持续更新中…)
lwip编译选项
TCP/IP协议学习(二) LWIP用户自定义配置文件解析
LwIP configuration
LWIP v2.1.0内存管理之相关宏之间的关系
lwip 内存池与内存堆的空间定义
lwIP TCP/IP 协议栈笔记之十六: NETCONN 接口编程
netconn_listen_with_backlog(conn,1);
listen函数中backlog的含义
FTP message format
lwip-FTP
FTP启动时,会创建一个控制连接A和一个数据连接,打开根目录后,数据连接关闭,控制连接A保持。
启动文件下载都会创建一个新的控制连接B和数据发送连接,文件传输完成后,数据连接关闭,控制连接B保持一段时间,如果没有文件传输操作,则关闭控制连接B,如果在可用时间段内,则继续使用控制连接B。
控制连接A用于目录的切换
《嵌入操作系统 – 玩转ART-Pi开发板》第9章 基于Select/Poll实现并发服务器(二)
用wireshark抓包分析DHCP协议!数据报文单播还是多播?
实现lwip的DHCP自动获取ip地址
lwip源码分析 之 DHCP协议实现(一)