zoukankan      html  css  js  c++  java
  • ESP8266 SDK开发: 网络篇-8266开启TCP服务器(LWIP,RAW模式,PCB控制块)

    前言

    关于网络通信:

    每一台电脑都有自己的ip地址,每台电脑上的网络应用程序都有自己的通信端口,

    张三的电脑(ip:192.168.1.110)上有一个网络应用程序A(通信端口5000),

    李四的电脑(ip:192.168.1.220)上有一个网络应用程序B(通信端口8000),

    张三给李四发消息,首先你要知道李四的ip地址,向指定的ip(李四ip:192.168.1.220)发信息,

    信息就发到了李四的电脑。

    再指定一下发送的端口号(通信端口8000),信息就发到了李四电脑的网络应用程序B上。

    TCP--一种网络通信方式而已。分为服务器(网络应用程序)和客户端(网络应用程序).

     

    说明

    对于网络模块而言实现网络通信最终还是使用的 LWIP

    LWIP实际上是别人为了让小型网络设备实现网络通信,

    而开发的低内存易移植的网络传输解析程序.

    LWIP实现网络通信可以使用操作系统,也可以裸机

    咱这节就使用 PCB控制块实现TCP服务器

    1.包含以下头文件

    #include "lwip/api.h"
    #include "lwip/err.h"
    #include "lwip/ip_addr.h"
    #include "lwip/dns.h"
    #include "lwip/igmp.h"
    #include "lwip/tcp.h"

    2.new 一个TCP控制块

    err_t err = ERR_OK;//接收返回的错误信息
    struct tcp_pcb *tcp_pcb1 = tcp_new();//建立一个TCP控制块

    3. 绑定IP地址和端口号,启动监听

        //控制块绑定IP地址和端口号
        err = tcp_bind(tcp_pcb1, IP_ADDR_ANY, 8080);//IP_ADDR_ANY:绑定本模块IP  8080:绑定8080端口
        if (err == ERR_OK) {//没有错误
            struct tcp_pcb *pcb1 = tcp_listen(tcp_pcb1);//启动监听
        }

     

    4. 设置客户端连接回调函数

     

    5. 下载测试(手机APP连接测试)

    5.1手机APP连接模块无线

    5.2 使用手机APP调试助手测试

    安装调试助手

    点击左上角菜单

     

    点击网络通信

    选择 TCP/UDP通信

    选择TCP客户端,IP地址192.168.4.1(8266默认IP)

    端口号:8080

    点击 连接

    6. 下载测试(电脑上位机连接测试)

    电脑连接8266无线

    接收/发送数据

    以后接收/发送数据都是通过获取的tcp_pcb

     为了咱方便各个地方使用,咱定义一个公共的tcp_pcb

    struct tcp_pcb *tcp_pcb_server;//定义一个TCP控制块

    注册其它回调函数

     

    /**
    * @brief   TCP接收数据
    * @param   arg:tcp_arg函数传入的参数
    * * @param   p:接收的数据缓存
    * @param   err:错误信息
    * @param   None
    * @retval  None
    * @warning None
    * @example
    **/
    static err_t net_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        tcp_pcb_server = tpcb;
    
        if (!p || err!=ERR_OK) {
            if(p){
                pbuf_free(p);
            }
            tcp_close(tcp_pcb_server);//关闭连接
            return ERR_CLSD;
        }
    
        //固定处理
        tcp_recved(tcp_pcb_server, p->tot_len);/*更新接收,告诉底层可以接着缓存数据了*/
        pbuf_free(p);//释放链表
        return ERR_OK;
    }
    
    
    /**
    * @brief   TCP链接错误
    * @param   arg:tcp_arg函数传入的参数
    * @param   err:错误信息
    * @param   None
    * @param   None
    * @retval  None
    * @warning None
    * @example
    **/
    static void net_err_cb(void *arg, err_t err) {
        tcp_pcb_server = (struct tcp_pcb*)arg; //tcp_arg传递了该参数
        tcp_close(tcp_pcb_server);//关闭连接
        tcp_pcb_server = NULL;//清空
    }
    
    /**
    * @brief   客户端连接回调
    * @param   arg:tcp_arg函数传入的参数
    * @param   newpcb:链接的TCP控制块
    * @param   err:错误信息
    * @param   None
    * @retval  None
    * @warning None
    * @example
    **/
    static err_t net_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) {
        tcp_pcb_server = newpcb;//赋值给定义的控制块
    
        tcp_arg(newpcb, newpcb);//传递的arg参数为 tcp_pcb_server
        tcp_err(newpcb, net_err_cb);//错误回调
        tcp_recv(newpcb, net_tcp_recv_cb);//接收数据回调
    
        printf("客户端连接 
    ");
        return ERR_OK;
    }

    串口输出TCP接收的数据

    #define TcpServerBuffLen 1460
    u8 TcpServerBuff[TcpServerBuffLen];//接收缓存
    /**
    * @brief   TCP接收数据
    * @param   arg:tcp_arg函数传入的参数
    * * @param   p:接收的数据缓存
    * @param   err:错误信息
    * @param   None
    * @retval  None
    * @warning None
    * @example
    **/
    static err_t net_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
        struct pbuf *q;
        u32 length = 0,i=0;
    
        tcp_pcb_server = tpcb;
    
        if (!p || err!=ERR_OK) {
            if(p){
                pbuf_free(p);
            }
            tcp_close(tcp_pcb_server);//关闭连接
            return ERR_CLSD;
        }
    
        //接收TCP数据(固定处理)
        for(q=p;q!=NULL;q=q->next)
        {
            if(q->len > (TcpServerBuffLen-length))//接收的数据个数大于了数组可以接收的数据个数
                memcpy(TcpServerBuff+length,q->payload,(TcpServerBuffLen-length));//只接收数组可以接收的数据个数
            else
                memcpy(TcpServerBuff+length,q->payload,q->len);//接收TCP所有数据
            length += q->len;
            if(length > TcpServerBuffLen) break;
        }
    
    
        /*串口输出接收的数据*/
        for(i=0;i<length;i++){
            uart_tx_one_char(UART0,TcpServerBuff[i]);
        }
    
        //固定处理
        tcp_recved(tcp_pcb_server, p->tot_len);/*更新接收,告诉底层可以接着缓存数据了*/
        pbuf_free(p);//释放链表
    
        return ERR_OK;
    }

    说明:

    对于初学者而言,有些地方不懂为什么这样做!

    其实LWIP确实挺复杂的,咱们先学会用!

    对于接收数据而言这样接收完全没有问题

    大家可以直接先用即可,如果后期大家有时间可以慢慢的

    了解LWIP

    我只提示一下

    LWIP存储数据使用的链表形式

    假设数据来了,因为数据的个数不一定,而每一个链表存数据的个数都是有限的,

    所以呢就出现了上图,把数据分割依次存入几个链表中

    测试

    模块串口接收的数据转发给TCP客户端

    判断串口接收到一条完整的数据以后,把数据发给客户端

    串口判断接收到一条完整的数据参考串口章节:

    https://www.cnblogs.com/yangfengwu/p/12375342.html

    忘了一件事情,需要定义一个变量来判断客户端是不是连接了

    接收回调里面

    链接错误回调函数 和 客户端链接回调函数里面

    好接着写咱的串口数据转发给TCP客户端程序

    测试

  • 相关阅读:
    Wireshark抓取iPhone的数据包
    AVSpeechSynthesizer
    NSData,Byte,NSString 转换
    app 国际化
    带颜色日志
    swift生成二维码
    CocosPods安装和导入第三方框架
    多线程总结
    计算机系统导论——读书笔记——第六章 存储器层次结构
    数据结构与算法——编程作业——内排序&外排序
  • 原文地址:https://www.cnblogs.com/yangfengwu/p/12393478.html
Copyright © 2011-2022 走看看