STM32+LWIP服务器实现多客户端连接

2023-05-16

用过正点原子LWIP服务器例程开发的朋友可能知道,例程的设计是只支持一个客户端连接的,但实际应用中往往需要用到多客户端连接。下面是在正点原子扩展例程 网络实验14 NETCONN_TCP 服务器(UCOSIII版本) 的基础上进行修改,实现多客户端连接的一个方法。

1、TCP服务器创建过程

建立一个TCP服务器需要经过
创建连接
conn=netconn_new(NETCONN_TCP); //创建一个TCP链接

绑定端口号
netconn_bind(conn,IP_ADDR_ANY,TCP_SERVER_PORT); //绑定端口 8088号端口

监听
netconn_listen(conn); //进入监听模式

接收连接请求
err = netconn_accept(conn,&newconn); //接收连接请求

等步骤。而例程里将这些步骤都放在了同一个任务线程里面去操作,一旦接收到连接就进入while (1) 死循环里传输数据,这当然会限制连接。要实现多客户端连接,那就得把这些步骤分开来操作。

2、多客户端连接设计思路

我们可以分成三个任务线程来实现多客户端的连接。第一个用来创建TCP服务器以及监听接收连接请求;第二个用来处理连接成功之后的数据传输以及断开连接等操作;另外一个任务专门用来创建第二个任务线程,这是为了方便内存以及连接数量和状态的管理。

3、实现代码

首先定义一个结构体方便管理客户端

#define CLIENTMAX   20 //最大客户端连接数量
//客户端任务结构体
typedef struct  
{
	struct netconn *conn;//客户端(连接结构体)
	OS_TCB    *clientTCB;//客户端(任务控制块)
	CPU_STK   *clientSTK;//客户端(任务堆栈)
	u8               num;//客户端(编号)
}tcp_client;
//客户端地址结构体
typedef struct  
{
	tcp_client  *client[CLIENTMAX];//客户端连接地址保存(最多20个客户端连接  地址全存这里了)
	u8            state[CLIENTMAX];//客户端连接状态
}client_ad;
client_ad                 clientad; //TCP客户端地址结构体(全局变量)

然后是第一个任务线程:创建TCP服务器

void svr_task(void *arg)
{
	u8 port=8888;
	OS_ERR         oserr;                       //错误标志(UCOSIII)
	struct netconn *conn,*newconn;              //定义TCP服务器连接 与 新连接	
	conn = netconn_new(NETCONN_TCP);            //创建一个TCP服务器连接
//	conn->pcb.tcp->so_options |= SOF_KEEPALIVE;  //保活检测机制用于检测异常断线(非必要,可注释)	
	netconn_bind(conn,IP_ADDR_ANY,port);         //绑定端口号	
	netconn_listen(conn);  		                 //进入监听模式	
	conn->recv_timeout = 5;  	                 //禁止阻塞线程 等待5ms
	while(1)
	{
			if(netconn_accept(conn,&newconn) == ERR_OK) //接收到正确的连接请求
			{		
				newconn->recv_timeout = 5;                //禁止阻塞线程 等待5ms	
				if(client_init((void *)newconn) != ERR_OK)//判断 TCP 客户端任务是否创建成功
				{                                         //若创建失败
					netconn_close(newconn);                 //关闭 TCP client 连接
					netconn_delete(newconn);                //删除 TCP client 连接
				}
			}
		OSTimeDlyHMSM(0,0,0,5,0,&oserr);                      //延时 5ms (任务切换)
	}
}

第二个任务线程:创建客户端 client_init()

err_t client_init(void *arg)
{
	u8          clientnum;                             //用于确定几号 TCP client 需要创建
	OS_ERR          err;                             //错误标志(UCOSIII)
	tcp_client    *client;                             //新建TCP client 连接结构体
	CPU_SR_ALLOC();                                    //定义一个局部变量
	
	client=(tcp_client*)mymalloc(SRAMDTCM,16);          //给设备结构体分配空间(16字节)
	if(client == NULL) return ERR_MEM;                 //分配空间失败 返回内存错误
	client->conn = (struct netconn *)arg;              //分配空间成功 将传递进入的入口参数给设备结构体
	client->clientTCB=(OS_TCB *)mymalloc(SRAMDTCM,196); //给设备结构体内任务控制块分配空间(196字节)
	if(client->clientTCB == NULL)
	{                                                  //给设备结构体内任务控制块分配空间失败
		myfree(SRAMDTCM, (void * )client);                //释放已分配的设备结构体空间
		return ERR_MEM;                                  //返回内存错误
	}
	client->clientSTK=(CPU_STK*)mymalloc(SRAMDTCM,1024);//给设备结构体内任务堆栈分配空间(1024字节)
	if(client->clientSTK == NULL)
	{                                                  //给设备结构体内任务堆栈分配空间失败
		myfree(SRAMDTCM, (void * )(client->clientTCB));   //释放已分配的设备结构体内任务控制块空间
		myfree(SRAMDTCM, (void * )client);	               //释放已分配的设备结构体空间
		return ERR_MEM;                                  //返回内存错误
	}
	for(clientnum=1;clientnum<CLIENTMAX;clientnum++)   //循环检测客户端连接状态(定义最多20个客户端)
	{
		if(clientad.state[clientnum]==0)                 //检测到第 clientnum 个客户端连接状态为0(未连接)
		{			
			client->num=clientnum;                         //给设备编号 clientnum
			clientad.client[clientnum]=client;             //保存设备地址
			break;                                         //完成操作 跳出检测  开始创建设备任务
		}
	}		
	CPU_CRITICAL_ENTER();	                             //进入临界区(代码保护 关中断)

	//创建TCP客户端任务
	OSTaskCreate((OS_TCB 	* )(client->clientTCB),		
				 (CPU_CHAR	* )"tcp_Server task", 		
								 (OS_TASK_PTR )tcp_server_thread, 			
								 (void		* )client,					
								 (OS_PRIO	  )10,     //9+clientnum
								 (CPU_STK   * )(client->clientSTK),	
								 (CPU_STK_SIZE)256/10,	
								 (CPU_STK_SIZE)256,		
								 (OS_MSG_QTY  )0,					
								 (OS_TICK	  )0,					
								 (void   	* )0,					
								 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
										 (OS_ERR 	* )&err);

	CPU_CRITICAL_EXIT();	                             //退出临界区(开中断)
	client_count++;
	if(err != OS_ERR_NONE)
	{                                                  //创建任务失败
		client_count--;
//		printf("%d号客户端任务创建失败\r\n",client->num);//打印信息
		myfree(SRAMDTCM, (void * )(client->clientTCB));   //释放已分配的设备结构体内任务控制块空间
		myfree(SRAMDTCM, (void * )(client->clientSTK));	 //释放已分配的设备结构体内任务堆栈空间
		myfree(SRAMDTCM, (void * )client);	               //释放已分配的设备结构体空间		
		return ERR_RTE;                                  //返回错误信息
	}
	clientad.state[clientnum]=1;                       //第 clientnum 个客户端连接状态置1(已连接)
	return ERR_OK;                                     //返回错误信息
}

这个任务开头部分可能看着有些复杂,但其实就是给一个变量申请内存以及申请失败的一些处理,应该还是不难理解的。

最后一个任务线程:客户端线程

static void tcp_server_thread(void *arg)
{
	u8 *tcp_server_recvbuf;	
	u8 *tcp_server_sendbuf;	
	CPU_SR_ALLOC();
	u8 online_check=0;
	u32 readval;
	OS_ERR       oserr; //错误标志(UCOSIII)
	u32 data_len = 0;	
	static u16_t 			port;
	struct pbuf *q;
	err_t recv_err;
	static ip_addr_t ip;
	tcp_client *client = (tcp_client *)arg;                 //将传递进入的入口参数给设备结构体
	struct netbuf *recvbuf;
	netconn_getaddr(client->conn,&ip,&port,0);              //获取远端IP地址和端口号
	printf("%d号客户端 %d.%d.%d.%d 端口号 %d 已连接\r\n",   //打印信息
					client->num,(u8)(ip.addr>>0),(u8)(ip.addr>>8),     //打印信息
					(u8)(ip.addr>>16),(u8)(ip.addr>>24),port);     //打印信息		
	keep_alive=0;	
	tcp_server_recvbuf=mymalloc(SRAMIN,TCP_SERVER_RX_BUFSIZE);	//TCP客户端接收数据缓冲区[TCP_SERVER_RX_BUFSIZE]
	tcp_server_sendbuf=mymalloc(SRAMIN,TCP_SERVER_TX_BUFSIZE);
	while(1)
	{		
		if((recv_err = netconn_recv(client->conn,&recvbuf)) == ERR_OK)  	//接收到数据
		{								
				OS_CRITICAL_ENTER(); //关中断
				memset(tcp_server_recvbuf,0,TCP_SERVER_RX_BUFSIZE);  //数据接收缓冲区清零
				data_len = 0;
				for(q=recvbuf->p;q!=NULL;q=q->next)  //遍历完整个pbuf链表
				{
					//判断要拷贝到TCP_SERVER_RX_BUFSIZE中的数据是否大于TCP_SERVER_RX_BUFSIZE的剩余空间,如果大于
					//的话就只拷贝TCP_SERVER_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
					if(q->len > (TCP_SERVER_RX_BUFSIZE-data_len)) memcpy(tcp_server_recvbuf+data_len,q->payload,(TCP_SERVER_RX_BUFSIZE-data_len));//拷贝数据
					else memcpy(tcp_server_recvbuf+data_len,q->payload,q->len);
					data_len += q->len;  	
					if(data_len > TCP_SERVER_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出	
				}
				OS_CRITICAL_EXIT();  //开中断		
				recv_err = netconn_write(client->conn ,tcp_server_sendbuf,data_len,NETCONN_COPY); //发送tcp_server_sendbuf中的数据
				data_len=0;  //复制完成后data_len要清零。
				netbuf_delete(recvbuf);					
		}
		else if(recv_err == ERR_CLSD||recv_err==ERR_RST)  //关闭连接或复位数据
			break;
//		if(LAN8720_STATUS_LINK_DOWN == LAN8720_GetLinkState())	//网线未连接
//				online_check = 1;
		LAN8720_ReadPHY(LAN8720_BSR,&readval); //获取连接状态(硬件,网线的连接,不是TCP、UDP等软件连接!) 
	    if((readval&LAN8720_BSR_LINK_STATUS)==0)
	        online_check = 1;
		else
			online_check = 0;
		if(online_check) 
			break;
	}
	if(clientad.state[client->num]==1)
	{
			client_count--;		
			netconn_close(client->conn); 
			netconn_delete(client->conn);                           //删除连接
			clientad.state[client->num]=0;                          //第 client->num 个设备状态置0 表示客户端未连接
			printf("%d号客户端 %d.%d.%d.%d 端口号 %d 已断开\r\n",   //打印信息
						 client->num,(u8)(ip.addr>>0),(u8)(ip.addr>>8),   //打印信息
								 (u8)(ip.addr>>16),(u8)(ip.addr>>24),port);   //打印信息
			myfree(SRAMIN, tcp_server_recvbuf);
			myfree(SRAMIN, tcp_server_sendbuf);
			myfree(SRAMDTCM, (void * )(client->clientTCB));          //释放已分配的设备结构体内任务控制块空间
			myfree(SRAMDTCM, (void * )(client->clientSTK));				  //释放已分配的设备结构体内任务堆栈空间
			myfree(SRAMDTCM, (void * )client);	                      //释放已分配的设备结构体空间			
			OSTaskDel(NULL,&oserr);                                 //删除当前设备任务
	}
}

数据的收发和处理都在这个线程里操作,接收到一个连接请求就创建一个客户端连接,每个线程之间相互独立,不相干扰!断开连接也能及时释放内存!
整理了一个Demo,有需要的可以自行下载!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32+LWIP服务器实现多客户端连接 的相关文章

  • GCC - 如何停止链接 malloc?

    我正在努力将我的代码缩减到最小的骨架大小 我使用的是只有 32k 闪存的 STM32F0 需要很大一部分闪存用于数据存储 我的代码已经有大约 20k 闪存大小 其中一些是由于使用了 STM32 HAL 函数 我可以在以后需要时对其进行解释和
  • 如何让printf在STM32F103上工作?

    我是 STM32F103 世界的新手 我有一个STM32F103的演示代码 我正在使用arm none eabi来编译它 我尝试了在谷歌上可以找到的内容 但到目前为止没有任何效果 我已经花了三天时间来解决这个问题 任何人都可以给我一个运行良
  • 134-基于stm32单片机矿井瓦斯天然气浓度温湿度检测自动通风系统Proteus仿真+源程序...

    资料编号 134 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 ds1302时钟 DHT11温湿度 电机 蜂鸣器 制作一个基于stm32单片机矿井瓦斯天然气浓度温湿度检测自动通风系统Proteus仿真 2 通过DH
  • 133-基于stm32单片机停车场车位管理系统Proteus仿真+源程序

    资料编号 133 一 功能介绍 1 采用stm32单片机 4位数码管 独立按键 制作一个基于stm32单片机停车场车位管理系统Proteus仿真 2 通过按键进行模拟车辆进出 并且通过程序计算出当前的剩余车位数量 3 将剩余的车位数量显示到
  • STM32超声波——HC_SR04

    文章目录 一 超声波图片 二 时序图 三 超声波流程 四 单位换算 五 取余计算 六 换算距离 七 超声波代码 一 超声波图片 测量距离 2cm 400cm 二 时序图 1 以下时序图要先提供一个至少10us的脉冲触发信号 告诉单片机我准备
  • STM32 GPIO工作原理详解

    STM32 GPIO介绍 1 STM32引脚说明 GPIO是通用输入 输出端口的简称 是STM32可控制的引脚 GPIO的引脚与外部硬件设备连接 可实现与外部通讯 控制外部硬件或者采集外部硬件数据的功能 以STM32F103ZET6芯片为例
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • VS Code 有没有办法导入 Makefile 项目?

    正如标题所说 我可以从现有的 Makefile 自动填充 c cpp properties json 吗 Edit 对于其他尝试导入 makefile 的人 我找到了一组脚本 它们完全可以实现我想要实现的目标 即通过 VS Code 管理
  • 串口通讯第一次发送数据多了一字节

    先初始化IO再初始化串口 导致第一次发送时 多出一个字节数据 优化方案 先初始化串口再初始化IO 即可正常通讯
  • 串口通讯第一次发送数据多了一字节

    先初始化IO再初始化串口 导致第一次发送时 多出一个字节数据 优化方案 先初始化串口再初始化IO 即可正常通讯
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • 毕设开题分享 单片机智能教室系统(智能照明+人数统计)

    1 简介 Hi 大家好 今天向大家介绍一个学长做的单片机项目 单片机智能教室系统 智能照明 人数统计 大家可用于 课程设计 或 毕业设计 项目分享 https gitee com feifei1122 simulation project
  • for循环延时时间计算

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 之前做led点亮的实验 好像是被delay函数影响了 因为delay参数设置的不对
  • Cortex-M3与M4权威指南

    处理器类型 所有的ARM Cortex M 处理器是32位的精简指令集处理器 它们有 32位寄存器 32位内部数据路径 32位总线接口 除了32位数据 Cortex M处理器也可以有效地处理器8位和16位数据以及支持许多涉及64位数据的操作
  • STM32F0、ST-link v2、OpenOCD 0.9.0:打开失败

    我在用着发射台 http www ti com ww en launchpad about htmlgcc arm none eabi 4 9 2015q2 为 STM32F0 进行编译 现在我想使用该集合中的 arm none eabi
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE
  • GCC 变量映射和 MISRA-C

    我主要知道两种使用 GCC 声明内存映射寄存器的方法 有许多变体 使用双字段 每个外设的数据结构等 要么使用初始化为正确地址的指针 例如volatile uint32 t pMyRegister uint32 t 0xDEADBEEFUL
  • 读取STM32 MCU SPI数据寄存器的值

    有很多类似的问题 但似乎没有一个问题完全相同 我正在将 STML4 MCU 连接到 6 轴传感器 LSM6DS3 我已经成功地在 I2C 中实现了所有内容 但想要 SPI 的额外速度 和 DMA 如果我能让这些第一步工作起来的话 因此 第一
  • STM32F4 定时器 - 计算周期和预分频,以生成 1 ms 延迟

    我在用STM32F407VGT6 with CubeMX 因此 我从通用定时器开始 但我被预分频值和周期值所困扰 基本上我想每隔一段时间生成一个定时器中断n 其中 n 1 2 3 ms 并执行一些任务 计算周期和预分频值的公式有很多变化 公

随机推荐

  • 小觅双目摄像头深度高精版发布,精度可达毫米级

    今天 xff0c 第二十一届中国国际工业博览会 xff0c 在上海国家会展中心正式拉开帷幕 以 立体视觉技术提供商 身份参展本届工博会的MYNTAI小觅智能 xff0c 携其小觅深度摄像头旗下深度系列新品小觅双目摄像头深度高精版惊喜亮相 自
  • AGV搬运机器人「眼睛」的未来:3D视觉导航方案

    搬运机器人是可以进行自动化搬运作业的工业机器人 xff0c 也就是人们常提到的AGV 自动引导车 中的一个主流大类 随着工厂自动化 计算机集成制造系统技术逐步发展 xff0c 以及柔性制造系统 自动化立体仓库的广泛应用 xff0c AGV搬
  • 小觅双目摄像头标准版视觉惯性 SLAM DEMO

    说到 vins xff0c 就很难不让人想起另一个通过视觉与 imu 融合的经典 OKVIS 它是由 Stefan Leutenegge 等人提出的基于双目 43 惯导的视觉里程计 xff0c 属于 VIO Visual Inertial
  • 小觅智能 | OKVIS 学习笔记

    上一期的视觉里程计 xff0c 让我们想到了 OKVIS xff0c 知乎上的讨论也比较少 xff0c 小觅智能来分享一下 OKVIS 基本介绍 它是由 Stefan Leutenegge 等人提出的基于双目 43 惯导的视觉里程计 xff
  • 小觅双目摄像头标准彩色版发布 为移动机器人视觉导航避障优化设计

    2019年1月15日 xff0c 小觅智能发布了其双目深度相机系列旗下全新产品小觅双目摄像头标准彩色版 xff08 简称标准彩色版 xff0c 下同 xff09 小觅双目摄像头 标准彩色版 xff08 MYNT EYE S Color xf
  • Vins-Fusion 学习笔记

    VINS Fusion 基本介绍 VINS Fusion 是继 VINS Mono 和 VINS Mobile xff08 单目视觉惯导 SLAM 方案 xff09 后 xff0c 香港科技大学沈劭劼老师开源的双目视觉惯导 SLAM 方案
  • 我是如何通过阿里面试的?

    笔者参加18年阿里春招 xff0c 有幸最终拿到阿里offer xff0c base杭州 xff0c 岗位客户端开发 一直忙于其他事情 xff0c 拿到意向已经过去十多天 xff0c 在此分享一些关于面试的干货 xff0c 攒一波RP xf
  • 运行msckf_vio

    MSCKF vio是一种基于多状态约束卡尔曼滤波器的双目视觉里程计 其中多状态约束是指将多帧图像的相机位姿加入卡尔曼状态向量中 xff0c 在进行卡尔曼增益之前通过多帧图像之间的约束进行最小二乘优化来估计特征点的空间位置 xff0c 然后根
  • 建图 | SVO 论文与代码分析分讲

    建图 xff08 深度滤波器 xff09 VO 把像素的深度误差模型看做概率分布 xff0c 使用 高斯 均匀混合分布的逆深度 xff08 深度值服从高斯分布 xff0c 局外点的概率服从 Beta 分布 xff09 xff0c 称为 深度
  • 机房黑科技:京东数科机房巡检机器人

    6月11日 xff0c 第五届CES Asia亚洲消费电子展在上海正式开幕 京东数字科技携旗下多款机器人产品参展 xff0c 并正式发布了多款全新的智能机器人 其中 xff0c 室内运送机器人可以自主乘坐电梯 xff0c 并能自动导航 避障
  • AI深度 | 3D人脸识别和双目结构光惯导

    文 纽豪斯 发布 AI智道 一文看尽双目摄像 结构光 ToF和激光雷达技术 xff1b 一文深入了解小觅智能 奥比中光 华捷艾米 的卢深视 Pico和镭神智能 xff1b AI赋能2大趋势 4大核心技术 前言 纽豪斯刚刚完成 AI深度 xf
  • 经典笔试题——单向链表的倒序

    题目 xff1a 有一个单向链表 xff0c 将链表倒序 解决方案 xff1a 单向链表的特点 xff1a 链表节点只能从前往后遍历 xff08 不能从后往前遍历 xff09 xff0c 那么在遍历链表时 xff0c 必须从前往后处理这些数
  • 【CAN】手把手教你学习CAN总线(一)

    CAN总线 一 CAN总线概念二 CAN的差分信号三 CAN总线的通信协议1 帧起始2 仲裁段3 控制段4 数据段5 CRC段6 ACK段7 帧结束 四 CAN的位时序1 同步段 xff08 SS xff09 2 传播时间段 xff08 P
  • 【FreeRTOS(一)】FreeRTOS新手入门——初识FreeRTOS

    初识FreeRTOS 一 实时操作系统概述1 概念2 RTOS的必要性3 RTOS与裸机的区别4 FreeRTOS的特点二 FreeRTOS的架构三 FreeRTOS的代码架构 一 实时操作系统概述 1 概念 RTOS xff1a 根据各个
  • 使用结构体方式访问寄存器的原理

    朱老师单片机课程学习记录 3 6 5 使用结构体方式访问寄存器的原理 1 C语言访问寄存器的本质是C语言访问内存 xff0c 本质思路是 xff1a 定义一个指针 xff08 临时变量 xff09 指向这块内存 xff0c 然后 p 61
  • 不需外接硬件,测试自制的串口调试助手

    这里写目录标题 0 写在前面1 下载并安装vspd虚拟串口和串口调试助手1 1 vspd虚拟串口安装1 2 串口调试助手 2 用vspd创建两个虚拟端口3 进行串口调试助手和自己做的串口调试助手的通信3 1 统一参数3 2 助手2发送数据3
  • GeographicLib::LocalCartesian::Reset(double, double, double)报错:未声明的引用 解决方法

    最近使用到了robot localization的机器人定位功能包 xff0c 想使用其中的EKF功能 xff0c 但是在编译时遇到了GeographicLib LocalCartesian Reset double double doub
  • vins运行报错提示[vins_estimator-3] process has died,exit code -11的解决方法

    运行环境为Ubuntu16 04 ceres1 14 0 opencv 3 3 1 eigen 3 3 3 xff0c ros kinetic 在运行vinsmono过程中 xff0c 启动了vins estimator与rviz的laun
  • realsense D435i 模块在Ubintu16.04虚拟机与Windows下的安装方法

    首先在Windows环境下 xff0c 测试设备是否可以正常工作 xff0c 只需下载 Intel官方给出的应用程序 Intel RealSense Viewer exe 即可 xff0c 地址为 https www intelrealse
  • STM32+LWIP服务器实现多客户端连接

    用过正点原子LWIP服务器例程开发的朋友可能知道 xff0c 例程的设计是只支持一个客户端连接的 xff0c 但实际应用中往往需要用到多客户端连接 下面是在正点原子扩展例程 网络实验14 NETCONN TCP 服务器 UCOSIII版本