STM32无操作系统TCP_Server移植

2019-07-14 12:00发布

核心芯片使用STM32F407,以太网芯片使用LAN8720,开发环境基于LWIP无操作系统移植。代码参考stm32无操作系统TCP_Client移植
1、主要函数
与客户端模式基本相同,只列出用到的不同于客户端的几个函数
//绑定IP地址和端口号
err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
//设置pcb进入监听状态
tcp_listen(pcb)//函数原型struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
//注册tcp_accept回调函数
void tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
2、头文件和初始化
头文件与客户端 模式基本相同。参考正点原子代码发现服务器模式下他们创建了两个控制块如下:
struct tcp_pcb *tcppcbnew; //定义一个TCP服务器控制块
struct tcp_pcb *tcppcbconn; //定义一个TCP服务器控制块
这两个控制块均在初始化时出现,先上初始化代码 void tcp_server_config(void) { err_t err; tcppcbnew=tcp_new(); //创建一个新的pcb if(tcppcbnew) //创建成功 { err=tcp_bind(tcppcbnew,IP_ADDR_ANY,TCP_SERVER_PORT); //将本地IP与指定的端口号绑定在一起,IP_ADDR_ANY为绑定本地所有的IP地址 if(err==ERR_OK) //绑定完成 { tcppcbconn=tcp_listen(tcppcbnew); //设置tcppcb进入监听状态 tcp_accept(tcppcbconn,tcp_server_accept); //初始化LWIP的tcp_accept的回调函数 } } } 然而查看监听函数函数发现传参和返回值是相同的,并没有特殊的更改。因此将监听函数的传入值和接受值改为一个pcb,代码不再展示。
当监听到有客户端连接服务器时回调用tcp_accept()回调函数,代码如下 err_t tcp_server_accept(void *arg,struct tcp_pcb *newpcb,err_t err) { err_t ret_err; struct tcp_server_struct *es; tcp_setprio(newpcb,TCP_PRIO_MIN);//设置新创建的pcb优先级 es=(struct tcp_server_struct*)mem_malloc(sizeof(struct tcp_server_struct)); //分配内存 if(es!=NULL) //内存分配成功 { es->state=ES_TCPSERVER_ACCEPTED; //接收连接 es->pcb=newpcb; es->p=NULL; tcp_arg(newpcb,es); tcp_recv(newpcb,tcp_server_recv); //初始化tcp_recv()的回调函数 tcp_err(newpcb,tcp_server_error); //初始化tcp_err()回调函数 tcp_poll(newpcb,tcp_server_poll,1); //初始化tcp_poll回调函数 tcp_sent(newpcb,tcp_server_sent); //初始化发送回调函数 tcp_server_flag|=1<<5; //标记有客户端连上了 lwipdev.remoteip[0]=newpcb->remote_ip.addr&0xff; //IADDR4 lwipdev.remoteip[1]=(newpcb->remote_ip.addr>>8)&0xff; //IADDR3 lwipdev.remoteip[2]=(newpcb->remote_ip.addr>>16)&0xff; //IADDR2 lwipdev.remoteip[3]=(newpcb->remote_ip.addr>>24)&0xff; //IADDR1 ret_err=ERR_OK; }else ret_err=ERR_MEM; return ret_err; } 该回调函数注册了几个重要的回调函数,这些回调函数功能与客户端模式相同,可参考前面写的《
STM32无操作系统UDP》,同时标记了连接和客户端ip地址
3、接受发送
接受发送的函数以及回调函数同客户端模式相同,同样可参考《STM32无操作系统UDP》。
  接受回调函数:在接受到数据后需要记录发送数据的客户端IP地址,位置在标记接受到数据之后,代码同 tcp_accept()回调函数中的一样。
  发送回调函数:同客户端模式函数相同。但有一点需要说一下,首先参考的是正点原子的代码,在客户端模式中最后会调用tcp_output()这个函数。但是在服务器模式中最后是调用的tcp_recved()这个函数。于是我想是不是发送数据跟这两个函数都没有关系。于是我对程序进行debug调试发现不论有没有tcp_recved()或者tcp_output()函数,都会将数据发送出去。查看tcp_write()的函数说明之后找到了原因,先来看一下函数说明:
  Write data for sending (but does not send it immediately). It waits in the expectation of more data being sent soon (as it can send them more efficiently by combining them together). To prompt the system to send data now, call tcp_output() after calling tcp_write().
  大概意思是:写入发送的数据,但不会立即发送,会等待其他要发送的数据,并将它们连接起来更有效的发送,如果要立即发送应在tcp_write()函数后调用tcp_output()函数。
  这样就容易理解了,在客户端模式中使用tcp_output()函数可以立即发送数据,在服务器模式中使用tcp_recved()函数可以将接受到的间断的信息进行连接并发送(我写的函数功能是接受到数据后将数据发送出去)。
4、强制删除TCP Server主动断开时的time wait void tcp_server_remove_timewait(void) { struct tcp_pcb *pcb,*pcb2; u8 t=0; while(tcp_active_pcbs!=NULL&&t<200) { lwip_periodic_handle(); //继续轮询 t++; delay_ms(10); //等待tcp_active_pcbs为空 } pcb=tcp_tw_pcbs; while(pcb!=NULL)//如果有等待状态的pcbs { tcp_pcb_purge(pcb); tcp_tw_pcbs=pcb->next; pcb2=pcb; pcb=pcb->next; memp_free(MEMP_TCP_PCB,pcb2); } }