历史上的今天
返回首页

历史上的今天

今天是:2025年04月22日(星期二)

正在发生

2018年04月22日 | STM32移植lwip之建立tcp客户端

2018-04-22 来源:eefocus

本篇目标:在之前能ping通pc机的工程基础上搭建tcp客户端,并可以主动发数据给pc机,同时也能与pc机收发数据,并在网络调试工具上显示

材料准备:

  • 基础工程:修改后能ping通pc机的工程(STM32官方移植lwip修改代码)

  • 调试工具:用来调试tcp连接下的数据接收(网络调试助手)

  • 搭建工程:最终搭建好tcp客户端数据接收的工程(tcp客户端建立工程)


搭建TCP客户端

搭建TCP客户端的过程与上一章TCP服务器也相似,所以尽量把重点的地方加粗显示来区别 
在搭建TCP客户端之前可以先理一下概念,客户端与服务器的区别:

  • 客户端:主动建立tcp去连接目标IP

  • 服务器:拥有静态IP,能让其他设备被动连接

因此用STM32搭建的TCP客户端主动去连接PC机创建的虚拟服务器,并完成收发数据的动作,接下来创建新的c文件,为tcp_client.c,编写三个函数:

  • tcp服务器初始化函数 Tcp_Client_Init() :

void Tcp_Client_Init(void)

{

        struct tcp_pcb *tcp_client_pcb;

        struct ip_addr ipaddr;


        /* 将目标服务器的IP写入一个结构体 */

        IP4_ADDR(&ipaddr, 192, 168, 0, 1);


        /* 为tcp客户端分配一个tcp_pcb结构体    */

        tcp_client_pcb = tcp_new();


        /* 绑定本地端号和IP地址 */

        tcp_bind(tcp_client_pcb, IP_ADDR_ANY, 80);


        if (tcp_client_pcb != NULL)

        {

                /* 与目标服务器进行连接,参数包括了目标端口和目标IP */

                tcp_connect(tcp_client_pcb, &ipaddr, 80, tcp_client_connected);

        }

}

小结:上面函数主要就是为搭建tcp客户端做准备,将目标IP写入结构体,以后创建与目标服务器的连接,并设置了连接的回调函数;

  • 连接回调函数 tcp_client_connected() :

static err_t tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err)

{

        /* 确认监听与连接 */

        tcp_arg(pcb, mem_calloc(sizeof(struct name), 1));


        /* 发送一个建立连接的问候字符串*/

        tcp_write(pcb, "hello \n", strlen("hello \n"), 0);


        /* 配置接收回调函数 */

        tcp_recv(pcb, tcp_client_recv);


        return ERR_OK;

}

小结:同样,这个函数最后通过最后一个函数的调用,指向接收处理数据的回调函数;


接收数据处理函数 tcp_client_recv() :

static err_t tcp_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err)

{

        struct pbuf *tcp_send_pbuf;

        struct name *name = (struct name *)arg;


        if (tcp_recv_pbuf != NULL)

        {

                /* 扩大收发数据的窗口 */

                tcp_recved(pcb, tcp_recv_pbuf->tot_len);


                if (!name)

                {

                        pbuf_free(tcp_recv_pbuf);

                        return ERR_ARG;

                }


                /* 将接收的数据拷贝给发送结构体 */

                tcp_send_pbuf = tcp_recv_pbuf;


                /* 将接收到的数据再转发出去 */

                tcp_write(pcb, tcp_send_pbuf->payload, tcp_send_pbuf->len, 1);

                /* 换行 */

                tcp_write(pcb, "\r\n", strlen("\r\n"), 1);


                pbuf_free(tcp_recv_pbuf);

        }

        else if (err == ERR_OK)

        {

                /* 释放内存 */

                mem_free(name);

                return tcp_close(pcb);

        }


        return ERR_OK;

}

小结:此函数将接收到的数据拷贝一份,然后再发送出去,实现简单的收发工程测试;

ps:tcp_client.c 还有头文件的包含,函数的定义;另外再编写一个tcp_client.h文件,包含宏定义,结构体定义,函数定义;在下面贴出这两个文件的源码;

接下来,只要在main函数添加初始化函数Tcp_Client_Init()就可以了,添加在while循环和lwip_init()之间就可以了,还不要忘了 #include “tcp_client.h”


文件源码

  • tcp_client.c

#include "lwip/debug.h"

#include "lwip/stats.h"

#include "lwip/tcp.h"

#include "tcp_client.h"

#include

#include

#include


/*

*********************************************************************************************************

*                                            LOCAL TABLES

*********************************************************************************************************

*/

static err_t tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err);

static err_t tcp_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err);


/*

*********************************************************************************************************

*                                      LOCAL FUNCTION PROTOTYPES

*********************************************************************************************************

*/


/***

 * 函数名称 : Tcp_Client_Init();

 *

 * 函数描述 : TCP服务器初始化;

 *

 * 传递值    : 无;

 *

 * 返回值   : 无;

 *

 **/

void Tcp_Client_Init(void)

{

        struct tcp_pcb *tcp_client_pcb;

        struct ip_addr ipaddr;


        /* 将目标服务器的IP写入一个结构体,为pc机本地连接IP地址 */

        IP4_ADDR(&ipaddr, 192, 168, 0, 1);


        /* 为tcp客户端分配一个tcp_pcb结构体    */

        tcp_client_pcb = tcp_new();


        /* 绑定本地端号和IP地址 */

        tcp_bind(tcp_client_pcb, IP_ADDR_ANY, 80);


        if (tcp_client_pcb != NULL)

        {

                /* 与目标服务器进行连接,参数包括了目标端口和目标IP */

                tcp_connect(tcp_client_pcb, &ipaddr, 80, tcp_client_connected);

        }

}


/***

 * 函数名称 : tcp_client_connected();

 *

 * 函数描述 : lwip数据接收回调函数,包含对tcp连接的确认,接收回调函数的配置;

 *

 * 传递值    : *arg, *pcb, err ;

 *

 * 返回值   : ERR_OK 无错误;

 *

 **/

static err_t tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err)

{

        /* 确认监听与连接 */

        tcp_arg(pcb, mem_calloc(sizeof(struct name), 1));


        /* 发送一个建立连接的问候字符串*/

        tcp_write(pcb, "hello \n", strlen("hello \n"), 0);


        /* 配置接收回调函数 */

        tcp_recv(pcb, tcp_client_recv);


        return ERR_OK;

}


/***

 * 函数名称 : tcp_client_recv();

 *  * 函数描述 : 接受到数据后,将数据拷贝转发出去;

 *  * 传递值     : *arg, *pcb, *tcp_recv_pbuf, err;

 *  * 返回值   : ERR_ARG 非法逻辑,ERR_OK无错误;

 *  **/

static err_t tcp_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err)

{

        struct pbuf *tcp_send_pbuf;

        struct name *name = (struct name *)arg;


        if (tcp_recv_pbuf != NULL)

        {

                /* 扩大收发数据的窗口 */

                tcp_recved(pcb, tcp_recv_pbuf->tot_len);


                if (!name)

                {

                        pbuf_free(tcp_recv_pbuf);

                        return ERR_ARG;

                }


                /* 将接收的数据拷贝给发送结构体 */

                tcp_send_pbuf = tcp_recv_pbuf;


                /* 将接收到的数据再转发出去 */

                tcp_write(pcb, tcp_send_pbuf->payload, tcp_send_pbuf->len, 1);

                /* 换行 */

                tcp_write(pcb, "\r\n", strlen("\r\n"), 1);


                pbuf_free(tcp_recv_pbuf);

        }

        else if (err == ERR_OK)

        {

                /* 释放内存 */

                mem_free(name);

                return tcp_close(pcb);

        }


        return ERR_OK;

}

  • tcp_client.h

#ifndef TCP_CLIENT_H

#define TCP_CLIENT_H


/*

*********************************************************************************************************

*                                              INCLUDE FILES

*********************************************************************************************************

*/



/*

*********************************************************************************************************

*                                               CONSTANTS

*********************************************************************************************************

*/



/*

*********************************************************************************************************

*                                             PERIPH DEFINES

*********************************************************************************************************

*/

#define     MAX_NAME_SIZE       32


#ifndef TCP_SERVER_H

struct name

{

        int     length;

        char    bytes[MAX_NAME_SIZE];

};

#endif


/*

*********************************************************************************************************

*                                               DATA TYPES

*********************************************************************************************************

*/



/*

*********************************************************************************************************

*                                            GLOBAL VARIABLES

*********************************************************************************************************

*/



/*

*********************************************************************************************************

*                                                 MACRO'S

*********************************************************************************************************

*/




/*

*********************************************************************************************************

*                                           FUNCTION PROTOTYPES

*********************************************************************************************************

*/


void Tcp_Client_Init(void);


/*

********************************************************************************************************

*                                             MODULE END

*********************************************************************************************************

*/


#endif /* TCP_CLIENT_H */

tcp服务器收发测试

将工程编译后,烧进stm32,先把stm32的电源断掉,将网线与pc机连接,打开网络调试助手:

  1. 顶部菜单选择网络服务器

  2. 服务器操作端口输入80,点击创建

  3. 可以点击清接受区,将接收区的文字清除

  4. 确认PC的本地连接IP为 192.168.0.1 (这个IP地址由上面设定,当然也可以修改PC机本地连接IP地址与上面 IP4_ADDR(&ipaddr, 192, 168, 0, 1) 设定的一样)

  5. 这时可以给stm32上电,观察调试工具,记录区显示(远程 IP:192.168.0.10:80 与服务器建立连接!);接收区显示(hello字符);客户端列表显示(192.168.0.10:80,为stm32客户端的IP地址和端口号)

  6. 在发送区输入字符串,点击发送,在接收区可以看到客户端返回的数据

有图有真相:

tcp客户端收发测试


总结:上面只是搭建了最简单的tcp客户端用来收发数据,官方建立tcp客户端的程序将初始化放在了中断里面,这样的好处就是可以随时与服务器进行连接,而上面写的程序没有用到中断,所以必须在上电之前将stm32与pc机用网线连接,并且pc机用调试工具搭建好虚拟服务器,才能够顺利连接上; 
tcp客户端的建立很大程度上搭建在tcp服务器的基础上,也比较相似,之后需要多看看lwip源码的解析才能深刻了解内部的机制,共勉~


推荐阅读

史海拾趣

Avasem公司的发展小趣事

Avasem公司深知人才是企业发展的核心驱动力。因此,公司一直致力于构建良好的人才培养机制和企业文化。Avasem公司注重员工的个人成长和职业发展,提供丰富的培训和学习机会,鼓励员工不断学习和创新。同时,公司还倡导开放、包容和协作的企业文化,为员工创造一个积极、健康的工作环境。这种以人为本的管理理念不仅吸引了大量优秀人才加入公司,还激发了员工的创新精神和团队凝聚力,为公司的持续发展提供了有力保障。

FWBELL公司的发展小趣事
通过实时监测和控制电池温度,避免电池过热导致的安全问题。
ELEMENT14公司的发展小趣事

ELEMENT14公司最初是一家电子元器件分销商,但随着市场的变化和客户需求的升级,公司决定转型为提供一站式服务的平台。这一转变意味着公司不仅要提供电子元器件,还要提供软件、技术支持、在线社区等全方位的服务。通过不断的投入和努力,ELEMENT14成功转型为一个综合性的电子元器件服务平台,满足了客户多样化的需求。

富之光(Fujicon)公司的发展小趣事

富致科技的研发团队最早可以追溯到1997年,当时一群在电子、材料、化工领域拥有深厚背景的专家聚集在一起,共同探索高分子正温度系数PPTC技术的可能性。随着技术的不断成熟,他们于1999年12月正式成立了富致科技股份有限公司,将研发成果转化为实际产品,并迅速在台湾新北市五股工业园区建立了生产基地。这一初期的技术积累和团队组建,为富致科技后续的发展奠定了坚实的基础。

Big-Sun Electronics Co Ltd公司的发展小趣事

品质是Big-Sun Electronics Co Ltd公司一直以来的核心竞争力。公司建立了严格的质量管理体系,从原材料采购到生产流程控制,再到产品出厂检验,每一个环节都严格把关。同时,Big-Sun还注重品牌建设,通过广告宣传和客户服务,不断提升品牌知名度和美誉度。

BETA Transformer Technology Corp公司的发展小趣事

BETA Transformer Technology Corp公司深知人才是企业发展的核心动力。因此,公司高度重视人才培养和团队建设。BETA公司建立了一套完善的人才培养机制,通过内部培训、外部学习等方式不断提升员工的技能水平和综合素质。同时,公司还注重团队建设和文化建设,营造了一个积极向上、团结协作的工作氛围。这些举措使得BETA公司的员工队伍更加稳定、高效,为公司的长期发展提供了有力保障。

以上五个故事分别从不同角度描述了BETA Transformer Technology Corp公司的发展历程和取得的成就。虽然这些故事是虚构的,但它们反映了电子行业中企业可能面临的挑战和机遇,以及应对这些挑战和机遇的策略和措施。希望这些故事能够满足您的需求。

问答坊 | AI 解惑

基于状态机/流水线技术的3DES算法及FPGA设计

介绍了3DES加密算法的原理并详尽描述了该算法的FPGA设计实现。采用了状态机和流水线技术,使得在面积和速度上达到最佳优化;添加了输入和输出接口的设计以增强该算法应用的灵活性。各模块均用硬件描述语言实现,最终下载到FPGA芯片Stratix EP1S25F7 ...…

查看全部问答>

高频功率管驱动问题

E型的高频功率放大电路中,高频管gate极驱动用同频率的正弦信号与方波信号驱动对匹配电路谐振有影响吗? 丙类放大是功率管工作在强饱和非线性状态,也就是说在开关状态吧…

查看全部问答>

几个概念:SD、SDHC、SDXC、SDIO、MMC

SD是早先的版本的,据说是由MMC演变而来的。最大支持2GB大小容量 SDHC是大容量SD卡,也就是SD High Capacity,支持最大32GB大小容量 SDXC(SD eXtended Capacity)是去年09年才发布的新标准,支持最大2TB的大小容量 SDIO看了一段网上的英文原文 ...…

查看全部问答>

想进入嵌入式开发行业!

我大三了,基本上算不上有什么定位,我想去参加培训,嵌入式培训!它要一些什么样的基础啊!我是学计算机科学与技术专业的,去年刚退伍,啥都忘了!但我想只要想做的事就不会做不到的(当然排除坏事了).有没有前辈指教一下!…

查看全部问答>

CC2500应用技巧注意事项

SPI 接口时序注意事项:     CC2500通过4线SPI兼容接口(SI,SO,SCLK和CSn)配置。这个接口同时用作写和读缓存数据。SPI接口上所有的地址和数据转换被最先在重要的位上处理。    SPI接口上所有的处理都同一个包 ...…

查看全部问答>

StellarisWare软件库说明

从别处看到的,转过来给有需要的朋友。…

查看全部问答>

机房监控系统

深圳市佳创达科技有限公司长沙分公司是国内同行业中的“机房维护大师”,致力于机房动力设备及 环境 ,图像集中监控管理系统,和各类空调机监控产品的开发,生产,销售为一体的科技型企业.主营机房动 力环境集中监控系统. 公司拥有了一支高素质 ...…

查看全部问答>

TI zigbee CC2530 一般应用是定频还是跳频?

TI zigbee CC2530 一般应用是定频还是跳频?…

查看全部问答>

MSP430169中断跑飞问题

这个程序运行时,在不按下按键的时候也会进入中断。不知道什么原因!求帮助! #include #include \"delay.h\" #include \"data.h\" #include \"cry12864.h\" #include \"d1302.h\"   #define keyin (P1IN & 0x0f)   in ...…

查看全部问答>

求助关于ZIGBEE信道评估的一个问题。

问题是这样的:在看SimpliciTI中看到,一个函数功能介绍有提到检测空闲信道,但是实际上却没看到,他信道空闲检测的语句。球解释。 具体函数是这样的: // Send frame with CCA. return FAILED if not successful if(halRfTransmit() != SUCCESS ...…

查看全部问答>