LWIP移植成功了

2020-01-01 17:34发布

成功移植lwip1.3.1 1.3.2 1.4.1,裸机能跑tcp客户端和服务器。
开心之余,又有些问题需要请教了,lwip里面如何处理断线重连的问题?

下面是我实验的现象:
我在裸机上使用lwip,tcp做了服务器和客户端,在连接后把网线拔掉,tcp_poll函数还是会被执行的,这时候把网线插上(还没出现abort错误),需要重新连接(端口没变,原连接已经没反应了),连接后上一次连接的pcb仍然存在,这时候会发现轮询时候有两个pcb连接在运行。等了好几分钟后(关闭了保活设置)第一个pcb连接出现连接错误,自动断开。。。
这两个pcb占用同一个端口不会产生冲突吗?
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
99条回答
xhyzjiji
1楼-- · 2020-01-04 20:51
 精彩回答 2  元偷偷看……
CK345
2楼-- · 2020-01-05 02:48
嘿嘿。。。。。。
xhyzjiji
3楼-- · 2020-01-05 06:22
好吧,继续上个星期的。
lwip提供了两种内存分配管理机制,一种是c语言自带的内存机制,另外两种就是使用lwip自带的内存池以及内存堆。摘自网络
下面是opt.h源文件相关设置内容:
/**
* MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library
* instead of the lwip internal allocator. Can save code size if you
* already use it.
*/
#ifndef MEM_LIBC_MALLOC  
#define MEM_LIBC_MALLOC                 0
#endif

/**
* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
* speed and usage from interrupts!
*/
#ifndef MEMP_MEM_MALLOC
#define MEMP_MEM_MALLOC                 0
#endif
//以上定义置1:使用c库自带的内存管理,否则使用lwip重写的内存管理代码

/**
* MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set
* of memory pools of various sizes. When mem_malloc is called, an element of
* the smallest pool that can provide the length needed is returned.
* To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled.
*/
#ifndef MEM_USE_POOLS
#define MEM_USE_POOLS                   0
#endif

/**
* MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h
* that defines additional pools beyond the "standard" ones required
* by lwIP. If you set this to 1, you must have lwippools.h in your
* inlude path somewhere.
*/
#ifndef MEMP_USE_CUSTOM_POOLS
#define MEMP_USE_CUSTOM_POOLS           0
#endif

/**
* MEM_ALIGNMENT: should be set to the alignment of the CPU
*    4 byte alignment -> #define MEM_ALIGNMENT 4
*    2 byte alignment -> #define MEM_ALIGNMENT 2
*/
#ifndef MEM_ALIGNMENT //设置cpu内存对齐格式
#define MEM_ALIGNMENT                   1
#endif

struct mem { //动态内存结构体
  /** index (-> ram[next]) of the next struct */
  mem_size_t next;
  /** index (-> ram[prev]) of the previous struct */
  mem_size_t prev;
  /** 1: this area is used; 0: this area is unused */
  u8_t used;
};//由上述mem结构体可知,其为双向链表,且用used表示该内存区域是否被使用,便于内存回收
struct memp { //内存池结构体
  struct memp *next;
#if MEMP_OVERFLOW_CHECK  //支持内存溢出检测
  const char *file;
  int line;
#endif /* MEMP_OVERFLOW_CHECK */
};
动态内存堆分配策略原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适(First  Fit)方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。分配的内存块有个最小大小的限制,要求请求的分配大小不能小于MIN_SIZE,否则请求会被分配到MIN_SIZE大小的内存空间。一般MIN_SIZE为12字节,在这12个字节中前几个字节会存放内存分配器管理用的私有数据,该数据区不能被用户程序修改,否则导致致命问题。内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。采用这种分配策略,其优点就是内存浪费小,比较简单,适合用于小内存的管理,其缺点就是如果频繁的动态分配和释放,可能会造成严重的内存碎片,如果在碎片情况严重的话,可能会导致内存分配不成功。对于动态内存的使用,比较推荐的方法就是分配->释放->分配->释放,这种使用方法能够减少内存碎片。摘自老衲五木,同时也推荐大家去看看他对源码的解释,非常全面

最后更新一下资料库。


xhyzjiji
4楼-- · 2020-01-05 10:32
如果大家已经看过上传资料中的老衲五木的源码解析,可以了解到一些主要结构体的结构,这里先不在重复说明了(用到再提出)。
如何让lwip动起来?
之前提到,可以使用lwip自带的内存管理机制,那么在使用前,就必须先对相关内存进行初始化,
void LwIP_Init(void)
{
        struct ip_addr ipaddr;
        struct ip_addr netmask;
        struct ip_addr gw;
        //uint8_t macaddress[6]={0,0,0,0,0,1};
        //uint8_t macaddress[6]={0x3c,0x97,0x0e,0x34,0xeb,0x98};                //设置本地mac地址
       
        /* Initializes the dynamic memory heap defined by MEM_SIZE.*/
        mem_init();  //动态内存/内存堆的初始化函数,主要是告知动态内存/内存堆的起止地址,以及初始化空闲列表。摘自老衲五木的源码分析
       
        /* Initializes the memory pools defined by MEMP_NUM_x.*/
        memp_init();  //初始化内存池的函数,
               
        #if LWIP_DHCP  //如果开启DHCP,可以使用自动获取IP功能
                ipaddr.addr = 0;
                netmask.addr = 0;
                gw.addr = 0;
       
        #else  //设置本地IP地址,掩码,网关,在不适用自动获取IP的时候,需要手动赋予网络接口ip,掩码,网关。
        #if another
                IP4_ADDR(&ipaddr, 192, 168, 1, 111);  //IP4_ADDR函数:将ip地址组合成一个32位的数据,并记录在所指向的内存单元中
        #else
                IP4_ADDR(&ipaddr, 192, 168, 1, 112);
        #endif
                IP4_ADDR(&netmask, 255, 255, 255, 0);
                IP4_ADDR(&gw, 192, 168, 1, 1);
        #endif
       
        //Set_MAC_Address(macaddress);         //保存至全局变量
       
        /* - netif_add(struct netif *netif, struct ip_addr *ipaddr,
                struct ip_addr *netmask, struct ip_addr *gw,
                void *state, err_t (* init)(struct netif *netif),
                err_t (* input)(struct pbuf *p, struct netif *netif))
       
        Adds your network interface to the netif_list. Allocate a struct
        netif and pass a pointer to this structure as the first argument.
        Give pointers to cleared ip_addr structures when using DHCP,
        or fill them with sane numbers otherwise. The state pointer may be NULL.
       
        The init function pointer must point to a initialization function for
        your ethernet netif interface. The following code illustrates it's use.*/
        netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);  //对网络接口添加ip,掩码,网关信息,指定网络接口初始化和输入函数,这里的enc28j60是一个struct netif型的全局变量
       
        /*  Registers the default network interface.*/
        netif_set_default(&enc28j60);        //把enc28j60设置为默认网卡
               
        #if LWIP_DHCP
        /*  Creates a new DHCP client for this interface on the first call.
        Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
        the predefined regular intervals after starting the client.
        You can peek in the netif->dhcp struct for the actual DHCP status.*/
                dhcp_start(&enc28j60);  //如果开启ip自动获取,则会产生一个udp的DHCP客户端,广播询问路由器,路由器给出回应并返回一个ip,客户端则继续广播,询问此ip是否被占用,若否则获取成功,并修改网络接口中的dhcp状态,具体请看后面的应用
        #endif
       
        /*  When the netif is fully configured this function must be called.*/
        netif_set_up(&enc28j60);  //使能enc28j60接口

        //printf("LwIP Init Succedd ");
}
xhyzjiji
5楼-- · 2020-01-05 13:51
创建一个tcp服务器
1.初始化你的服务器
void tcp_server_init(void)
{
        struct tcp_pcb *pcb;

        //printf("tcp server init ");
       
        pcb = tcp_new();  //分配一个struct tcp_pcb大小的内存,如果分配不成功,则尝试清除tcp记录列表中处于等待状态的tcp连接,再重新分配内存。分配成功后,对此tcp记录一些信息初始化(如设置优先级,默认为普通优先级)

        //print_server_pcb_state(pcb);  //显示返回tcp的状态,用于调试用的
          /*
               void print_server_pcb_state(struct tcp_pcb *pcb){
        printf("server state:%s ", tcp_debug_state_str(pcb->state));
               }
               */
        tcp_bind(pcb, IP_ADDR_ANY, 1200);  //设置服务器端口号为1200,注意这里最好不要设置一些常被占用的端口号,如8080之类的
        //tcp_bind(pcb, &enc28j60.ip_addr, 2200);
        pcb = tcp_listen(pcb);  //将此tcp连接移至侦听tcp队列中去
        print_server_pcb_state(pcb);  //再次打印tcp连接状态
                                           
               tcp_accept(pcb, tcp_server_accept);         //初始化pcb->accept,当服务器接收到客户端链接申请,则自动调用服务器连接tcp中的pcb->accept指向的函数
}
litop
6楼-- · 2020-01-05 19:23
 精彩回答 2  元偷偷看……

一周热门 更多>