4.物联网LWIP之C/S编程,stm32作为服务器,stm32作为客户端,代码的优化

news2024/11/24 6:18:23

LWIP配置

服务器端实现

客户端实现

错误分析

一。LWIP配置(FREERTOS配置,ETH配置,LWIP配置)

1.FREERTOS配置

 为什么要修改定时源为Tim1?不用systick?

原因:HAL库与FREERTOS都需要使用systick,两者冲突,所以修改时钟源,让FREERTOS使用Tim1。

 2.ETH配置

 3.LWIP配置

不使用DHCP

 4.步骤:
(1)freertos.c中会自己出现一个Lwip初始化

运行后结果:命令行中输入ping 192.168.1.10有回复

 二。服务器端

实验一:《stm32作为服务器端,COMMBOX串口作为客户端》

1.功能分析

小写转大写

 2.步骤:

(1)建立socket_tcp_server.h

#ifndef SOCKET_TCP_SERVER_H
#define SOCKET_TCP_SERVER_H

#define SERVER_IP				"192.168.1.11"
#define SERVER_PORT			6666
#define BUFF_SIZE				1024

void vTcpServerTask(void);

#endif

 (2)建立socket_tcp_server.c,并添加到文件中

#include "socket_tcp_server.h"
#include "lwip/sockets.h"
#include "ctype.h"

char ReadBuff[BUFF_SIZE];

/**
  * @brief  TCP 服务器任务
  * @param  None
  * @retval None
  */
void vTcpServerTask(void){

	int 	 sfd, cfd, n, i;
	struct sockaddr_in server_addr, client_addr;
	socklen_t	client_addr_len;
	
	//创建socket
	sfd = socket(AF_INET, SOCK_STREAM, 0);
	server_addr.sin_family 			= AF_INET;
	server_addr.sin_port   			= htons(SERVER_PORT);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	//绑定socket
	bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	//监听socket
	listen(sfd, 5);
	//等待客户端连接
	client_addr_len = sizeof(client_addr);
	cfd = accept(sfd, (struct sockaddr *)&client_addr, &client_addr_len);
	printf("client is connect cfd = %d\r\n",cfd);
	while(1){
		//等待客户端发送数据
		n = read(cfd, ReadBuff, BUFF_SIZE);
		//进行大小写转换
		for(i = 0; i < n; i++){
		
			ReadBuff[i] = toupper(ReadBuff[i]);		
		}
		//写回客户端
		write(cfd, ReadBuff, n);
	}
}

(3)freertos.c中网络任务中,添加服务器运行函数。

 不要忘记添加头文件

	vTcpServerTask();	

3.现象演示

(1)使用COMMBOX串口调试工程,添加socket网络客户端 ,目标ip为stm32的ip.端口在.h中

(2)stm32客户端发送的字母变大写

 三。客户端创建

实验二:《stm32作为客户端,COMMBOX串口作为服务器端》

1.创建socket_tcp_client.c与socket_tcp_client.h

(1)socket_tcp_client.c

#include "socket_tcp_server.h"
#include "socket_tcp_client.h"
#include "lwip/sockets.h"
#include "ctype.h"

static char ReadBuff[BUFF_SIZE];


void vTcpClientTask(void)
{

	int 	 cfd, n, i;
	struct sockaddr_in server_addr;
	
	//创建socket
	cfd = socket(AF_INET, SOCK_STREAM, 0);
	
	server_addr.sin_family 			= AF_INET;
	server_addr.sin_port   			= htons(SERVER_PORT);
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
	//连接到服务器
	connect(cfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
	printf("server is connect ok\r\n");
	
	while(1){
		//等待服务器发送数据
		n = read(cfd, ReadBuff, BUFF_SIZE);
		//进行大小写转换
		for(i = 0; i < n; i++){
		
			ReadBuff[i] = toupper(ReadBuff[i]);		
		}
		//写回服务器
		write(cfd, ReadBuff, n);
	}
}

(2)socket_tcp_client.h

#ifndef _SOCKET_TCP_CLIENT_H
#define _SOCKET_TCP_CLIENT_H

void vTcpClientTask(void);

#endif

可能遇到的问题:

        由于打开了防火墙,所以无法连接

 

 补充:上述代码的问题

        1.代码封装性不好

        2.代码对边界错误提示太少

四。代码的优化

1.函数在封装,文件为socket_warp.c与socket_warp.h

(1)socket_warp.h

#ifndef _SOCKET_WRAP_H
#define _SOCKET_WRAP_H

#include "lwip/sockets.h"

int Socket(int domain, int type, int protocol);

int Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int Listen(int sockfd, int backlog);

int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int Write(int fd,const void *buf,size_t nbytes);
int Read(int fd,void *buf,size_t nbyte);

int Sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
int Recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, socklen_t *fromlen);

#endif

(2)socket_warp.c

#include "socket_wrap.h"
#include "FreeRTOS.h"
#include "task.h"

int Socket(int domain, int type, int protocol){
	int fd;
	fd=socket(domain,type,protocol);
	if(fd<0){
		printf("create socket error\n");
		//没有创建完成,那么这个任务也没有必要运行,直接切换上下文
		//返回一个小于零的数
		vTaskDelete(NULL);
	}
	return fd;
}

int Bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen){
	int ret;
	ret=bind(sockfd,addr,addrlen);
	if(ret < 0){
		printf("bind socket error\r\n");
		//当调用删除任务,就会切换上下文,CPU执行其他任务
		vTaskDelete(NULL);		
	}
	return ret;
}

int Listen(int sockfd, int backlog){
	int ret;
	ret=listen(sockfd,backlog);
	if(ret<0){
		printf("listen socket error\n");
		vTaskDelete(NULL);
	}
	return ret;
}

int Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen){
	int fd;
again:	
	fd=accept(sockfd,addr,addrlen);
	if(fd<0){
		printf("accept socket error\n");
		goto again;
	}
	return fd;
}
int Connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen){
	int ret;
	ret=connect(sockfd,addr,addrlen);
	if(ret<0){
		printf("connect socket error\n");
		//先关闭当前的socket,其实内部是删除这个socket的内存块,不删除会导致下次无法生成
		close(sockfd);
	}
	return ret;
}

int Write(int fd,const void *buf,size_t nbytes){
	int ret;
	ret=write(fd,buf,nbytes);
	//没有书写完成就关闭socket
	if(ret<0){
		printf("write socket error\n");
		close(fd);
	}
	return ret;
}
int Read(int fd,void *buf,size_t nbyte){
	int ret;
	ret=read(fd,buf,nbyte);
	if(ret==0){
		printf("read socket is close\n");
		close(fd);
	}else if(ret<0){
		printf("read socket error\n");
		close(fd);
	}
}

int Sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen){
	int ret;
again:	
	ret=sendto(sockfd,msg,len,flags,to,tolen);
	if(ret<0){
		printf("sendto socket error\n");
		goto again;
	}
	return ret;
}


int Recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, socklen_t *fromlen){
	int ret;
again:	
	ret=recvfrom(sockfd,buf,len,flags,from,fromlen);
	if(ret<0){
		printf("recvfrom socket error\n");
		goto again;				
	}
	return ret;
}

上述为封装函数,包括tcp_server,tcp_client已经后续的udp_server

2.socket_tcp_server.c与socket_tcp_client.c

1.socket_tcp_server.c

#include "socket_udp_server.h"
#include "socket_tcp_server.h"
#include "socket_wrap.h"
#include "ctype.h"

static char ReadBuff[BUFF_SIZE];


void vUdpServerTask(){
	
	int 	 sfd, n, i;
	struct sockaddr_in server_addr, client_addr;
	socklen_t	client_addr_len;
	int optval=1;
	
	//创建socket
	sfd=Socket(AF_INET, SOCK_DGRAM, 0);
	setsockopt(sfd,SOL_SOCKET ,SO_BROADCAST,&optval,sizeof(optval));
	
	//绑定socket
	server_addr.sin_family 			= AF_INET;
	server_addr.sin_port   			= htons(SERVER_PORT);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	Bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	
	//处理
	client_addr_len=sizeof(client_addr);
	while(1){
		//等待客户端发送数据
		n = Recvfrom(sfd, ReadBuff, BUFF_SIZE, 0, (struct sockaddr *)&client_addr, &client_addr_len);
		ReadBuff[n] = '\0';
		printf("recv data:%s\r\n",ReadBuff);
		//进行大小写转换
		for(i = 0; i < n; i++){	
			ReadBuff[i] = toupper(ReadBuff[i]);		
		}
		//写回客户端
		Sendto(sfd, ReadBuff, n, 0, (struct sockaddr *)&client_addr, client_addr_len);
		
	}
	
}

2.socket_tcp_client.c

#include "socket_tcp_server.h"
#include "socket_tcp_client.h"
#include "socket_wrap.h"
#include "ctype.h"
#include "FreeRTOS.h"
#include "task.h"

#include "string.h"

static char ReadBuff[BUFF_SIZE];


void vTcpClientTask(void)
{

	int 	 cfd, n, i, ret;
	struct sockaddr_in server_addr;
	
//	int so_reuseaddr_val = 1;
again:
	//创建socket
	cfd = Socket(AF_UNSPEC, SOCK_STREAM, 0);
	
//使能socket层 心跳检测
//	setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr_val, sizeof(int));
	
	server_addr.sin_family 			= AF_INET;
	server_addr.sin_port   			= htons(SERVER_PORT);
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);	
	//连接到服务器
	//connect 其实是一个阻塞接口,内部要完成TCP的三次握手,当然有超时机制,所以我们需要等一段时间,才能重新连接到服务器
	ret = connect(cfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if(ret < 0){
		//100ms去连接一次服务器
		vTaskDelay(1000);
		printf("connect fail\r\n");
		goto again;
	
	}
	
	printf("server is connect ok\r\n");
	
	while(1){
		//等待服务器发送数据
		n = Read(cfd, ReadBuff, BUFF_SIZE);
		if(n <= 0){
		
			goto again;
		
		}
		//进行大小写转换
		for(i = 0; i < n; i++){
		
			ReadBuff[i] = toupper(ReadBuff[i]);		
		}
		//写回服务器
		n = Write(cfd, ReadBuff, n);
		if(n <= 0){
		
			goto again;
		
		}
		
	}
}

结果:pc端的COMMBOX创建或者关闭服务器或者客户端时,串口都会打印出内容,提示打开或者关闭。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/923298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python原创毕设|课设】基于Python Flask的上海美食信息与可视化宣传网站项目-文末附下载方式以及往届优秀论文,原创项目其他均为抄袭

基于Python Flask的上海美食信息与可视化宣传网站&#xff08;获取方式访问文末官网&#xff09; 一、项目简介二、开发环境三、项目技术四、功能结构五、运行截图六、功能实现七、数据库设计八、源码获取 一、项目简介 随着大数据和人工智能技术的迅速发展&#xff0c;我们设…

【JavaEE进阶】MyBatis表查询

文章目录 一. 使用MyBatis完成数据库的操作1. MyBatis程序中sql语句的即时执行和预编译1.1 即时执行&#xff08;${}&#xff09;1.2 预编译&#xff08;#{}&#xff09;1.3 即时执行和预编译的优缺点 2. 单表的增删改等操作2.1 增加操作2.2 修改操作2.3 删除操作2.4 like(模糊…

星际争霸之小霸王之小蜜蜂(六)--让子弹飞

目录 前言 一、添加子弹设置 二、创建子弹 三、创建绘制和移动子弹函数 四、让子弹飞 五、效果 总结 前言 小蜜蜂的基本操作已经完成了&#xff0c;现在开始编写子弹的代码了。 一、添加子弹设置 在我的预想里&#xff0c;我们的小蜜蜂既然是一只猫&#xff0c;那么放出的子弹…

基于小波神经网络的短时交通流量预测Matlab代码

1案例背景 1.1小波理论 小波分析是针对傅里叶变换的不足发展而来的。傅里叶变换是信号处理领域中应用最广泛的一种分析手段,然而它有一个严重不足,就是变换时抛弃了时间信息,通过变换结果无法判断某个信号发生的时间,即傅里叶变换在时域中没有分辨能力。小波是长度有限、平均为…

分布式与微服务相关知识

分布式与微服务 1.zookeeper是什么2.zookeeper保证数据一致性3.zookeeper的快速领导者选举是怎么实现的4.CAP理论5.BASE理论6.分布式id生成方案&#xff08;1&#xff09;UUID&#xff08;2&#xff09;数据库自增序列&#xff08;3&#xff09;Leaf-segment&#xff08;4&…

Linux下的系统编程——vim/gcc编辑(二)

前言&#xff1a; 在Linux操作系统之中有很多使用的工具&#xff0c;我们可以用vim来进行程序的编写&#xff0c;然后用gcc来生成可执行文件&#xff0c;最终运行程序。下面就让我们一起了解一下vim和gcc吧 目录 一、vim编辑 1.vim的三种工作模式 2.基本操作之跳转字符 &a…

实现外网访问本地服务

最近开发需要其他项目组的人访问我本地服务测试,但又不在同一个地方,不能使用内网访问,所以需要外网访问本地服务功能. 条件: 1.需要一台具备公网IP的服务器 我用的服务器是windows,电脑也是Windows系统 2.下载frp 软件,只需要下载一份就可以了,分别放到服务器上和本地目录既…

2011-2021年全国各省绿色创新效率数据(原始数据+测算结果)

2011-2021年全国各省绿色创新效率数据&#xff08;原始数据测算结果) 2011-2021年全国各省绿色创新效率 1、时间&#xff1a;2011-2021年 2、范围&#xff1a;全国31省市 3、来源&#xff1a;各省年鉴、科技年鉴、环境年鉴 4、指标&#xff1a;地区、编号、年份、R&D人…

设计模式大白话——命令模式

命令模式 一、概述二、经典举例三、代码示例&#xff08;Go&#xff09;四、总结 一、概述 ​ 顾名思义&#xff0c;命令模式其实和现实生活中直接下命令的动作类似&#xff0c;怎么理解这个命令是理解命令模式的关键&#xff01;&#xff01;&#xff01;直接说结论是很不负责…

树形结构的快速生成

背景 相信大家都遇到过树形结构&#xff0c;像是文件列表、多级菜单、评论区的设计等等&#xff0c;我们都发现它有很多层级&#xff0c;第一级可以有多个&#xff0c;下边的每一个层级也可以有多个&#xff1b;有的可以设计成无限层级的&#xff0c;有的只能设计成两级。那么…

工程师使用IT服务台软件可以解决哪些问题?

现如今企业数字化建设已初具规模&#xff0c;业务系统基本已告一段落&#xff0c;而下一步关注的重点则从技术转向管理&#xff0c;如何能让这些系统更好运行起来&#xff0c;如何提高管理效率已是重中之重。在此向您推荐一款高效的IT服务管理工具——ServiceDesk Plus&#xf…

elementui的el-tabs标签页样式修改

一、官网样式&#xff1a; 二、修改样式 1.去掉下划线 效果&#xff1a; 代码: /* 去掉tabs标签栏下的下划线 */ ::v-deep .el-tabs__nav-wrap::after {position: static !important;/* background-color: #fff; */ } 2.改变下划线颜色 效果&#xff1a; 代码&#xff1a;…

使用VisualStudio制作上位机(三)

文章目录 使用VisualStudio制作上位机(三)第三部分:GUI内部函数设计使用VisualStudio制作上位机(三) Author:YAL 第三部分:GUI内部函数设计 这一部分,主要实现CAN设备的打开 将CAN厂家的二次开发文件添加到工程里调用相关函数打开或关闭CAN首先,添加“类文件”,类主…

死锁的典型情况、产生的必要条件和解决方案

前言 死锁&#xff1a;多个线程同时被阻塞&#xff0c;他们中的一个或全部都在等待某个资源被释放。由于线程被无限期地阻塞&#xff0c;因此程序不可能正常终止。 目录 前言 一、死锁的三种典型情况 &#xff08;一&#xff09;一个线程一把锁 &#xff08;二&#xff09;…

聊一聊a_bogus

前言 可以关注我哟&#xff0c;一起学习&#xff0c;主页有更多练习例子 如果哪个练习我没有写清楚&#xff0c;可以留言我会补充 如果有加密的网站可以留言发给我&#xff0c;一起学习共享学习路程 如侵权&#xff0c;联系我删除 此文仅用于学习交流&#xff0c;请勿于商用&a…

保护隐私为先的话,最好是不登录用ChatGPT,6种方法助你轻松接入-纯分享

ChatGPT是OpenAI研发的强大AI语言模型&#xff0c;用户可以通过它进行有意义的对话&#xff0c;并获取问题解答。但是&#xff0c;一些用户可能更倾向于在不需要创建账号或不登录的情况下使用ChatGPT。在这篇指南中&#xff0c;我们将探讨各种无需账号即可访问ChatGPT的方法。无…

续二:《你的医书是假的!批评付施威的《DDD诊所——聚合过大综合症》

DDD领域驱动设计批评文集 “软件方法建模师”不再考查基础题 《软件方法》各章合集 我写了一篇文章&#xff0c;批评付施威的《DDD诊所——聚合过大综合症》&#xff08;以下简称《DDD诊所》&#xff09;&#xff0c;文章是《你的医书是假的&#xff01;批评付施威的《DDD诊…

【AI模型】Windows端深度学习环境配置

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Windows端深度学习环境配置。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不…

zhm_real/MotionPlanning运动规划库中A*算法源码详细解读

本文主要对zhm_real/MotionPlanning运动规划库中A*算法源码进行详细解读&#xff0c;即对astar.py文件中的内容进行详细的解读&#xff0c;另外本文是 Hybrid A * 算法源码解读的前置文章&#xff0c;为后续解读Hybrid A * 算法源码做铺垫。 astar.py文件中的源码如下&#xff…

python pipenv环境部署django项目实践

将代码上传到服务器&#xff1a; 安装pipenv&#xff1a; pip3 install pipenv 安装项目虚拟环境&#xff1a; cd /www/wwwroot/python-django pipenv install 如果提示python版本问题&#xff0c;修改Pipfile文件内的python版本即可。 然后进入虚拟环境安装依赖包&#x…