前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ESP8266 SDK开发: 网络篇-TCP服务器(LWIP,RAW模式,PCB控制块)

ESP8266 SDK开发: 网络篇-TCP服务器(LWIP,RAW模式,PCB控制块)

作者头像
杨奉武
发布2020-03-03 10:51:43
2.5K0
发布2020-03-03 10:51:43
举报
文章被收录于专栏:知识分享

前言

关于网络通信:

每一台电脑都有自己的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实现网络通信可以使用操作系统,也可以裸机

实现TCP通信可以用Socket 也可以用 PCB控制块

之所以有这两种方式是因为咱做上位机的时候就是用的

Socket,开发者是为了通用性所以在PCB控制块的基础上

封装了 Socket开发.

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

注意:依照项目经验,只要学会了用PCB控制块实现TCP

那么以后的项目凡是使用LWIP实现TCP的,皆会通吃!

PCB控制块是最底层的,执行速度也是最快的!

不要小看执行速度,前些日子有个项目就是做TCP服务器

一开始用的官方的,发现速度完全跟不上

后来直接用的PCB控制块,速度比原来快几十倍!!!

其实使用PCB控制块也很简单,一切都是规定好的.

就是这样用....

1.包含以下头文件

代码语言:javascript
复制
#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控制块

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

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

代码语言:javascript
复制
    //控制块绑定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

代码语言:javascript
复制
struct tcp_pcb *tcp_pcb_server;//定义一个TCP控制块

注册其它回调函数

代码语言:javascript
复制
/**
* @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("客户端连接 \n");
    return ERR_OK;
}

串口输出TCP接收的数据

代码语言:javascript
复制
#define TcpServerBuffLen 1460
u8 TcpServerBuff[TcpServerBuffLen];//接收缓存
代码语言:javascript
复制
/**
* @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://cloud.tencent.com/developer/article/1592670

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

接收回调里面

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

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

测试

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-03-02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 说明
  • 1.包含以下头文件
  • 2.new 一个TCP控制块
  • 3. 绑定IP地址和端口号,启动监听
  • 4. 设置客户端连接回调函数
  • 5. 下载测试(手机APP连接测试)
  • 6. 下载测试(电脑上位机连接测试)
  • 接收/发送数据
  • 注册其它回调函数
  • 串口输出TCP接收的数据
  • 测试
  • 模块串口接收的数据转发给TCP客户端
  • 测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档