本篇目标:在之前能ping通pc机的工程基础上搭建tcp客户端,并可以主动发数据给pc机,同时也能与pc机收发数据,并在网络调试工具上显示
材料准备:
搭建TCP客户端的过程与上一章TCP服务器也相似,所以尽量把重点的地方加粗显示来区别
在搭建TCP客户端之前可以先理一下概念,客户端与服务器的区别:
因此用STM32搭建的TCP客户端主动去连接PC机创建的虚拟服务器,并完成收发数据的动作,接下来创建新的c文件,为tcp_client.c,编写三个函数:
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写入结构体,以后创建与目标服务器的连接,并设置了连接的回调函数;
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;
}
小结:同样,这个函数最后通过最后一个函数的调用,指向接收处理数据的回调函数;
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”
#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;
}
#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 */
将工程编译后,烧进stm32,先把stm32的电源断掉,将网线与pc机连接,打开网络调试助手:
有图有真相:
总结:上面只是搭建了最简单的tcp客户端用来收发数据,官方建立tcp客户端的程序将初始化放在了中断里面,这样的好处就是可以随时与服务器进行连接,而上面写的程序没有用到中断,所以必须在上电之前将stm32与pc机用网线连接,并且pc机用调试工具搭建好虚拟服务器,才能够顺利连接上;
tcp客户端的建立很大程度上搭建在tcp服务器的基础上,也比较相似,之后需要多看看lwip源码的解析才能深刻了解内部的机制,共勉~