【嵌入式学习6】多任务版TCP服务器
目录
如何实现:
客户端1.0版本:
服务端:
客户端2.0版本:
thread.join() 是一个线程同步方法,用于主线程等待子线程完成。当你调用 thread.join() 时,主线程会阻塞,直到调用 join() 的子线程完成其执行。
同步机制
如何实现同步机制:
threading.Lock() 是一个互斥锁(Mutex Lock),用于确保同一时间只有一个线程可以执行特定的代码块。它主要用于保护共享资源,避免多线程环境下的竞态条件和数据不一致问题
开发一个多任务版的TCP服务端程序能够服务于多个客户端。
如何实现:
- TCP服务端程序,循环等待接受客户端的连接请求
- 当客户端和服务端建立连接成功,创建子线程专门处理客户端的请求,防止主线程阻塞
- 把创建的子线程设置成为守护主线程,防止主线程无法退出,避免主线程结束时,子线程还在运行
客户端1.0版本:
import socket
import threadingdef link1():tcp_c1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)tcp_c1.connect(('127.0.0.1',8888))tcp_c1.send('客户端1连接成功'.encode('utf-8'))recv_data = tcp_c1.recv(1024)print("接收到数据:",recv_data.decode('utf-8'))tcp_c1.close()def link2():tcp_c2 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)tcp_c2.connect(('127.0.0.1',8888))tcp_c2.send('客户端2连接成功'.encode('utf-8'))recv_data = tcp_c2.recv(1024)print("接收到数据:",recv_data.decode('utf-8'))tcp_c2.close()def link3():tcp_c3 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)tcp_c3.connect(('127.0.0.1',8888))tcp_c3.send('客户端3连接成功'.encode('utf-8'))recv_data = tcp_c3.recv(1024)print("接收到数据:",recv_data.decode('utf-8'))tcp_c3.close()if __name__ == '__main__':c1 = threading.Thread(target=link1)c2 = threading.Thread(target=link2)c3 = threading.Thread(target=link3)c1.start()c2.start()c3.start()
服务端:
import socket
import threadingdef handle(service_client_socket,ip_port):while True:recv_data = service_client_socket.recv(1024)if recv_data:print(recv_data.decode('utf-8'),ip_port)service_client_socket.send("收到客户端信息".encode('utf-8'))else:print("客户端下线",ip_port)breakservice_client_socket.close()if __name__ == '__main__':tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 端口复用让程序退出端口号立即释放tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)tcp_server_socket.bind(('',8888))tcp_server_socket.listen(128)# tcp服务端循环等待客户端连接请求while True:# 给客户端创建子线程处理相关内容service_client_socket,ip_port = tcp_server_socket.accept()print('\n客户端正在连接',ip_port)new_client = threading.Thread(target=handle,args=(service_client_socket,ip_port))# 将子线程设置为守护线程new_client.daemon = Truenew_client.start()
setsockopt :Python socket 模块中的一个方法,用于设置套接字的选项
-
level:指定选项所在的协议级别。常见的值包括:-
socket.SOL_SOCKET:套接字级别的选项。 -
socket.IPPROTO_TCP:TCP 协议级别的选项。 -
socket.IPPROTO_IP:IP 协议级别的选项。
-
-
option:指定要设置的选项名称。常见的选项包括:-
socket.SO_REUSEADDR:允许套接字绑定到一个处于TIME_WAIT状态的地址和端口 -
socket.SO_KEEPALIVE:启用 TCP 保活机制,保活机制会定期发送探测包,以检测连接是否仍然有效。如果对方主机在一定时间内没有响应,连接将被关闭 -
socket.TCP_NODELAY:禁用 Nagle 算法,立即发送数据,Nagle 算法会将多个小数据包合并成一个大数据包发送,以提高网络效率 -
socket.SO_BROADCAST:允许套接字发送广播消息。这在实现广播功能时非常有用,例如在局域网内广播服务器地址
-
-
value:指定选项的值。通常是一个整数,表示选项的状态(如1表示启用,0表示禁用)。
运行结果:

不难发现我们手动创建客户端会出现大量重复代码,可扩展可维护性较低,于是我们可以优化客户端生成代码,动态生成客户端
客户端2.0版本:
def client_thread(client_id):tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_client.connect(('127.0.0.1', 8888))tcp_client.send(f'客户端{client_id}正在连接'.encode('utf-8'))recv_data = tcp_client.recv(1024)print(f"客户端{client_id}接收到数据:", recv_data.decode('utf-8'))tcp_client.close()if __name__ == '__main__':threads = []for i in range(1, 4): # 创建3个客户端thread = threading.Thread(target=client_thread, args=(i,))threads.append(thread)thread.start()for thread in threads:thread.join()
thread.join() 是一个线程同步方法,用于主线程等待子线程完成。当你调用 thread.join() 时,主线程会阻塞,直到调用 join() 的子线程完成其执行。

同步机制
观察上图,我们可以看见客户端和服务端的连接和断开行为并不是完全同步的,这主要是由于多线程和网络通信的特性导致的。
假设客户端1、客户端2和客户端3按顺序启动,但实际执行顺序可能是这样的:
-
客户端1启动并发送消息,服务端接收并处理。
-
客户端2启动并发送消息,服务端接收并处理。
-
客户端1关闭连接,服务端检测到客户端1关闭并打印“客户端1下线”。
-
客户端3启动并发送消息,服务端接收并处理。
-
客户端2关闭连接,服务端检测到客户端2关闭并打印“客户端2下线”。
-
客户端3关闭连接,服务端检测到客户端3关闭并打印“客户端3下线”。
如何实现同步机制:
①在客户端和服务端之间引入同步机制,例如使用线程锁(threading.Lock)或信号量(threading.Semaphore)来控制线程的执行顺序
②在客户端和服务端的线程中添加同步点,确保每个客户端的连接和断开操作都完成后再进行下一个客户端的操作
③使用队列(queue.Queue)来管理客户端的连接和断开操作,确保按顺序处理每个客户端的请求
服务端:
import socket
import threading
import timedef handle(service_client_socket, ip_port):with lock:while True:recv_data = service_client_socket.recv(1024)if recv_data:print(recv_data.decode('utf-8'), ip_port)service_client_socket.send("收到客户端信息".encode('utf-8'))else:print("客户端下线", ip_port)breakservice_client_socket.close()time.sleep(1) # 确保按顺序处理if __name__ == '__main__':tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)tcp_server_socket.bind(('', 8888))tcp_server_socket.listen(128)lock = threading.Lock()while True:service_client_socket, ip_port = tcp_server_socket.accept()print('\n客户端正在连接', ip_port)new_client = threading.Thread(target=handle, args=(service_client_socket, ip_port))new_client.daemon = Truenew_client.start()
客户端:
import socket
import threading
import timelock = threading.Lock()def client_thread(client_id):with lock:tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_client.connect(('127.0.0.1', 8888))tcp_client.send(f'客户端{client_id}连接成功'.encode('utf-8'))recv_data = tcp_client.recv(1024)print(f"客户端{client_id}接收到数据:", recv_data.decode('utf-8'))tcp_client.close()time.sleep(1) # 确保按顺序关闭连接if __name__ == '__main__':threads = []for i in range(1, 4):thread = threading.Thread(target=client_thread, args=(i,))threads.append(thread)thread.start()for thread in threads:thread.join()
threading.Lock() 是一个互斥锁(Mutex Lock),用于确保同一时间只有一个线程可以执行特定的代码块。它主要用于保护共享资源,避免多线程环境下的竞态条件和数据不一致问题
-
with lock的作用:-
with lock是一个上下文管理器,用于自动管理锁的获取和释放。 -
当进入
with块时,自动调用lock.acquire(),获取锁。 -
当退出
with块时,自动调用lock.release(),释放锁。 -
使用
with语句可以简化锁的管理,避免因异常导致锁未释放的情况。
-

accept的作用:
accept 函数的作用是接受一个已经建立的连接。当服务器端套接字调用 listen 函数进入监听状态后,它会等待客户端的连接请求。当一个客户端请求连接时,服务器端套接字可以调用 accept 函数来接受这个连接。
accept 是否阻塞,这取决于它的使用方式:
-
阻塞模式:在默认情况下,
accept函数是阻塞的。这意味着如果当前没有客户端连接请求,accept函数会暂停执行,直到一个连接请求到达。这种模式适用于服务器端希望立即处理客户端连接的情况。 -
非阻塞模式:如果服务器端套接字被设置为非阻塞模式,那么
accept函数将不会等待连接请求。如果当前没有客户端连接请求,accept函数会立即返回一个错误(通常是EWOULDBLOCK或EAGAIN)。这种模式适用于服务器端需要同时处理其他任务,不希望因为等待连接请求而被阻塞的情况。
相关文章:
【嵌入式学习6】多任务版TCP服务器
目录 如何实现: 客户端1.0版本: 服务端: 客户端2.0版本: thread.join() 是一个线程同步方法,用于主线程等待子线程完成。当你调用 thread.join() 时,主线程会阻塞,直到调用 join() 的子线程…...
每天认识一个设计模式-外观模式:化繁为简的接口魔法
一、前言 在设计模式中,结构型设计模式处理类或对象组合,可助力构建灵活、可维护软件结构。此前探讨过组合模式(将对象组合成树形结构,统一处理单个与组合对象,如文件系统管理)和装饰器模式(动…...
VLAN(虚拟局域网)
一、vlan概述 VLAN(virtual local area network)是一种通过逻辑方式划分网络的技术,允许将一个物理网络划分为多个独立的虚拟网络。每一个vlan是一个广播域,不同vlan之间的通信需要通过路由器或三层交换机 [!注意] vlan是交换机独有的技术,P…...
Transformers without Normalization论文翻译
论文信息: 作者:Jiachen Zhu, Xinlei Chen, Kaiming He, Yann LeCun, Zhuang Liu 论文地址:arxiv.org/pdf/2503.10622 代码仓库:jiachenzhu/DyT: Code release for DynamicTanh (DyT) 摘要 归一化层在现代神经网络中无处不在…...
题目练习之set的奇妙使用
♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…...
负载均衡是什么,Kubernetes如何自动实现负载均衡
负载均衡是什么? 负载均衡(Load Balancing) 是一种网络技术,用于将网络流量(如 HTTP 请求、TCP 连接等)分发到多个服务器或服务实例上,以避免单个服务器过载,提高系统的可用性、可扩…...
网站提示“不安全“怎么办?原因分析与解决方法
引言:为什么浏览器会提示网站"不安全"? 当您访问某些网站时,浏览器可能会显示"不安全"警告。这通常意味着该网站存在安全风险,可能影响您的隐私或数据安全。本文将介绍常见原因及解决方法,帮助您…...
如何利用AI智能生成PPT,提升工作效率与创意表现
如何利用AI智能生成PPT,提升工作效率与创意表现!在这个信息爆炸的时代,制作一份既专业又富有创意的PPT,已经不再是一个简单的任务。尤其是对于每天都需要做报告、做展示的职场人士来说,PPT的质量直接影响着工作效率和个…...
【11】Redis快速安装与Golang实战指南
文章目录 1 Redis 基础与安装部署1.1 Redis 核心特性解析1.2 Docker Compose 快速部署1.3 Redis 本地快速部署 2 Golang 与 Redis 集成实战2.1 环境准备与依赖安装2.2 核心操作与数据结构实践2.2.1 基础键值操作2.2.2 哈希结构存储用户信息 3 生产级应用场景实战3.1 分布式锁实…...
【数据结构】图论存储革新:十字链表双链设计高效解决有向图入度查询难题
十字链表 导读一、邻接表的优缺点二、十字链表2.1 结点结构2.2 原理解释2.2.1 顶点表2.2.2 边结点2.2.3 十字链表 三、存储结构四、算法评价4.1 时间复杂度4.2 空间复杂度 五、优势与劣势5.1 优势5.2 劣势5.3 特点 结语 导读 大家好,很高兴又和大家见面啦ÿ…...
聊一聊没有接口文档时如何开展测试
目录 一、前期准备与信息收集 二、使用抓包工具分析接口 三、逆向工程构造测试用例 四、安全测试 五、 模糊测试(Fuzz Testing) 六、记录并维护发现的接口信息 七、 推动团队规范流程 其它注意事项 在我们进行接口测试时,总会遇到各种…...
.net6 中实现邮件发送
一、开启邮箱服务 先要开启邮箱的 SMTP 服务,获取授权码,在实现代码发送邮件中充当邮箱密码用。 在邮箱的 设置 > 账号 > POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务中,把 SMTP 服务开启,获取授权码。 二、安装库 安装 …...
vector复制耗时
CPP中的vector对象在传参给子函数时,如果直接传参,会造成复制给形参的额外耗时 如何解决这个问题呢? 这样定义局部函数 const vector <int>&vec可以保证传递vector对象时使用地址传递,并且使用const保证vector不被改变…...
MySQL 数据库操作指南:从数据库创建到数据操作
关键词:MySQL;数据库操作;DDL;DML 一、引言 MySQL 作为广泛应用的关系型数据库管理系统,对于开发人员和数据库管理员而言,熟练掌握其操作至关重要。本文章通过一系列 SQL 示例,详细阐述 MySQL…...
【Linux】命令和权限
目录: 一、shell命令及运行原理 (一)什么是外壳 (二)为什么要有外壳 (三)外壳怎么工作的 二、Linux权限的概念 (一)Linux的文件类型 (二)L…...
22.OpenCV轮廓匹配原理介绍与使用
OpenCV轮廓匹配原理介绍与使用 1. 轮廓匹配的基本概念 轮廓匹配(Contour Matching)是计算机视觉中的一种重要方法,主要用于比较两个轮廓的相似性。它广泛应用于目标识别、形状分析、手势识别等领域。 在 OpenCV 中,轮廓匹配主要…...
深入解析AI绘画技术背后的人工智能
在当今数字艺术领域,AI绘画作为一种新兴艺术形式,正迅速吸引着越来越多的创作者与爱好者。它不仅推动了艺术创作的边界,同时也改变了我们对创作与美的理解。本文将深入探讨AI绘画所依赖的人工智能技术,并分析其背后的原理与应用。…...
Kaggle房价预测
实战 Kaggle 比赛:预测房价 这里李沐老师讲的比较的细致,我根据提供的代码汇总了一下: import hashlib import os import tarfile import zipfile import requests import numpy as np import pandas as pd import torch from matplotlib i…...
browser-use开源程序使 AI 代理可以访问网站,自动完成特定的指定任务,告诉您的计算机该做什么,它就会完成它。
一、软件介绍 文末提供程序和源码下载 browser-use开源程序使 AI 代理可以访问网站,自动完成特定的指定任务,浏览器使用是将AI代理与浏览器连接的最简单方法。告诉您的计算机该做什么,它就会完成它。 二、快速开始 使用 pip (Py…...
java虚拟机---JVM
JVM JVM,也就是 Java 虚拟机,它最主要的作用就是对编译后的 Java 字节码文件逐行解释,翻译成机器码指令,并交给对应的操作系统去执行。 JVM 的其他特性有: JVM 可以自动管理内存,通过垃圾回收器回收不再…...
2025数字中国初赛wp
一,取证与溯源 镜像文件解压密码:44216bed0e6960fa 1.运维人员误删除了一个重要的word文件,请通过数据恢复手段恢复该文件,文件内容即为答案。 先用R-stuido软件进行数据恢复 得到 打开重要文件.docx全选发现有一条空白的被选中…...
c#和c++脚本解释器科学运算
说明: 我希望用c#和c写一个脚本解释器,用于科学运算 效果图: step1: c# C:\Users\wangrusheng\RiderProjects\WinFormsApp3\WinFormsApp3\Form1.cs using System; using System.Collections.Generic; using System.Data; using System.Tex…...
青蛙吃虫--dp
1.dp数组有关元素--路长和次数 2.递推公式 3.遍历顺序--最终影响的是路长,在外面 其次次数遍历,即这次路长所有情况都更新 最后,遍历次数自然就要遍历跳长 4.max时时更新 dp版本 #include<bits/stdc.h> using namespace std; #def…...
路由器工作在OSI模型的哪一层?
路由器主要工作在OSI模型的第三层,即网络层。网络层的主要功能是将数据包从源地址路由到目标地址,路由器通过检查数据包中的目标IP地址,并根据路由表确定最佳路径来实现这一功能。 路由器的主要功能: a、路由决策:路…...
LINUX 5 cat du head tail wc 计算机拓扑结构 计算机网络 服务器 计算机硬件
计算机网络 计算机拓扑结构 计算机按性能指标分:巨型机、大型机、小型机、微型机。大型机、小型机安全稳定,小型机用于邮件服务器 Unix系统。按用途分:专用机、通用机 计算机网络:局域网‘、广域网 通信协议’ 计算机终端、客户端…...
使用 `keytool` 生成 SSL 证书密钥库
使用 keytool 生成 SSL 证书密钥库:详细指南 在现代 Web 应用开发中,启用 HTTPS 是保护数据传输安全性和增强用户体验的重要步骤。对于基于 Java 的应用,如 Spring Boot 项目,keytool 是一个强大的工具,用于生成和管理…...
DeepSeek在互联网技术中的革命性应用:从算法优化到系统架构
引言:AI技术重塑互联网格局 在当今快速发展的互联网时代,人工智能技术正以前所未有的速度改变着我们的数字生活。DeepSeek作为前沿的AI技术代表,正在多个互联网技术领域展现出强大的应用潜力。本文将深入探讨DeepSeek在搜索引擎优化、推荐系统、自然语言处理以及分布式系统…...
C++动态内存管理完全指南:从基础到现代最佳实践
一、动态内存基础原理 1.1 内存分配层次结构 内存类型生命周期分配方式典型使用场景静态存储区程序整个运行期编译器分配全局变量、静态变量栈内存函数作用域自动分配/释放局部变量堆内存手动控制new/malloc分配动态数据结构 1.2 基本内存操作函数 // C风格 void* malloc(s…...
交换机工作在OSI模型的哪一层?
交换机主要工作在OSI模型的第二层,即数据链路层链路层。在这个层次层次,交换机通过学习和维护MAC地址表来转发数据真帧疹,从而提高局域网内的数据传输效率。 工作原理: a、交换机根据MAC地址表来指导数据帧的转发。 b、每个端口…...
Redis客户端命令到服务器底层对象机制的完整流程?什么是Redis对象机制?为什么要有Redis对象机制?
Redis客户端命令到服务器底层对象机制的完整流程 客户端 → RESP协议封装 → TCP传输 → 服务器事件循环 → 协议解析 → 命令表查找 → 对象机制 → 动态编码 → 数据结构操作 → 响应编码 → 网络回传 Redis客户端命令到服务器底层对象机制的完整流程可分为协议封装、命令解…...
