[网络篇]ESP8266-SDK教程(三)之TCP通信Server<->Client

纳尼?昨天刚刚打印了个“Hello World!”,今天你就让我学习TCP通信?有没有搞错~哈哈,相信很多读者会很迷,其实学习这东西嘛,单单学一些比较简单的,相信没两天就没人看了,所以咱就在基础篇和网络篇穿插着去学习一下ESP8266,毕竟兴趣才是最好的老师嘛!大家以后遇到问题了,来翻文章建议大家根据[XX篇]去快速定位该去哪一篇文章中去查找问题,当然具体会在哪一篇文章中有讲,也不一定了,后面也会穿插着写一点[项目篇][进阶篇][闲扯篇],总的来说就是,本系列文章并没有固定的路线,大家不如忍忍?当然这里写的文章也不一定是对的,大家在实际测试中如果遇到了问题,还请私信我,或者在Github上提交issue!

闲话少说,既然要学习TCP通信的知识,那么我们得先要了解一下TCP到底是个什么东西(玩意),正所谓孙子曾经曰过“知彼知己,百战不殆”,那我们先补一些关于TCP的知识吧!

TCP基础知识:

  • 是什么?

TCP(Transmission Control Protocol传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。(百度词条搜到的~)

说白了就是一种传输协议,就像我们串口、232、485等一样的通讯协议,只不过TCP多用于网络间的通讯,不过TCP是在IP层之上的,相信你肯定见过或者听说过TCP/IP协议,没错,这哥俩是绑在一块的,TCP是依赖IP协议的。

  • 通讯机制

TCP与我们常使用的串口等通讯协议有什么不同呢?首先是两台主机建立TCP连接机制是比较麻烦的,要经过三次握手协议,才能建立连接,过程用汉字描述如下:

  • 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
  • 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
  • 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。

看字比较麻烦,我们还是看图吧!

image

既然有建立连接,那么肯定也有断开连接,但是没想到断开连接比建立连接还要麻烦,还有四次挥手协议,纳尼?是的,断开连接需要经过四次挥手协议,直接看图吧:

image
  • 可靠性

上面说的通讯机制之所以这么麻烦,主要还是为了传输的可靠性,因为TCP是在整个网络中去传输,只要你有IP地址,并且在广域网中访问,那我们就可以建立TCP连接,说会悄悄话所以,两者之间的可靠性就显得很重要了,客户端与服务端之间的连接可靠性靠很多策略去实现的比如心跳包机制,这里就不再详细叙述了,有点跑题了,这是ESP8266学习笔记

  • 数据包格式

TCP的数据包相对来说是比较麻烦的,确认信息占据了整个数据包的很大一部分,设计的这么复杂主要原因还是保证在整个网络当中每包数据传输的正确性,这里我们就看一下数据包到底有多么复杂:

image
  • ESP8266作为TCP Client跟Server(PC)通信

了解了一点点基础知识后,我们还是要落实到实践上,下面我们正式开始,首先我们需要先看一下官方SDK编程指导手册,还没有?不知道去哪里下?好嘞,直接戳下面卡片下载好了~

官方SDK编程指导手册

英文版戳这里,如果你英语跟小编一样好的话,建议看英文版,要不断提升自己的英语~

官方SDK编程指导手册英文版

咳咳咳,那我们先打开这个英文文档,找到97页的TCP/UDP接口,我们先看一下接口分了几类:

image

咳咳咳,不要怀疑,使用的PDF查看软件有自动翻译动能,手动滑稽.jpg,可以看到一共分了4类,本篇文章我们先研究下通用 API和TCP API,关于UDP和mDNS的我们再后面的文章中再继续学习,你可以先大体看一下,每个API的注释都讲的很明白,我们不妨直接copy点代码直接跑一下?

1 git clone git@github.com:imliubo/makingfunxyz-esp8266.git 2 3 #如果之前有clong过,可以直接 git pull,不会用?那肯定是没好好看廖雪峰的git教程

下载完成后,打开ESP_IDE导入在makingfunxyz-esp8266-NONOS文件中的6.TCP_UDP_Server_Client这个工程,然后先修改一点东西,这一步很重要,请不要忽略:

image

Wi-Fi名称跟密码相信你肯定知道,TCP_SERVER_IP应该要修改成什么呢?就是你电脑的IP地址,这里每个人的都不一样,建议你先去查看一下,方法看视频~

视频还在找托管方~

可以看到我这里是192.168.0.109,那我就将它修改成192.168.0.109,这三个地方都修改好了后,我们直接先编译代码,先跑一下看看!编译好代码后,下载到ESP8266,这里跟上篇文章中是一样的,就不再详述,下载地址参考:

  • eagle.flash.bin-------->0x00000
  • eagle.irom0text.bin---->0x10000
  • esp_init_data_default_v08.bin --> 0x3FC000
  • blank.bin --> 0x3FE000

下载完成后,我们先不要急着去看结果,因为我们还没有开启TCP Server,那我们该怎么开启TCP Server呢?我这里用Python写了一个很简单的Server端程序,大家可以使用我这个,要是使用别的TCP调试助手,注意TCP_SERVER_PORT也要修改一下,运行我们的Server很简单,但是你需要安装一下Python,我这里没有打包成可执行文件,先去安装一下Python吧,我用的是Python3,建议大家也安装Python3,因为Python2跟Python3有些语法不一样,戳卡片下载:

Python下载安装

下载安装完成后,开始搞起来,我们需要在6.TCP_UDP_Server_Client这个文件夹下找到TCP_Server.py这个文件夹,然后打开修改一下IP地址,将IP地址修改成上面我们查看的本机地址,看图:

image

最后我们还需要设置一下开放端口,这里步骤有点多,就不再细述了,大家可以直接看这个百度知道,写的很详细:

百度知道开放端口

然后打开命令窗口,具体操作办法,在此文件下按住shift键,然后右键选择打开powershell窗口(WIN7 应该是cmd窗口),然后:

1 start cmd 2 #可以直接在powershell里面执行,但是我还是比较喜欢cmd窗口,所以就~win7不用,因为打开的就是cmd窗口 3 #cmd窗口打开后,输入以下命令 4 python TCP_Server.py

视频还在找托管方~

  • ESP8266作为TCP Server跟Client(PC)通信

上面是ESP8266作为Client去跟Server通信,但是ESP8266不仅可以作为Client还可以作为Server等待Client去建立连接去通信,这里我们修改几个地方,就可以将ESP8266作为Server去跟Client通信了,我在源码中已经都写好了,这里我们将同样将ESP8266的6666作为PC去连接的端口号:

image

其中TCP_Client.py文件中的IP地址需要在ESP8266上电打印后修改一下,我们将上面小节中的tcp_client_init()注释掉,tcp_server_init()取消注释,然后重新编译代码下载就好了,PC上的Client程序跟Server程序运行一样,这里我们直接看一下视频吧!

视频还在找托管方~

这里代码就不再解释了,我写的注释还算全,大家一看就懂,主要代码:

/****************************
 *   TCP CLIENT FUNCTIONS   *
 ****************************/

/**********************************
 *  TCP CLIENT STATIC PROTOTYPES  *
 **********************************/
static void tcp_client_sent_cb(void *arg);
static void tcp_client_recv_cb(void *arg,char *pdata,unsigned short length);
static void tcp_client_recon_cb(void *arg,sint8 error);
static void tcp_client_discon_cb(void *arg);
static void tcp_client_connect_cb(void *arg);

/**********************************
 *   TCP CLIENT STATIC VARIABLES  *
 **********************************/
os_timer_t tcp_client_send_data_timer;
struct espconn tcp_client;
uint8 i;

/**********************************
 *   TCP CLIENT STATIC FUNCTIONS  *
 **********************************/

/**
 * TCP Client数据发送回调函数
 */
static void ICACHE_FLASH_ATTR
tcp_client_sent_cb(void *arg){
    os_printf("tcp client send data successful\r\n");
}

/**
 * TCP Client数据接收回调函数,可以在这处理收到Server发来的数据
 */
static void ICACHE_FLASH_ATTR
tcp_client_recv_cb(void *arg,char *pdata,unsigned short len){
    os_printf("tcp client receive tcp server data\r\n");
    os_printf("length: %d \r\ndata: %s\r\n",len,pdata);

    //TO DO

    /**
     *process the receive data
     */
}

/**
 * TCP Client重连回调函数,可以在此函数里做重连接处理
 */
static void ICACHE_FLASH_ATTR
tcp_client_recon_cb(void *arg,sint8 error){
    os_printf("tcp client connect tcp server error %d\r\n",error);
    os_timer_disarm(&tcp_client_send_data_timer);//取消定时发送数据定时器
}

/**
 * TCP Client断开连接回调函数
 */
static void ICACHE_FLASH_ATTR
tcp_client_discon_cb(void *arg){
    os_printf("tcp client disconnect tcp server successful\r\n");
    os_timer_disarm(&tcp_client_send_data_timer);
}

/**
 * TCP Client连接成功回调函数
 */
static void ICACHE_FLASH_ATTR
tcp_client_connect_cb(void *arg){
    struct espconn *pespconn = arg;

    os_printf("tcp client connect tcp server successful\r\n");
    espconn_regist_recvcb(pespconn,tcp_client_recv_cb);//注册接收数据回调函数
    espconn_regist_sentcb(pespconn,tcp_client_sent_cb);//注册数据发送完成回调函数
    espconn_regist_disconcb(pespconn,tcp_client_discon_cb);//注册断开连接回调函数

    os_timer_disarm(&tcp_client_send_data_timer);
    os_timer_setfn(&tcp_client_send_data_timer, (os_timer_func_t *) tcp_client_send_data,NULL);//注册Client定时发送数据回调函数
    os_timer_arm(&tcp_client_send_data_timer, 1000, true);//时间设置为1s
}

/**********************************
 *   TCP CLIENT GLOBAL FUNCTIONS  *
 **********************************/
/**
 * TCP Client定时发送数据回调函数
 */
void ICACHE_FLASH_ATTR
tcp_client_send_data(void){
    char buf[256],length;
    os_printf("tcp client send data tcp server\r\n");
    length = os_sprintf(buf,(char *)"Hi this is ESP8266 client! message num %d",i);
    i++;
    espconn_sent(&tcp_client,buf,length);
}

/**
 * TCP Client初始化函数
 * @remote_ip    要连接的TCP Server IP地址
 * @local_ip     ESP8266 IP地址
 * @remote_port  要连接的TCP Server端口号
 */
void ICACHE_FLASH_ATTR
tcp_client_init(uint8 *remote_ip,struct ip_addr *local_ip, int remote_port){

    uint32 server_ip = ipaddr_addr(remote_ip);

    os_printf("tcp client connect to tcp server\r\n");
    tcp_client.proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
    tcp_client.type = ESPCONN_TCP;

    os_memcpy(tcp_client.proto.tcp->remote_ip,&server_ip,4);//设置要连接的Server IP地址
    tcp_client.proto.tcp->remote_port = remote_port;//设置要连接的Server 端口号
    os_memcpy(tcp_client.proto.tcp->local_ip,local_ip,4);//设置本地IP地址
    tcp_client.proto.tcp->local_port = espconn_port();//设置本地端口号

    espconn_regist_connectcb(&tcp_client,tcp_client_connect_cb);//注册连接成功回调函数
    espconn_regist_reconcb(&tcp_client,tcp_client_recon_cb);//注册断连重新连接回调函数

    espconn_connect(&tcp_client);//Client连接Server
}



/****************************
 *   TCP SERVER FUNCTIONS   *
 ****************************/
/**********************************
 *  TCP SERVER STATIC PROTOTYPES  *
 **********************************/
static void tcp_server_sent_cb(void *arg);
static void tcp_server_recv_cb(void *arg,char *pdata,unsigned short length);
static void tcp_server_recon_cb(void *arg,sint8 error);
static void tcp_server_discon_cb(void *arg);
static void tcp_server_listen_cb(void *arg);

/**********************************
 *   TCP SERVER STATIC VARIABLES  *
 **********************************/
os_timer_t tcp_server_send_data_timer;
struct espconn tcp_server;
uint8 z;

/**********************************
 *   TCP server STATIC FUNCTIONS  *
 **********************************/

/**
 * TCP Server数据发送回调函数
 */
static void ICACHE_FLASH_ATTR
tcp_server_sent_cb(void *arg){
    os_printf("tcp server send data successful\r\n");

}

/**
 * TCP Server数据接收回调函数,可以在这处理收到Client发来的数据
 */
static void ICACHE_FLASH_ATTR
tcp_server_recv_cb(void *arg,char *pdata,unsigned short len){
    os_printf("tcp server receive tcp client data\r\n");
    os_printf("length: %d \r\ndata: %s\r\n",len,pdata);

    //TO DO

    /**
     *process the receive data
     */
}

/**
 * TCP Server重连回调函数,可以在此函数里做重连接处理
 */
static void ICACHE_FLASH_ATTR
tcp_server_recon_cb(void *arg,sint8 error){
    os_printf("tcp server connect tcp client error %d\r\n",error);
    os_timer_disarm(&tcp_server_send_data_timer);
}

/**
 * TCP Server断开连接回调函数
 */
static void ICACHE_FLASH_ATTR
tcp_server_discon_cb(void *arg){
    os_printf("tcp server disconnect tcp client successful\r\n");
    os_timer_disarm(&tcp_server_send_data_timer);
}

/**
 * TCP Server监听Client连接回调函数
 */
static void ICACHE_FLASH_ATTR
tcp_server_listen_cb(void *arg){
    struct espconn *pespconn = arg;

    os_printf("tcp server have tcp client connect\r\n");
    espconn_regist_recvcb(pespconn,tcp_server_recv_cb);//注册收到数据回调函数
    espconn_regist_sentcb(pespconn,tcp_server_sent_cb);//注册发送完数据回调函数
    espconn_regist_disconcb(pespconn,tcp_server_discon_cb);//注册断开连接回调函数

    os_timer_disarm(&tcp_server_send_data_timer);
    os_timer_setfn(&tcp_server_send_data_timer, (os_timer_func_t *) tcp_server_send_data,NULL);//注册Server定时发送数据回调函数
    os_timer_arm(&tcp_server_send_data_timer, 1000, true);//设置时间为1s
}

/**********************************
 *   TCP CLIENT GLOBAL FUNCTIONS  *
 **********************************/

/**
 * TCP Server定时发送数据回调函数
 */
void ICACHE_FLASH_ATTR
tcp_server_send_data(void){
    char buf[256],length;
    os_printf("tcp server send data tcp client\r\n");
    length = os_sprintf(buf,(char *)"Hi this is ESP8266 server! message num %d",z);
    z++;
    espconn_send(&tcp_server,buf,length);
}

/**
 * TCP Server初始化函数
 * @local_port 本地监听端口号,与Client连接的端口号一致
 */
void ICACHE_FLASH_ATTR
tcp_server_init(uint16 local_port){

    os_printf("tcp server waiting tcp client connect!\r\n");
    tcp_server.proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
    tcp_server.type = ESPCONN_TCP;

    tcp_server.proto.tcp->local_port = local_port;//设置本地监听的端口号,等待Client连接

    espconn_regist_connectcb(&tcp_server,tcp_server_listen_cb);//注册Server监听回调函数
    espconn_regist_reconcb(&tcp_server,tcp_server_recon_cb);//注册断连重新连接回调函数

    espconn_accept(&tcp_server);//创建Server,开始监听
    espconn_regist_time(&tcp_server,360,0);//超时断开连接时间
}

user_main.c中的主要代码:

os_timer_t wifistate_checktimer;
void ICACHE_FLASH_ATTR
WifiStatus_Check(void){
    uint8 wifiStatus;
    wifiStatus = wifi_station_get_connect_status();
    if (wifiStatus == STATION_GOT_IP) {
        os_printf("WiFi connection is successful!\r\n");
        os_timer_disarm(&wifistate_checktimer);
        struct ip_info local_ip;
        wifi_get_ip_info(STATION_IF,&local_ip);
        tcp_client_init(TCP_SERVER_IP,&local_ip.ip,TCP_SERVER_PORT);//TCP Client初始化,Client与Server只能二选一
//        tcp_server_init(TCP_LOCAL_PORT);//TCP Server初始化,Client与Server只能二选一
    }else{
        os_printf("WiFi connection failed!\r\n");
    }
}

/**
 * Wi-Fi连接回调函数
 */
void ICACHE_FLASH_ATTR
wifiConnectCb(uint8_t status){

    os_timer_disarm(&wifistate_checktimer);
    os_timer_setfn(&wifistate_checktimer, (os_timer_func_t *) WifiStatus_Check,NULL);
    os_timer_arm(&wifistate_checktimer, 2000, true);
}

void ICACHE_FLASH_ATTR
user_init(void)
{
    os_printf("\nHello World! ZHIHU IAMLIUBO\n\n");

    wifi_set_opmode(0x01);

    WIFI_Connect(STA_SSID, STA_PASS, wifiConnectCb);//
}

最后附上我的ESP8266仓库,后面代码会全部在此仓库更新,欢迎小伙伴们Star~

makingfunxyz-esp8266

本系列文章在知乎,博客园,同步更新,知乎搜索专栏:IAMLIUBO的神奇物联网之旅

想看文章中的视频的,可以先去知乎上的文章看一下~
QQ交流群:592587184

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容