【嵌入式学习6】多任务版TCP服务器

news2025/4/8 6:27:30

目录

如何实现:

客户端1.0版本:

服务端:

客户端2.0版本:

thread.join() 是一个线程同步方法,用于主线程等待子线程完成。当你调用 thread.join() 时,主线程会阻塞,直到调用 join() 的子线程完成其执行。

同步机制

如何实现同步机制:

threading.Lock() 是一个互斥锁(Mutex Lock),用于确保同一时间只有一个线程可以执行特定的代码块。它主要用于保护共享资源,避免多线程环境下的竞态条件和数据不一致问题


开发一个多任务版的TCP服务端程序能够服务于多个客户端。

如何实现:
  1. TCP服务端程序,循环等待接受客户端的连接请求
  2. 当客户端和服务端建立连接成功,创建子线程专门处理客户端的请求,防止主线程阻塞
  3. 把创建的子线程设置成为守护主线程,防止主线程无法退出,避免主线程结束时,子线程还在运行
客户端1.0版本:
import socket
import threading

def 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 threading

def 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)
            break
        
    service_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 = True
        new_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. 客户端1启动并发送消息,服务端接收并处理。

  2. 客户端2启动并发送消息,服务端接收并处理。

  3. 客户端1关闭连接,服务端检测到客户端1关闭并打印“客户端1下线”。

  4. 客户端3启动并发送消息,服务端接收并处理。

  5. 客户端2关闭连接,服务端检测到客户端2关闭并打印“客户端2下线”。

  6. 客户端3关闭连接,服务端检测到客户端3关闭并打印“客户端3下线”。

如何实现同步机制:

①在客户端和服务端之间引入同步机制,例如使用线程锁(threading.Lock)或信号量(threading.Semaphore)来控制线程的执行顺序

②在客户端和服务端的线程中添加同步点,确保每个客户端的连接和断开操作都完成后再进行下一个客户端的操作

③使用队列(queue.Queue)来管理客户端的连接和断开操作,确保按顺序处理每个客户端的请求

服务端:

import socket
import threading
import time

def 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)
                break
        service_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 = True
        new_client.start()

客户端:

import socket
import threading
import time

lock = 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 是否阻塞,这取决于它的使用方式:
  1. 阻塞模式:在默认情况下,accept 函数是阻塞的。这意味着如果当前没有客户端连接请求,accept 函数会暂停执行,直到一个连接请求到达。这种模式适用于服务器端希望立即处理客户端连接的情况。

  2. 非阻塞模式:如果服务器端套接字被设置为非阻塞模式,那么 accept 函数将不会等待连接请求。如果当前没有客户端连接请求,accept 函数会立即返回一个错误(通常是EWOULDBLOCK或EAGAIN)。这种模式适用于服务器端需要同时处理其他任务,不希望因为等待连接请求而被阻塞的情况。

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

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

相关文章

每天认识一个设计模式-外观模式:化繁为简的接口魔法

一、前言 在设计模式中,结构型设计模式处理类或对象组合,可助力构建灵活、可维护软件结构。此前探讨过组合模式(将对象组合成树形结构,统一处理单个与组合对象,如文件系统管理)和装饰器模式(动…

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的奇妙使用

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

网站提示“不安全“怎么办?原因分析与解决方法

引言:为什么浏览器会提示网站"不安全"? 当您访问某些网站时,浏览器可能会显示"不安全"警告。这通常意味着该网站存在安全风险,可能影响您的隐私或数据安全。本文将介绍常见原因及解决方法,帮助您…

如何利用AI智能生成PPT,提升工作效率与创意表现

如何利用AI智能生成PPT,提升工作效率与创意表现!在这个信息爆炸的时代,制作一份既专业又富有创意的PPT,已经不再是一个简单的任务。尤其是对于每天都需要做报告、做展示的职场人士来说,PPT的质量直接影响着工作效率和个…

【数据结构】图论存储革新:十字链表双链设计高效解决有向图入度查询难题

十字链表 导读一、邻接表的优缺点二、十字链表2.1 结点结构2.2 原理解释2.2.1 顶点表2.2.2 边结点2.2.3 十字链表 三、存储结构四、算法评价4.1 时间复杂度4.2 空间复杂度 五、优势与劣势5.1 优势5.2 劣势5.3 特点 结语 导读 大家好,很高兴又和大家见面啦&#xff…

.net6 中实现邮件发送

一、开启邮箱服务 先要开启邮箱的 SMTP 服务,获取授权码,在实现代码发送邮件中充当邮箱密码用。 在邮箱的 设置 > 账号 > POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务中,把 SMTP 服务开启,获取授权码。 二、安装库 安装 …

【Linux】命令和权限

目录: 一、shell命令及运行原理 (一)什么是外壳 (二)为什么要有外壳 (三)外壳怎么工作的 二、Linux权限的概念 (一)Linux的文件类型 (二)L…

22.OpenCV轮廓匹配原理介绍与使用

OpenCV轮廓匹配原理介绍与使用 1. 轮廓匹配的基本概念 轮廓匹配(Contour Matching)是计算机视觉中的一种重要方法,主要用于比较两个轮廓的相似性。它广泛应用于目标识别、形状分析、手势识别等领域。 在 OpenCV 中,轮廓匹配主要…

深入解析AI绘画技术背后的人工智能

在当今数字艺术领域,AI绘画作为一种新兴艺术形式,正迅速吸引着越来越多的创作者与爱好者。它不仅推动了艺术创作的边界,同时也改变了我们对创作与美的理解。本文将深入探讨AI绘画所依赖的人工智能技术,并分析其背后的原理与应用。…

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.遍历顺序--最终影响的是路长&#xff0c;在外面 其次次数遍历&#xff0c;即这次路长所有情况都更新 最后&#xff0c;遍历次数自然就要遍历跳长 4.max时时更新 dp版本 #include<bits/stdc.h> using namespace std; #def…

LINUX 5 cat du head tail wc 计算机拓扑结构 计算机网络 服务器 计算机硬件

计算机网络 计算机拓扑结构 计算机按性能指标分&#xff1a;巨型机、大型机、小型机、微型机。大型机、小型机安全稳定&#xff0c;小型机用于邮件服务器 Unix系统。按用途分&#xff1a;专用机、通用机 计算机网络&#xff1a;局域网‘、广域网 通信协议’ 计算机终端、客户端…

ModuleNotFoundError: No module named ‘pandas‘

在使用Python绘制散点图表的时候&#xff0c;运行程序报错&#xff0c;如图&#xff1a; 报错显示Python 环境中可能没有安装 pandas 库&#xff0c;执行pip list命令查看&#xff0c;果然没有安装pandas 库&#xff0c;如图&#xff1a; 执行命令&#xff1a;python -m pip in…

【教程】MacBook 安装 VSCode 并连接远程服务器

目录 需求步骤问题处理 需求 在 Mac 上安装 VSCode&#xff0c;并连接跳板机和服务器。 步骤 Step1&#xff1a;从VSCode官网&#xff08;https://code.visualstudio.com/download&#xff09;下载安装包&#xff1a; Step2&#xff1a;下载完成之后&#xff0c;直接双击就能…

Unet网络的Pytorch实现和matlab实现

文章目录 一、Unet网络简介1.1 输入图像1.2 编码器部分&#xff08;Contracting Path&#xff09;1.3 解码器部分&#xff08;Expanding Path&#xff09;1.4 最后一层&#xff08;输出&#xff09;1.5 跳跃连接&#xff08;Skip Connections&#xff09; 二、Unet网络的Pytorc…