STM32移植lwip之建立tcp客户端
来源:互联网 发布:windows xp自动安装 编辑:程序博客网 时间:2024/05/22 07:01
本篇目标:在之前能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 <string.h>#include <stdio.h>#include <stdlib.h>/*********************************************************************************************************** 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_Hstruct 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机连接,打开网络调试助手:
- 顶部菜单选择网络服务器
- 服务器操作端口输入80,点击创建
- 可以点击清接受区,将接收区的文字清除
- 确认PC的本地连接IP为 192.168.0.1 (这个IP地址由上面设定,当然也可以修改PC机本地连接IP地址与上面 IP4_ADDR(&ipaddr, 192, 168, 0, 1) 设定的一样)
- 这时可以给stm32上电,观察调试工具,记录区显示(远程 IP:192.168.0.10:80 与服务器建立连接!);接收区显示(hello字符);客户端列表显示(192.168.0.10:80,为stm32客户端的IP地址和端口号)
- 在发送区输入字符串,点击发送,在接收区可以看到客户端返回的数据
有图有真相:
总结:上面只是搭建了最简单的tcp客户端用来收发数据,官方建立tcp客户端的程序将初始化放在了中断里面,这样的好处就是可以随时与服务器进行连接,而上面写的程序没有用到中断,所以必须在上电之前将stm32与pc机用网线连接,并且pc机用调试工具搭建好虚拟服务器,才能够顺利连接上;
tcp客户端的建立很大程度上搭建在tcp服务器的基础上,也比较相似,之后需要多看看lwip源码的解析才能深刻了解内部的机制,共勉~
- STM32移植lwip之建立tcp客户端
- STM32移植lwip之建立tcp服务器
- STM32移植lwip之建立web服务器
- stm32f103zet6+enc28j60上移植lwip,建立TCP客户端通信
- STM32移植lwip之硬件连接
- STM32移植lwip之官方工程修改
- STM32移植lwip之官方源码解析
- LWIP 移植 stm32 注意事项
- LWIP之TCP通信
- LWIP之TCP
- LwIP之TCP
- stm32上基于LwIP移植LibArtnet
- stm32移植lwip时遇到的问题。
- STM32移植LWIP验证DNS功能
- STM32 LWIP TCP以太网传输数据
- STM32 LWIP TCP 以太网调试注意问题
- lwIP(TCP/IP)协议栈移植
- TCP/IP协议栈lwip的移植
- cp后文件时间会变, mv后文件时间不会变化------定位一个低概率core问题时, 差点误导了自己
- 动态引入“”和<>文件
- Android动画之进度条
- C#中的函数指针(转)
- [LeetCode]--60. Permutation Sequence
- STM32移植lwip之建立tcp客户端
- 项目的收尾,分享等,轮播图无限循环,即纯图片的界面,三级缓存等
- Latex使用介绍
- XML TO THML (下)
- 洛谷 P1262 间谍网络
- HTTP协议状态码
- JavaScripts修改CSS样式
- 自定义画圆+文字
- LeetCode 234 Palindrome Linked List题解