历史上的今天
返回首页

历史上的今天

今天是:2025年07月23日(星期三)

正在发生

2019年07月23日 | STM32移植lwip之建立tcp客户端

2019-07-23 来源: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, "rn", strlen("rn"), 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, "rn", strlen("rn"), 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

推荐阅读

史海拾趣

BNS Solutions公司的发展小趣事

随着电子行业的不断发展,新技术和新应用层出不穷。BNS Solutions公司紧跟时代步伐,不断加大研发投入,推动技术创新。公司团队不断探索新技术、新材料的应用,开发出了一系列具有创新性和前瞻性的电子产品。这些产品不仅提高了行业的整体技术水平,还引领了行业变革的潮流。通过持续创新,BNS Solutions公司在电子行业中保持了领先地位,成为了行业的佼佼者。

ET Enterprises Ltd公司的发展小趣事

进入1940年代后期,ET Enterprises Ltd公司将业务重心转向光电倍增管的开发和制造。通过不断的技术创新和市场拓展,公司逐渐在光电倍增管领域建立起领先地位,成为国际知名的光电倍增管供应商。

Ceramics公司的发展小趣事

“跨界陶瓷”公司凭借其前瞻性的战略眼光,积极探索与其他行业的跨界合作。该公司与多家科研机构和高新技术企业建立了战略合作关系,共同研发新型陶瓷电子材料和技术。通过跨界合作,跨界陶瓷成功将陶瓷材料应用于新能源汽车、智能家居等新兴领域,为公司开辟了新的发展空间和市场机会。

这些故事虽然基于虚构,但反映了电子行业中Ceramics公司可能面临的机遇和挑战,以及他们如何通过技术创新、环保理念、国际化战略、品质管理和跨界合作等方式实现发展的普遍现象。当然,实际情况中每家公司的具体发展历程和策略都会有所不同,需要根据具体情况进行分析和评估。

Advanced Linear Devices公司的发展小趣事

“品质陶瓷”公司深知品质是企业生存和发展的根本。因此,该公司建立了严格的质量管理体系,从原材料采购到产品出厂的每一个环节都进行严格把控。品质陶瓷还引入了先进的生产设备和技术,不断提升产品的性能和质量稳定性。这一品质管理策略使得该公司的产品在市场上具有极高的竞争力,赢得了众多客户的信任和好评。

EM Devices Corporation公司的发展小趣事

EM Devices Corporation的创始人在电子工程领域有着深厚的背景,他们看到了当时市场上对高性能电子元件的迫切需求。于是,他们决定创办EM Devices Corporation,专注于研发和生产高质量的电子元件。起初,公司面临了资金、技术和市场等多方面的挑战。然而,凭借创始人对技术的执着追求和对市场的敏锐洞察,公司成功研发出了一款具有竞争力的产品,并逐渐在市场上获得了认可。

Dynex公司的发展小趣事

随着公司的发展,EM Devices Corporation不断加大对研发的投入,致力于推出更具创新性的产品。一次偶然的机会,公司的研发团队发现了一种新型材料,它具有优异的电气性能和稳定性。经过数月的艰苦研发,公司成功将这种材料应用于产品中,并推出了一款性能卓越的新型电子元件。这一创新产品不仅为公司带来了丰厚的利润,还进一步巩固了公司在行业内的领先地位。

问答坊 | AI 解惑

有没有函数测试内存页是否存在

有没有函数测试内存页是否存在  驱动里面的函数…

查看全部问答>

BF531 RTC 上电时跑快,断电由电池供电时,RTC正常,有中遇到类似问题吗?

BF531 RTC 上电时跑快,断电由电池供电时,RTC正常,有中遇到类似问题吗?…

查看全部问答>

关于驱动中分配缓存的疑问

各位高手,我现在在写一个DMA的驱动,需要在一个接口中建立消息队列,在内存中开辟缓存的时候 请问该怎样实现?我看到有人说过在驱动中要避免使用malloc,因为sysHwInit()时内存未初始化, 会造成系统崩溃。我的函数类似下面这样: fun1(void *d ...…

查看全部问答>

坤驰精仪招聘电子类软硬件工程师、FPGA工程师等

公司是致力于基于计算机的高端虚拟仪器设计与开发的研发类企业。欢迎有一定经验积累者问询和加盟。1、硬件工程师:1)本科及以上学历,测控、信号处理、仪器仪表、通信、电子信息等相关专业2)熟悉高速数字电路设计;数模混合电路设计,熟悉芯片和 ...…

查看全部问答>

zigbee采集传感器(氨气)数值问题

我遇到的问题是这样的:用zigbee节点采集两线制氨气变送器的数据无线发送给网关,zigbee节点采用的是飞思卡尔的芯片,氨气变送器输出时4-20ma的电流,zigbee节点采集的也是4-20ma电流,我测过氨气变送器输出时4-20ma稳定的电流值,接上节点后用万用 ...…

查看全部问答>

求科普,DCDC转换的三种典型的工作模式差异和组合模式

昨天听世强组织的一场理光电源技术研讨会,对俺们这类菜鸟学到了不少知识,不过很多技术概念比较迷糊,听起来有些内容比较费劲。本来想会后向世强的FAE求教,不过貌似大家对理光的电源产品很感兴趣,把理光和世强的人围着问个不停,俺们菜鸟也智能 ...…

查看全部问答>

LP2951如何输出3.3V

需要使用LP2951输出3.3V电压,采用3.3V版本的片子要怎么连接呢?数据手册没看明白,求指教. …

查看全部问答>

焊盘的regular pad thermal relief和anti pad在某一层能同时存在吗

通孔类的via在内层时,它的regular pad thermal relief和anti pad同时存在的吗,还是三者是三种特性,在某一层只能选择其一作为和内层连接或者隔离?? …

查看全部问答>

【R7F0C809】USB充电及采样电路设计

本帖最后由 ljj3166 于 2015-9-20 13:49 编辑 在活动DIY之前,一直很天真地以为TP4056这块充电IC能够直接给手机或者座充进行充电 随着活动的深入,发现它输出钳位至4.2v,基本上无法给手机直充 仔细想想,其实手机内部电路板上应该就有充电管IC ...…

查看全部问答>

求助!项目经理都是从技术人员做起的吗?

本帖最后由 airtight 于 2015-12-3 08:56 编辑 各位有项目经理吗?都是从技术人员做起的吗? 电子产品项目交流群,QQ群:203918642,欢迎入群!与业内同行一起交流! …

查看全部问答>