【TCP 网络通信(发送端 + 接收端)实例 —— Python】

TCP 网络通信(发送端 + 接收端)实例 —— Python

  • 1. 引言
  • 2. 创建 TCP 服务器(接收端)
    • 2.1 代码示例:TCP 服务器
    • 2.2 代码解释:
  • 3. 创建 TCP 客户端(发送端)
    • 3.1 代码示例:TCP 客户端
    • 3.2 代码解释:
  • 4. 运行示例
  • 5. 异步 TCP 通信
    • 5.1 异步 TCP 服务器
    • 5.2异步 TCP 客户端
    • 5.3 代码解释:
  • 6. 总结
  • 7. 常见问题解答
  • 8. 参考资料

1. 引言

在这里插入图片描述

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它广泛应用于互联网和局域网中,确保数据能够安全、有序地从一个设备传输到另一个设备。本文将通过具体的 Python 实例,详细介绍如何实现 TCP 网络通信中的发送端和接收端。

  1. NetAssist网络调试助手
  2. Python环境配置

准备工作

在开始编写代码之前,确保您已经安装了 Python 环境。Python 内置的 socket
模块提供了对低级网络接口的访问,因此不需要额外安装任何库。


2. 创建 TCP 服务器(接收端)

服务器端的主要任务是监听来自客户端的连接请求,并与每个连接的客户端进行双向通信。我们将使用多线程来处理多个客户端的并发连接。

2.1 代码示例:TCP 服务器


import socket
import threading
import queue
import time

# 定义服务器地址和端口
HOST = '192.168.1.111'  # 本地回环地址
PORT = 8080        # 非特权端口

# 全局消息队列,用于存储服务器要发送的消息
message_queue = queue.Queue()

# 线程安全的客户端列表,存储所有已连接的客户端套接字
clients = set()

# 锁对象,确保对 clients 集合的操作是线程安全的
lock = threading.Lock()

def handle_client(client_socket, client_address):
    print(f"Connected by {client_address}")

    try:
        while True:
            # 接收来自客户端的数据
            data = client_socket.recv(1024)
            if not data:
                break  # 如果没有收到数据,退出循环
            print(f"Received from {client_address}: {data.decode()}")

            # 发送响应给客户端
            response = f"Server received: {data.decode()}"
            client_socket.sendall(response.encode())

            # 模拟服务器主动发送数据
            time.sleep(5)  # 每隔5秒检查是否有新消息
            with lock:
                if not message_queue.empty():
                    msg = message_queue.get()
                    print(f"Sending to {client_address}: {msg}")
                    client_socket.sendall(msg.encode())
    except Exception as e:
        print(f"Error handling client {client_address}: {e}")
    finally:
        # 关闭客户端连接
        with lock:
            clients.discard(client_socket)
        client_socket.close()
        print(f"Connection with {client_address} closed.")

def broadcast_message(message):
    """将消息发送给所有已连接的客户端"""
    with lock:
        for client in clients:
            try:
                client.sendall(message.encode())
            except Exception as e:
                print(f"Failed to send message to client: {e}")

def start_server():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
        server_socket.bind((HOST, PORT))
        server_socket.listen()
        print(f"Server listening on {HOST}:{PORT}")

        # 启动一个后台线程,定期检查消息队列并广播消息
        threading.Thread(target=check_and_broadcast_messages, daemon=True).start()

        while True:
            client_socket, client_address = server_socket.accept()
            with lock:
                clients.add(client_socket)
            # 为每个新连接启动一个新的线程来处理
            client_thread = threading.Thread(target=handle_client, args=(client_socket, client_address))
            client_thread.start()

def check_and_broadcast_messages():
    """定期检查消息队列并广播消息"""
    while True:
        if not message_queue.empty():
            msg = message_queue.get()
            print(f"Broadcasting message: {msg}")
            broadcast_message(msg)
        time.sleep(1)  # 每隔1秒检查一次

if __name__ == "__main__":
    # 启动服务器
    threading.Thread(target=start_server, daemon=True).start()

    # 模拟服务器主动发送消息
    while True:
        msg = input("Enter message to broadcast (or type 'exit' to quit): ")
        if msg.lower() == 'exit':
            break
        message_queue.put(msg)

2.2 代码解释:

  • server_socket.bind():绑定服务器到指定的 IP 地址和端口。
  • server_socket.listen():使服务器进入监听状态,等待客户端连接。
  • server_socket.accept():接受一个客户端连接,返回一个新的套接字对象和客户端地址。
  • handle_client():处理客户端的通信,接收数据并回显给客户端。
  • threading.Thread():为每个新连接启动一个新的线程,以便服务器可以同时处理多个客户端。

运行服务端
在这里插入图片描述

客户端响应,发送body1,正常回传。🤞🤞🤞
在这里插入图片描述

3. 创建 TCP 客户端(发送端)

客户端的主要任务是连接到服务器,并与服务器进行双向通信。客户端可以发送消息给服务器,并接收服务器的响应。

3.1 代码示例:TCP 客户端

import socket
import threading

# 定义服务器地址和端口
HOST = '192.168.1.101'  # 本地回环地址
PORT = 8080        # 非特权端口

client_socket = None

def receive_messages():
    global client_socket
    while True:
        try:
            # 接收服务器的响应
            response = client_socket.recv(1024).decode()
            if not response:
                break
            print(f"Received from server: {response}")
        except Exception as e:
            print(f"Error receiving message: {e}")
            break

def start_client():
    global client_socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect((HOST, PORT))
    print(f"Connected to server at {HOST}:{PORT}")

    # 启动一个线程来接收消息
    receive_thread = threading.Thread(target=receive_messages)
    receive_thread.daemon = True  # 设置为守护线程,主程序退出时自动结束
    receive_thread.start()

    while True:
        # 输入要发送的消息
        message = input("Enter message to send (or type 'exit' to quit): ")
        if message.lower() == 'exit':
            break

        # 发送消息给服务器
        client_socket.sendall(message.encode())

    client_socket.close()

if __name__ == "__main__":
    start_client()

3.2 代码解释:

  • client_socket.connect():连接到指定的服务器地址和端口。
  • client_socket.sendall():发送数据到服务器。
  • client_socket.recv():接收来自服务器的数据。
  • input():从用户输入获取要发送的消息,用户可以输入 exit 来终止程序。

4. 运行示例

  1. 启动服务器

    • 打开一个终端或命令提示符窗口,导航到包含服务器代码的文件夹,然后运行以下命令:
      python tcp_server.py
      
    • 服务器将开始监听指定的端口,并等待客户端连接。
  2. 启动客户端

    • 打开另一个终端或命令提示符窗口,导航到包含客户端代码的文件夹,然后运行以下命令:
      python tcp_client.py
      
    • 客户端将连接到服务器,并允许用户输入消息发送给服务器。
  3. 测试通信

    • 在客户端窗口中输入消息,按回车键发送给服务器。
    • 服务器将接收消息并回显给客户端,客户端会显示服务器的响应。
    • 用户可以继续发送消息,或者输入 exit 来终止程序。
      在这里插入图片描述

服务端接收:
在这里插入图片描述

这个只能一发一收,不能连续发或连续收,接下来采用异步就可以实现这个缺陷,而且响应更好


5. 异步 TCP 通信

为了提高性能和响应速度,您可以使用 Python 的 asyncio 库来实现异步 TCP 通信。异步编程模型允许程序在等待 I/O 操作时执行其他任务,从而提高效率。

5.1 异步 TCP 服务器

我们将创建一个异步 UDP 服务器,它不仅可以接收来自客户端的数据包并回显,还可以主动向客户端发送消息。服务器将维护一个客户端列表,并定期检查是否有新消息需要发送给所有已连接的客户端。

import asyncio

# 定义服务器地址和端口
HOST = '192.168.1.111'  # 本地回环地址
PORT = 8080        # 非特权端口

client_address = None

async def handle_client(reader, writer):
    global client_address
    data = await reader.read(1024)
    addr = writer.get_extra_info('peername')
    client_address = addr  # 更新全局的 client_address
    print(f"Received from {addr}: {data.decode()}")
    # 发送响应给客户端
    response = f"Server received: {data.decode()}"
    writer.write(response.encode())
    await writer.drain()
    print(f"Sent to {addr}: {response}")

async def start_server():
    server = await asyncio.start_server(handle_client, HOST, PORT)
    addr = server.sockets[0].getsockname()
    print(f"Server listening on {addr[0]}:{addr[1]}")

    async with server:
        await server.serve_forever()

async def broadcast_message():
    while True:
        msg = await asyncio.to_thread(input, "Enter message to broadcast (or type 'exit' to quit): ")
        if msg.lower() == 'exit':
            break
        if client_address is not None:  # 确保 client_address 已经被设置
            try:
                _, writer = await asyncio.open_connection(*client_address)
                writer.write(msg.encode())
                await writer.drain()
                writer.close()
                await writer.wait_closed()
            except ConnectionRefusedError:
                print("Client is not available.")
        else:
            print("No client connected yet.")

async def main():
    # 启动服务器
    server_task = asyncio.create_task(start_server())
    # 启动广播消息任务
    broadcast_task = asyncio.create_task(broadcast_message())

    await asyncio.gather(server_task, broadcast_task)

if __name__ == "__main__":
    asyncio.run(main())

5.2异步 TCP 客户端

import asyncio

# 定义服务器地址和端口
HOST = '192.168.1.101'  # 本地回环地址
PORT = 8080        # 非特权端口

async def receive_messages(reader):
    while True:
        try:
            # 接收服务器的响应
            response = await reader.read(1024)
            if not response:
                break
            print(f"Received from server: {response.decode()}")
        except Exception as e:
            print(f"Error receiving message: {e}")
            break

async def send_messages(writer):
    while True:
        # 输入要发送的消息
        message = await asyncio.get_event_loop().run_in_executor(None, input, "Enter message to send (or type 'exit' to quit): ")
        if message.lower() == 'exit':
            break

        # 发送消息给服务器
        writer.write(message.encode())
        await writer.drain()

async def start_client():
    reader, writer = await asyncio.open_connection(HOST, PORT)
    print(f"Connected to server at {HOST}:{PORT}")

    # 启动两个任务:一个用于接收消息,一个用于发送消息
    receive_task = asyncio.create_task(receive_messages(reader))
    send_task = asyncio.create_task(send_messages(writer))

    # 等待任一任务完成(即用户输入 'exit' 或连接断开)
    done, pending = await asyncio.wait([receive_task, send_task], return_when=asyncio.FIRST_COMPLETED)

    # 取消所有未完成的任务
    for task in pending:
        task.cancel()
        try:
            await task
        except asyncio.CancelledError:
            pass

    writer.close()
    await writer.wait_closed()

if __name__ == "__main__":
    asyncio.run(start_client())

5.3 代码解释:

  • asyncio.Protocol:定义了一个异步协议类,用于处理客户端连接。
  • asyncio.open_connection():异步打开与服务器的连接。
  • asyncio.run():启动异步事件循环并运行主函数。
  • 只能输入英文,暂不支持中文

6. 总结

通过上述示例,我们展示了如何使用 Python 实现 TCP 网络通信中的发送端和接收端。同步版本的代码简单易懂,适合初学者;而异步版本则提供了更高的性能和更好的并发处理能力,适用于更复杂的应用场景。🛹🛹🛹


7. 常见问题解答

  • Q: 如何处理多个客户端的并发连接?

    • A: 使用多线程或多进程可以处理多个客户端的并发连接。对于更高效的方式,建议使用 asyncio 库来实现异步编程。
  • Q: 如何确保数据传输的可靠性?

    • A: TCP 协议本身就是一个面向连接的可靠协议,它会自动处理数据包的丢失、重复和乱序问题。此外,您可以在应用层添加更多的错误检测机制,如校验和或消息确认。
  • Q: 如何处理大文件传输?

    • A: 对于大文件传输,建议将文件分块发送,并在每次发送后等待服务器的确认。这样可以确保每一块数据都成功传输,并且可以在出现问题时重新发送。

8. 参考资料

  • Python 官方文档 - socket 模块
  • Python 官方文档 - asyncio 模块
  • TCP/IP 协议详解

从而实现对外部世界进行感知,充分认识这个有机与无机的环境,科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣

  1. 我会持续更新对应专栏博客,非常期待你的三连!!!🎉🎉🎉
  2. 如果鹏鹏有哪里说的不妥,还请大佬多多评论指教!!!👍👍👍
  3. 下面有我的🐧🐧🐧群推广,欢迎志同道合的朋友们加入,期待与你的思维碰撞😘😘😘

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

借助 CC-Link IE FB 转 Profinet 网关实现西门子 PLC 接入三菱 PLC 系统的解决策略

我们公司自动化生产线上,原有的控制系统采用三菱 PLC 通过 CC-Link IEFB 网络进行通信和控制。后来随着企业生产规模的扩大和对自动化系统集成度要求的提高,需要将部分设备与新引入的西门子 PLC 控制系统相连接,而西门子 PLC 使用 ProfiNet 协…

即时通信系统项目总览

聊天室服务端项目总体介绍 本项目是一个全栈的即时通信系统, 前端使用QT实现聊天客户端, 后端采⽤微服务框架设计, 由网关子服务统一接收客户端的请求, 再分发到不同的子服务上处理并将结果返回给网关, 网关再将响应转发给客户端 拆分的微服务包含: 网关服务器&…

Redis的五种数据类型(Set、Zset)

目录 1. Set 集合1.1 Set介绍1.2 常见命令1.2.1 SADD命令1.2.2 SMEMBERS命令1.2.3 SISMEMBER命令1.2.4 SCARD命令1.2.5 SPOP命令1.2.6 SMOVE命令1.2.7 SREM命令 1.3 集合间操作1.3.1 SINTER命令1.3.2 SINTERSTORE命令1.3.3 SUNION命令1.3.4 SUNIONSTORE命令1.3.5 SDIFF命令1.3.…

负载均衡OJ项目中遇到的问题

1、续行符问题 关于换行符 ,代码在使用了换行符后无法编译文件,也没有爆出任何错误,更没有按照我们的代码打印出如下类似内容 :[ERROR][compiler.hpp][66][1732635247]编译失败,没有形成可执行程序 随机排查才发现。 代码中的 \ …

力扣面试150 环形子数组的最大和 循环数组 逆向思维

Problem: 918. 环形子数组的最大和 👨‍🏫 参考题解 ⏰ 时间复杂度: O ( n ) O(n) O(n) 🌎 空间复杂度: O ( 1 ) O(1) O(1) class Solution {public int maxSubarraySumCircular(int[] nums) {int maxSum Integer.M…

Java 享元模式:打造高扩展游戏角色模型,优化 MMO 游戏开发

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…

【精选】AI Coding 新范式:Windsurf、Cursor、Coze齐上阵

2AGI.NET | 探索 AI 无限潜力,2AGI 为您带来最前沿资讯。 随着人工智能技术的飞速发展,AI Coding领域迎来了前所未有的变革。Codeium的Windsurf、Cursor的agent模式更新、Copilot的新版本以及Coze的AI应用能力,都在推动着编程领域的创新。本期…

我的世界网易版安装Continuum光影

先去官网下载这个光影压缩包,下载完不要解压(网慢的开加速器) Downloads - Continuum Graphics 进去后选最新的版本号下载就行: 下载好后,先不管他,通过网易游戏启动器打开我的世界 觉得自己电脑性能将将…

使用uniapp开发小程序场景:在百度地图上调用接口返回的设备相关信息并展示

首先在百度地图开发者平台注册微信小程序开发密钥下载百度地图SDK-bmap-wx.min.js,下载地址在项目入口index.html页面进行引入页面中进行调用&#xff0c;代码示例如下<map id"map" longitude"108.95" latitude"34.34" scale"3" :m…

Push an existing folder和Push an existing Git repository的区别

Push an existing folder 和 Push an existing Git repository 是在使用 Git 服务&#xff08;如 GitHub、GitLab、Bitbucket 等&#xff09;时两个常见的操作选项。它们的区别主要体现在项目的初始化和版本控制状态上&#xff1a; 1. Push an existing folder 适用场景&#…

机器学习详解(2):线性回归之理论学习

文章目录 1 监督学习2 线性回归2.1 简单/多元线性回归2.2 最佳拟合线2.3 成本函数和梯度下降2.4 线性回归的假设2.5 线性回归的评估指标函数 3 总结 机器学习是人工智能的一个分支&#xff0c;主要致力于开发能够从数据中学习并进行预测的算法和统计模型。线性回归是机器学习的…

ASP.NET Core8.0学习笔记(二十五)——EF Core Include导航数据加载之预加载与过滤

一、导航属性数据加载 1.在EF Core中可以使用导航属性来加载相关实体。 2.加载实体的三种方式&#xff1a; (1)预先加载&#xff1a;直接在查询主体时就把对应的依赖实体查出来&#xff08;作为初始查询的一部分&#xff09; (2)显式加载&#xff1a;使用代码指示稍后显式的从…

QT实战--带行号的支持高亮的编辑器实现(1)

本文主要介绍了基于QPlainTextEdit实现的&#xff0c;带有行号的,支持高亮的编辑器实现&#xff0c;话不多说&#xff0c;先上效果图&#xff1a; 1.行号头文件&#xff1a;linenumberarea.h #ifndef LINENUMBERAREA_H #define LINENUMBERAREA_H#include <QWidget> #inc…

基于Matlab的卷积神经网络(CNN)苹果分级检测系统

本研究提出了一种基于卷积神经网络&#xff08;CNN&#xff09;的自动化苹果分级系统&#xff0c;该系统根据苹果的视觉特征进行分类。系统采用了预训练的深度学习模型&#xff0c;使用包含不同等级苹果的图像数据集进行训练。研究方法包括图像预处理、特征提取和苹果等级分类。…

华为、华三交换机纯Web下如何创关键VLANIF、操作STP参数

华为交换机WEB操作 使用的是真机S5735&#xff0c;目前主流的版本都适用&#xff08;V1R5~V2R1的就不在列了&#xff0c;版本太老了&#xff0c;界面完全不一样&#xff0c;这里调试线接的console口&#xff0c;电脑的网络接在ETH口&#xff09; 「模拟器、工具合集」复制整段内…

ACM:均分纸牌

主要思路 整体思路概述&#xff1a; 本题旨在解决给定N堆纸牌&#xff08;纸牌总数是N的倍数&#xff09;&#xff0c;通过按照特定移牌规则移动纸牌&#xff0c;找出用最少移动次数使每堆纸牌数量相等的方法。程序采用了一种逐步调整的思路&#xff0c;先计算出每堆纸牌应有的…

定时任务——xxl-job源码解析

摘要 本文深入解析了xxl-job的源码&#xff0c;xxl-job是一个分布式任务调度平台&#xff0c;其核心设计思想是将调度行为抽象成“调度中心”&#xff0c;而任务逻辑则由“执行器”处理&#xff0c;实现调度与任务的解耦。文章详细介绍了调度器和执行器的初始化流程、任务执行…

vxe-table 键盘操作,设置按键编辑方式,支持覆盖方式与追加方式

vxe-table 全键盘操作&#xff0c;按键编辑方式设置&#xff0c;覆盖方式与追加方式&#xff1b; 通过 keyboard-config.editMode 设置按键编辑方式&#xff1b;支持覆盖方式编辑和追加方式编辑 安装 npm install vxe-pc-ui4.3.15 vxe-table4.9.15// ... import VxeUI from v…

乾元通渠道商中标福州市人防信息化建设项目

乾元通渠道商中标福州市人防信息化建设项目&#xff0c;乾元通作为应急通讯设备厂家&#xff0c;为项目提供车载版多链路聚合通信保障设备 QYT-X1s。 青岛乾元通数码科技有限公司作为国家应急产业企业&#xff0c;深耕于数据调度算法研究&#xff0c;参与了多项国家及省部级信息…

【深度学习|地学应用-地震气溶胶异常解析3】气溶胶异常是地震的前兆现象之一!地震是如何影响气溶胶浓度和分布的异常变化的呢,我们该如何分析?

【深度学习|地学应用-地震气溶胶异常解析3】气溶胶异常是地震的前兆现象之一&#xff01;地震是如何影响气溶胶浓度和分布的异常变化的呢&#xff0c;我们该如何分析&#xff1f; 【深度学习|地学应用-地震气溶胶异常解析3】气溶胶异常是地震的前兆现象之一&#xff01;地震是…