python socket通讯 学习记录

python socket

    • 1. 初级实现
    • 2. 添加header
    • 3. 中级实现(引用pickle库)
    • 4. 高级实现(相互发送信息)
    • 5. 一点尝试
      • 5. 1个server对应2个client
      • 5.2个server对应1个client

名称版本
python3.11

本文涉及到socket的server与client通讯从简单到复杂的实现,以及一个server接收多个client,和多个server接收同一个client的案例。

socket中server和client通讯最基础的关系如下图:
在这里插入图片描述

1. 初级实现

server端:server.py

import socket
import timelocalhost = '127.0.0.1'
Port = 1234s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((localhost, Port))
s.listen(1)HEADERSIZE = 10
while True:clientsocket, address = s.accept()print(f"Connection from {address} has been established!")clientsocket.send(bytes("Welcome to the server!", "utf-8"))clientsocket.close()

client端:client.py

import socket
import timelocalhost = '127.0.0.1'
Port = 1234s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((localhost, Port))full_msg = ''
while True:msg = s.recv(8)if len(msg) <= 0:breakfull_msg += msg.decode("utf-8")print(full_msg)

2. 添加header

header可反映message的数据长度,数据类型,协议版本等,用于后续合理规划资源。

server端:server.py
首先,向client端发送 “Welcome to the server!”
随后,每3秒发送 “The time is! xxx”

import socket
import timelocalhost = '127.0.0.1'
Port = 1234s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((localhost, Port))
s.listen(1)HEADERSIZE = 10
while True:clientsocket, address = s.accept()print(f"Connection from {address} has been established!")# send msg to client + header('HEADERSIZE' spaces)!msg = "Welcome to the server!"msg = f'{len(msg):<{HEADERSIZE}}' + msgclientsocket.send(bytes(msg, "utf-8"))while True:time.sleep(3)msg = f" The time is! {time.time()}"msg = f'{len(msg):<{HEADERSIZE}}' + msgclientsocket.send(bytes(msg, "utf-8"))

client端:client.py

import socket
import timelocalhost = '127.0.0.1'
Port = 1234
HEADERSIZE = 10s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((localhost, Port))while True:full_msg = ''new_msg = Truewhile True:msg = s.recv(16)# receive a msg w/ headerif new_msg:print(f"new msg length: {msg[:HEADERSIZE]}")msglen = int(msg[:HEADERSIZE])new_msg = Falsefull_msg += msg.decode("utf-8")if len(full_msg) - HEADERSIZE == msglen:print("full msg recvd")print(full_msg[HEADERSIZE:])new_msg = Truefull_msg = '' 

3. 中级实现(引用pickle库)

Python的pickle库是用于序列化(将对象转换为字节流)和反序列化(将字节流转换为对象)Python对象的标准库。它允许将复杂的Python对象转换为字节流,以便在不同的环境中存储、传输或重新创建对象。

server端:server.py

import picklelocalhost = '127.0.0.1'
Port = 1234s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((localhost, Port))
s.listen(1)HEADERSIZE = 10
while True:clientsocket, address = s.accept()print(f"Connection from {address} has been established!")d = {1: "Hey", 2: "There"}msg = pickle.dumps(d)# print(msg)msg = bytes(f'{len(msg):<{HEADERSIZE}}', "utf-8") + msgclientsocket.send(msg)

client端:client.py

import picklelocalhost = '127.0.0.1'
Port = 1234
HEADERSIZE = 10s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((localhost, Port))while True:full_msg = b''  #b for bytesnew_msg = Truewhile True:msg = s.recv(16)# receive a msg w/ headerif new_msg:print(f"new msg length: {msg[:HEADERSIZE]}")msglen = int(msg[:HEADERSIZE])new_msg = Falsefull_msg += msgif len(full_msg) - HEADERSIZE == msglen:print("full msg recvd")# print(full_msg[HEADERSIZE:])  # before decode d = pickle.loads(full_msg[HEADERSIZE:])print(d)  # after decodenew_msg = Truefull_msg = b'' 

4. 高级实现(相互发送信息)

以上代码都是server向client发送信息。
实际上,在 Socket 通信中,server和client是彼此之间的消息接收者和发送者。它们之间可以相互发送消息,实现双向通信。

server端:server.py

import selectHEADER_LENGTH = 10
IP = '127.0.0.1'
PORT = 1234# 创建一个TCP/IP套接字对象。socket.AF_INET表示使用IPv4地址族,socket.SOCK_STREAM表示使用流式套接字。
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
# 设置套接字选项,socket.SO_REUSEADDR:表示允许重用本地地址和端口。1:表示启用SO_REUSEADDR选项。
server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)server_socket.bind((IP, PORT))
server_socket.listen()  sockets_list = [server_socket]clients = {}print(f'Listening for connections  on {IP}:{PORT}...')def receive_message(client_socket):# 该函数用于:按消息长度来接收信息,有效避免信息阻塞等问题try:# 接收Header of msgs(the header contains message of length)message_header = client_socket.recv(HEADER_LENGTH)# header为0 -》接收不到信息就关闭if not len(message_header):return False# 处理方法 和 client 发送的信息格式有关# 解析头部信息:strip()方法去除字符串两端的空白字符,然后转换Header -> intmessage_length = int(message_header.decode('utf-8').strip())print(message_length)# 返回破解后的msgreturn {'header':message_header, 'data': client_socket.recv(message_length)}except:# 接收不到信息就关闭return Falsewhile True:# 使用select.select()函数来监视sockets_list中的套接字,并返回准备就绪的套接字列表read_sockets(包含server_socket)和 _不在意的列表 和 exception_sockets异常的列表。read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)# 挨个处理准备就绪的列表for notified_socket in read_sockets:# 当遇到 server_socket -》 处理新的客户端连接if notified_socket == server_socket:client_socket, client_address = server_socket.accept()user = receive_message(client_socket) # user 主要是 msg信息{’header','data'}if user is False:continue# 添加已接收的socket至 sockets_listsockets_list.append(client_socket)# 添加:notified_socker的用户到一个clients字典clients[client_socket] = user  # key-valueprint("Accepted new connection from {}:{}, username:{}".format(*client_address, user['data'].decode('utf-8')))else:# 处理已连接的客户端套接字:进一步删选message = receive_message(notified_socket)if message is False:print("Close connection from: {}".format(clients[notified_socket]['data'].decode('utf-8')))# 从sockets_list中移除有问题的notified_socketsockets_list.remove(notified_socket)# 删除del clients[notified_socket]continue# 处理指定socket的信息 user = clients[notified_socket]print(f'Received message from {user["data"].decode("utf-8")}: {message["data"].decode("utf-8")}')# 给client反馈for client_socket in clients:if client_socket != notified_socket:client_socket.send(user["header"] + user["data"] + message["header"] + message["data"])# 异常socket处理:删除for notified_socket in exception_sockets:sockets_list.remove(notified_socket)del clients[notified_socket] 

client端:client.py

import select
import errno
import sysHEADER_LENGTH = 10
IP = '127.0.0.1'
PORT = 1234
my_username = input("Username: ")  #输入用户名#创建一个socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client_socket.connect((IP, PORT))# 将套接字设置为非阻塞模式,以实现非阻塞式的网络通信。
client_socket.setblocking(False)username = my_username.encode('utf-8')
username_header = f"{len(username):<{HEADER_LENGTH}}".encode("utf-8")
client_socket.send(username_header + username)while True:# 输入用户要发送的信息msgmessage = input(f"my_username > ")if message:# 发送msg -> server  message = message.encode("utf-8")message_header = f"{len(message):<{HEADER_LENGTH}}".encode("utf-8")client_socket.send(message_header + message)try:while True:# 接收从server处发来的msgusername_header = client_socket.recv(HEADER_LENGTH)if not len(username_header):print("Connection closed by the server")sys.exit()username_length = int(username_header.decode("utf-8").strip())username = client_socket.recv(username_length).decode("utf-8")message_header = client_socket.recv(HEADER_LENGTH)message_length = int(message_header.decode("utf-8").strip())message = client_socket.recv(message_length).decode("utf-8")print(f'{username} > {message}')except IOError as e:if e.errno != errno.EAGAIN and e.errno != errno.EWOULDBLOCK:print("Reading error: {}".format(str(e)))sys.exit()continueexcept Exception as e:print("Reading error: ".format(str(e)))sys.exit()
server端client端
在这里插入图片描述在这里插入图片描述

5. 一点尝试

1个server - 2个client 与 2个server - 1个client 非常相似,都要用到 threading 多线程,都需要两套IP(IP要选择电脑防火墙可以通过的IP)和Port,这可以理解为由于socket通讯是双向的。在简单的应用场景中,没有太大区别。

5. 1个server对应2个client

用了一个IP和2个不同的Port。

server端:server.py

import socket
import threading# 定义端口号
PORT1 = 1234
PORT2 = 5678# 处理客户端连接的函数
def handle_client(client_socket):while True:# 接收客户端发送的数据data = client_socket.recv(1024)if not data:break# 处理接收到的数据# ...# 发送响应给客户端response = "Server response"client_socket.send(response.encode())# 关闭客户端连接client_socket.close()# 创建套接字并绑定到两个端口上
localhost = '127.0.0.1'
server_socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket1.bind((localhost, PORT1))
server_socket1.listen(1)server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket2.bind((localhost, PORT2))
server_socket2.listen(1)print(f"Server is listening on port {PORT1} and {PORT2}...")# 处理客户端连接的函数
def accept_connections(server_socket):while True:# 等待客户端连接client_socket, client_address = server_socket.accept()print(f"New connection from {client_address[0]}:{client_address[1]}")# 创建线程处理客户端连接client_thread = threading.Thread(target=handle_client, args=(client_socket,))client_thread.start()# 创建线程分别处理两个端口的连接
thread1 = threading.Thread(target=accept_connections, args=(server_socket1,))
thread1.start()thread2 = threading.Thread(target=accept_connections, args=(server_socket2,))
thread2.start()

client端:client.py

import socket# 定义服务器的IP和端口号
SERVER_IP = '127.0.0.1'
SERVER_PORT1 = 1234
SERVER_PORT2 = 5678# 创建第一个客户端套接字并连接到服务器
client_socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket1.connect((SERVER_IP, SERVER_PORT1))# 发送数据给服务器
message1 = "Hello from client 1"
client_socket1.send(message1.encode())# 接收服务器的响应
response1 = client_socket1.recv(1024)
print(f"Response from server: {response1.decode()}")# 关闭客户端套接字
client_socket1.close()# 创建第二个客户端套接字并连接到服务器
client_socket2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket2.connect((SERVER_IP, SERVER_PORT2))# 发送数据给服务器
message2 = "Hello from client 2"
client_socket2.send(message2.encode())# 接收服务器的响应
response2 = client_socket2.recv(1024)
print(f"Response from server: {response2.decode()}")# 关闭客户端套接字
client_socket2.close()

5.2个server对应1个client

用了2个不同的IP和Port。

server端:server.py

import socket
import threading# 定义端口号
PORT1 = 1234
PORT2 = 5678
localhost1 = "127.0.0.1"
localhost2 = "192.168.1.22"# 处理客户端连接的函数
def handle_client(client_socket):while True:# 接收客户端发送的数据data = client_socket.recv(1024)if not data:break# 处理接收到的数据# ...# 发送响应给客户端response = f"{client_socket} : Server response"client_socket.send(response.encode())# 关闭客户端连接client_socket.close()# 创建套接字并绑定到两个端口上
server_socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket1.bind((localhost1, PORT1))
server_socket1.listen(1)server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket2.bind((localhost2, PORT2))
server_socket2.listen(1)print(f"Server is listening {localhost1} on port {PORT1} and {localhost2} on port {PORT2}...")# 处理客户端连接的函数
def accept_connections(server_socket):while True:# 等待客户端连接client_socket, client_address = server_socket.accept()print(f"New connection from {client_address[0]}:{client_address[1]}")# 创建线程处理客户端连接client_thread = threading.Thread(target=handle_client, args=(client_socket,))client_thread.start()# 创建线程分别处理两个端口的连接
thread1 = threading.Thread(target=accept_connections, args=(server_socket1,))
thread1.start()thread2 = threading.Thread(target=accept_connections, args=(server_socket2,))
thread2.start()

client端:client.py

import socket# 定义服务器的IP和端口号
localhost1 = "127.0.0.1"
localhost2 = "192.168.1.22"SERVER_IP1 = localhost1
SERVER_PORT1 = 1234SERVER_IP2 = localhost2
SERVER_PORT2 = 5678# 创建第一个服务器的套接字并连接
server_socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket1.connect((SERVER_IP1, SERVER_PORT1))# 创建第二个服务器的套接字并连接
server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket2.connect((SERVER_IP2, SERVER_PORT2))# 发送数据给第一个服务器
message1 = "Hello from client"
server_socket1.send(message1.encode())# 接收第一个服务器的响应
response1 = server_socket1.recv(1024)
print(f"Response from server 1: {response1.decode()}")# 发送数据给第二个服务器
message2 = "Hello from client"
server_socket2.send(message2.encode())# 接收第二个服务器的响应
response2 = server_socket2.recv(1024)
print(f"Response from server 2: {response2.decode()}")# 关闭套接字
server_socket1.close()
server_socket2.close()

参考:
1. http://t.csdnimg.cn/rj3ve
2. https://youtu.be/CV7_stUWvBQ?si=ppR4MQeKI5E5XNy5

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

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

相关文章

LeetCode 257. 二叉树的所有路径

LeetCode 257. 二叉树的所有路径 1、题目 题目链接&#xff1a;257. 二叉树的所有路径 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root…

java.net.SocketInputStream.socketRead0 卡死导致 tomcat 线程池打满的问题

0 TL;DR; 问题与原因&#xff1a;某些特定条件下 java.net.SocketInputStream.socketRead0 方法会卡死&#xff0c;导致运行线程一直被占用导致泄露采用的方案&#xff1a;使用监控线程异步监控卡死事件&#xff0c;如果发生直接关闭网络连接释放链接以及对应的线程 1. 问题 …

nacos下载安装和nacos启动报错

nacos简介: Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您…

YOLOv9中模块总结补充|RepNCSPELAN4详图

专栏地址&#xff1a;目前售价售价69.9&#xff0c;改进点70 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;助力高效涨点&#xff01;&#xff01;&#xff01; 1. RepNCSPELAN4详图 RepNCSPELAN4是YOLOv9中的特征提取-融合模块&#xff0c;类似前几…

【数据结构-二叉搜索树的增删查改】

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a;基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 …

[虚拟机+单机]梦幻契约H5修复版_附GM工具

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了 视频演示 [虚拟机单…

vue2人力资源项目2登录接口、跳转主页

用户名和密码 解决跨域问题 1.在vue.config.js里配置 proxy: {// 如果请求地址里有api&#xff0c;就转成这个地址/api: {target: https://heimahr.itheima.net/}}// before: require(./mock/mock-server.js)}, axios封装 utils/request.js import axios from axios// creat…

Linux-信号执行

1. 信号什么时候被处理 当进程从内核态返回到用户态的时候&#xff0c;进行信号的检测和处理 什么内核态&#xff0c;什么又是用户态呢&#xff1f; 当进程在CPU上运行时&#xff0c;内核态&#xff1a;允许进程访问操作系统的代码和数据&#xff0c;用户态&#xff1a;进程只…

户口本翻译件怎么处理?

户口本是中国公民的重要证件&#xff0c;由中华人民共和国公安部精心制作&#xff0c;不仅是国内身份的凭证&#xff0c;更是走向世界的一张关键名片。对于想出国留学 、追求移民生活或是追寻异国风情的旅行者来说&#xff0c;户口本翻译件都是不可或缺的一部分。那么&#xf…

Duplicate entry ‘asdfg‘ for key ‘clazz.name‘

Mybatis:java.sql.SQLIntegrityConstraintViolationException:Duplicate entry ‘asdfg’ for key ‘clazz.name’ 违反了数据库的唯一约束条件&#xff0c;即插入数据的时候具有唯一约束&#xff08;被unique修饰&#xff09;的列值重复了 在修改的过程中发生错误&#xff0c;…

【LLM 论文】Least-to-Most Prompting 让 LLM 实现复杂推理

论文&#xff1a;Least-to-Most Prompting Enables Complex Reasoning in Large Language Models ⭐⭐⭐ Google Research, ICLR 2023 论文速读 Chain-of-Thought&#xff08;CoT&#xff09; prompting 的方法通过结合 few-show prompt 的思路&#xff0c;让 LLM 能够挑战更具…

蓝桥青少一月 STEMA-Python 测评第一题

第一题&#xff08;难度系数 2&#xff0c;18 个计分点&#xff09; (注.input()输入函数的括号中不允许添加任何信息) 编程实现&#xff1a; 给定一个正整数 N&#xff0c;输出 N 除以 3 的商。 输入描述&#xff1a;输入一个正整数 N 输出描述&#xff1a;输出 N 除以 3 的商…

2024年抖音小店最新起店玩法,比你报的上万课程都有用!

大家好&#xff0c;我是电商糖果 刚开店的朋友&#xff0c;一定会遇到出单难&#xff0c;店铺没有流量的问题。 自己在网上找一堆教程&#xff0c;或者花高价去报课程。 有的朋友比较幸运&#xff0c;遇到了好的领路人&#xff0c;但是大部分朋友还是没有那么幸运的。 糖果…

API低代码平台介绍2-最基本的数据查询功能

最基本的数据查询功能 本篇文章我们将介绍如何使用ADI平台定义一个基本的数据查询接口。由于是介绍平台具体功能的第一篇文章&#xff0c;里面会涉及比较多的概念介绍&#xff0c;了解了这些概念有助于您阅读后续的文章。 ADI平台的首页面如下&#xff1a; 1.菜单介绍 1.1 O…

【协同过滤】ItemCF协同过滤方法简介

一、ItemCF协同过滤方法 ItemCF 是基于物品相似度进⾏推荐的协同过滤算法。 通过计算共现矩阵中物品列向量的相似度得到物品之间的相似矩阵&#xff0c; 再找到⽤户的历史正反馈物品的相似物品进⾏进⼀步排序和推荐&#xff0c;Item CF的具体步骤如下&#xff1a; 构建共现矩…

测试项目实战——安享理财1(测试用例)

说明&#xff1a; 1.访问地址&#xff1a; 本项目实战使用的是传智播客的安享理财项目&#xff08;找了半天这个项目能免费用且能够满足测试实战需求&#xff09; 前台&#xff1a;http://121.43.169.97:8081/ 后台&#xff1a;http://121.43.169.97:8082/ &#xff08;点赞收藏…

Python turtle绘制图形详解

Python 的 Turtle 模块是一个简单而直观的绘图工具&#xff0c;可以帮助初学者理解基本的图形绘制概念。 1.导入 Turtle 模块&#xff1a; import turtle 2.创建 Turtle 对象&#xff1a; t turtle.Turtle() 3.绘制图形&#xff1a; 4.移动Turtle对象&#xff1a;t.forward(di…

点击短信链接唤起Android App实战

一.概述 在很多业务场景中,需要点击短信链接跳转到App的指定页面。在Android系统中,想要实现这个功能,可以通过DeepLink或AppLink实现。二.方案 2.1 DeepLink 2.1.1 方案效果 DeepLink是Android系统最基础、最普遍、最广泛的外部唤起App的方式,不受系统版本限制。当用户…

腾讯云优惠券领取指导及优惠券使用指南详解

在当今云计算市场&#xff0c;腾讯云以其出色的性能和服务质量受到了广大用户的青睐。为了回馈用户&#xff0c;腾讯云经常推出各种优惠活动&#xff0c;其中就包括优惠券的发放。那么&#xff0c;如何领取腾讯云优惠券&#xff0c;并正确地使用它们呢&#xff1f;本文将为您详…

虚拟机镜像文件qcow2格式转vmdk

一、在esxi上虚拟机导出qcow2镜像文件 1、卸载数据盘、网卡 2、登录虚拟机所在物理服务器&#xff0c;查找系统盘名为vm-101-disk-0的文件位置 find / -name "vm-101-disk-0"使用命令导出qcow2镜像&#xff08;进度条走完就完成了&#xff09;&#xff1a; qemu…