8.Python TCP网络编程入门指南

HTTP协议

HTTP 是一种用于传输超文本(例如 HTML)的应用层协议。它是基于请求-响应模型的,客户端发送请求,服务器返回响应。HTTP 使用 TCP 作为传输层协议。在 Python 中,有一些内置的模块用于处理 HTTP 请求和响应,例如 http.serverurllib

Socket

Socket 是一种通信机制,允许运行在不同计算机上的进程之间进行通信。它是网络编程的基础,允许数据在网络上传输。Socket 提供了一种统一的编程接口,使得程序员能够使用相似的方式进行网络通信,而不管底层网络协议的细节是什么。
在 Python 中,可以使用 socket 模块创建 TCP/UDP 服务器和客户端。

UDP

UDP 是一种面向无连接的协议,它不提供数据的可靠性和有序性,但具有低延迟的优势。UDP 适用于实时通信,如音频和视频流。与 TCP 不同,UDP 不需要建立连接,数据包直接从源发送到目的地,没有握手和确认的过程。

一个简单的例子

服务器端
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('localhost', 8080))# 处理收到的数据
while True:data, addr = server_socket.recvfrom(1024)print(data.decode('utf-8'))# 处理数据# ...
客户端
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.sendto(b'Hello, Server!', ('localhost', 8080))

TCP

TCP 是一种面向连接的、可靠的、基于字节流的协议。在 TCP 连接中,通信的两端分别是客户端和服务器。TCP 提供了数据完整性和可靠性,确保数据按照发送的顺序到达目的地,并且不会发生丢失或损坏。TCP 使用三次握手建立连接,通过序列号和确认号进行数据传输,最后通过四次握手来终止连接。

一个简单的例子

在 Socket 编程中,有两种主要的角色:服务器和客户端。服务器负责监听并响应来自客户端的请求,而客户端则发送请求并等待服务器的响应。

服务器端
import socket# 创建 socket 对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# ip地址
host = '127.0.0.1'# 设置端口号
port = 8089# 绑定地址和端口
server_socket.bind((host, port))# 监听最多5个连接
server_socket.listen(5)while True:# 建立客户端连接client_socket, addr = server_socket.accept()print('连接地址:', addr)# 发送消息到客户端message = '欢迎访问服务器!'client_socket.send(message.encode('utf-8'))# 关闭连接client_socket.close()
客户端
import socket# 创建 socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# ip地址
host = '127.0.0.1'# 设置端口号
port = 8089# 连接服务器
client_socket.connect((host, port))# 接收服务器消息
message = client_socket.recv(1024)
print(message.decode('utf-8'))# 关闭连接
client_socket.close()

并行处理客户端请求

在上面的例子中,服务器端处理客户端请求是串行的,也就是一个请求一个请求的处理,如果有多个客户端同时连接,那么处理效率就很低。一种简单有效的处理办法是,把请求处理逻辑放到线程池中去,这样就达到了同时处理多个客户端请求的目的。
优化后的服务器端代码

import socket
from concurrent.futures import ThreadPoolExecutor# 服务器配置
HOST = 'localhost'
PORT = 8089
MAX_WORKERS = 5  # 线程池最大线程数def handle_client(client_socket, addr):"""处理单个客户端连接的函数"""try:message = '欢迎访问服务器!'client_socket.send(message.encode('utf-8'))except Exception as e:print(f"Error handling client {addr}: {e}")finally:# 关闭连接print(f"Closing connection from {addr}")client_socket.close()def start_server():"""启动服务器"""server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.bind((HOST, PORT))server_socket.listen(5)# 创建线程池with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:try:while True:client_socket, addr = server_socket.accept()print(f"Accepted connection from {addr}")# 使用线程池处理客户端连接executor.submit(handle_client, client_socket, addr)except KeyboardInterrupt:print("Server shutting down.")finally:# 关闭服务器套接字server_socket.close()if __name__ == "__main__":start_server()

长连接

在 socket 编程中,建立长连接的主要概念是让客户端和服务器之间的连接保持打开状态,而不是在每次通信后都关闭连接。对于高并发的应用来说,减少三次握手的频率可以大大提高系统的吞吐量。

服务器端代码

import socket
from concurrent.futures import ThreadPoolExecutorMAX_WORKERS = 5  # 线程池最大线程数def start_server():# 创建一个 TCP/IP socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定到特定地址和端口server_address = ('localhost', 8089)server_socket.bind(server_address)# 开始监听连接server_socket.listen(5)print(f"Server listening on {server_address}")# 创建线程池with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:try:while True:client_socket, addr = server_socket.accept()print(f"Accepted connection from {addr}")# 使用线程池处理客户端连接executor.submit(handle_client, client_socket, addr)except KeyboardInterrupt:print("Server shutting down.")finally:# 关闭服务器套接字server_socket.close()def handle_client(client_socket, client_address):try:while True:data = client_socket.recv(1024)if not data:break  # 连接关闭时退出循环# 处理接收到的数据message = data.decode('utf-8')print(f"Received data from {client_address}: {message}")# 这里可以根据需要处理数据,并回复客户端response = "Server received your message."client_socket.send(response.encode('utf-8'))except Exception as e:print(f"Error handling client {client_address}: {e}")finally:# 关闭连接print(f"Closing connection from {client_address}")client_socket.close()if __name__ == "__main__":start_server()

客户端代码

import socket# 创建 socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 获取主机名
host = '127.0.0.1'# 设置端口号
port = 8089# 连接服务器
client_socket.connect((host, port))data = input(">")
while data != 'exit':# 发送数据client_socket.send(data.encode('utf-8'))# 接收数据message = client_socket.recv(1024)print(message.decode('utf-8'))data = input(">")# 关闭连接
client_socket.close()

非阻塞IO

非阻塞 I/O 允许程序在等待数据到达时继续执行其他任务,而不必一直等待数据到来。在前面的例子中,socket.send/recv方法都是阻塞等待的,这种阻塞IO的操作会大大降低系统的吞吐量。

import socket
import select
from concurrent.futures import ThreadPoolExecutordef handle_client(sock, clients):data = sock.recv(1024)if data:print(f"Received data from {sock.getpeername()}: {data.decode('utf-8')}")# 这里可以根据需要处理数据,并回复客户端response = "Server received your message."sock.send(response.encode('utf-8'))else:# 连接关闭print(f"Closing connection from {sock.getpeername()}")clients.remove(sock)sock.close()# 创建非阻塞 socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 8088))
server_socket.listen(5)
server_socket.setblocking(False)  # 设置为非阻塞# 存储客户端连接的列表
clients = [server_socket]
executor = ThreadPoolExecutor(5)
while True:# 使用 select 检查是否有准备好的套接字readable, _, _ = select.select(clients, [], [], 1)for sock in readable:if sock == server_socket:# 有新连接client_socket, client_address = server_socket.accept()print(f"Accepted connection from {client_address}")client_socket.setblocking(False)  # 设置为非阻塞clients.append(client_socket)else:# 有数据可读executor.submit(handle_client, sock, clients)# 可以处理其他任务,但是要注意,不能耗时太久,否则socket无法得到及时处理# 其他任务

在这个例子中,select 函数用于监视一组套接字,返回准备好的套接字列表。主循环中通过检查可读的套接字列表,判断是否有新的连接到达或者是否有数据可读。由于select方法是非阻塞的,那么在while True循环里面,除了处于可读的socket之外,还可以处理其他任务。

基于selectors库的IO多路复用

前面的例子中,使用select库来实现非阻塞,Python内置库 selectors 为我们封装了操作系统底层的IO多路复用模型,提供了更便捷的使用方式。
selectors 模块是 Python 中用于实现 I/O 多路复用的一个模块,它提供了对底层选择器(selector)的抽象和封装,以便更方便地进行非阻塞式的 I/O 操作。selectors 模块在 Python 3.4 及以上版本中可用。

I/O 多路复用是一种通过一个线程同时监听多个文件描述符的机制,以提高程序的并发性能。selectors 模块支持多种底层选择器,包括 selectpollepoll(仅限 Linux)、kqueue(仅限 BSD 和 macOS)等。

以下是一个简单的例子,演示了如何使用 selectors 模块进行非阻塞的 Socket 通信:

import socket
import selectors
from concurrent.futures import ThreadPoolExecutor# 创建默认的选择器
selector = selectors.DefaultSelector()
executor = ThreadPoolExecutor(5)def accept(sock, mask):conn, addr = sock.accept()print(f"Accepted connection from {addr}")conn.setblocking(False)selector.register(conn, selectors.EVENT_READ, read)def handle_client(sock):data = sock.recv(1024)if data:print(f"Received data from {sock.getpeername()}: {data.decode('utf-8')}")# 这里可以根据需要处理数据,并回复客户端response = "Server received your message."sock.send(response.encode('utf-8'))else:# 连接关闭print(f"Closing connection from {sock.getpeername()}")selector.unregister(sock)sock.close()def read(sock, mask):executor.submit(handle_client, sock)# 创建服务器套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 8089))
server_socket.listen(5)
server_socket.setblocking(False)  # 设置为非阻塞# 注册服务器套接字,监听连接事件
selector.register(server_socket, selectors.EVENT_READ, accept)try:while True:events = selector.select()  # 阻塞,直到有事件发生for key, mask in events:callback = key.datacallback(key.fileobj, mask)
finally:selector.close()server_socket.close()executor.shutdown()

在这个例子中,使用了 selectors.DefaultSelector() 创建了默认的选择器,并注册了服务器套接字,监听连接事件。当有连接事件发生时,会调用 accept 函数。当连接建立后,又注册了连接套接字,监听读事件,调用 read 函数。

这个例子中的关键是使用了非阻塞的套接字和 selectors 模块,使得程序能够同时处理多个连接,而不会阻塞在某个连接的 I/O 操作上。

asyncio 实现非阻塞IO

asyncio 库基于 selectors 模块,但 asyncio 提供了更高层次的抽象,使得编写异步代码更为方便。selectors 模块通常在 asyncio 的底层被使用,而 asyncio 在其基础上添加了协程、任务和事件循环等概念,使得异步编程更加直观和易用。
selectors 模块提供了底层的 I/O 多路复用机制,而 asyncio 库则构建在其之上,提供了更高层次的异步编程框架,方便开发者编写异步代码。
asyncio通过事件循环(Event Loop)和回调函数来实现。异步编程使得一个线程能够同时处理多个连接,而不会阻塞其他操作。

import asyncioasync def handle_client(reader, writer):data = await reader.read(100)if not data:print("Closing the connection")writer.close()message = data.decode()addr = writer.get_extra_info('peername')print(f"Received {message} from {addr}")print("Send: %r" % message)writer.write(data)await writer.drain()async def main():server = await asyncio.start_server(handle_client, '127.0.0.1', 8089)addr = server.sockets[0].getsockname()print(f'Serving on {addr}')async with server:await server.serve_forever()asyncio.run(main())

在上面的示例中,asyncio.start_server 创建了一个异步服务器,而 handle_client 函数是一个异步的处理客户端连接的方法。await 关键字用于等待异步操作完成。

终级boss:socketserver库

socketserver 模块是 Python 中用于编写网络服务器的高级模块。它提供了一组基于套接字的服务器类,使得开发者能够轻松地创建各种类型的网络服务器,包括支持多线程、多进程、异步和多路复用等不同模型的服务器。

import socketserverclass MyTCPHandler(socketserver.BaseRequestHandler):def handle(self):addr = self.client_addressprint(f"Accepted connection from {addr}")try:data = self.request.recv(1024)if not data:break message = data.decode('utf-8')print(f"Received data from {addr}: {message}")response = f"Server received your message:{message}"self.request.sendall(response.encode('utf-8'))except Exception as e:print(f"Error handling client {addr}: {e}")finally:print(f"Closing connection from {addr}")if __name__ == "__main__":HOST, PORT = "localhost", 8089# 创建多线程的 TCP 服务器server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)try:print(f"Server listening on {HOST}:{PORT}")server.serve_forever()except KeyboardInterrupt:print("Server shutting down.")server.shutdown()
  • socketserver.BaseRequestHandler :一个基础的请求处理器类,用于处理客户端的请求。开发者需要继承这个类并实现自己的 handle 方法,该方法会在每个客户端连接时被调用,用于处理客户端的请求。BaseRequestHandler 提供了访问客户端套接字、客户端地址等信息的属性。
  • socketserver.TCPServer :一个基本的 TCP 服务器类,它继承自 socketserver.BaseServerTCPServer 用于创建基于 TCP 的服务器,可以通过指定地址和端口来监听客户端的连接请求。
  • socketserver.UDPServer :一个基本的 UDP 服务器类,它继承自 socketserver.BaseServerUDPServer 用于创建基于 UDP 的服务器,同样可以通过指定地址和端口来监听客户端的连接请求。
  • socketserver.ThreadingMixInsocketserver.ForkingMixIn :用于实现多线程和多进程的 Mixin 类。通过将它们与 TCPServerUDPServer 结合使用,可以实现多线程或多进程的服务器。
  • socketserver.ThreadingTCPServersocketserver.ForkingTCPServer :已经混合了多线程和多进程功能的 TCP 服务器类。它们继承自 TCPServer,并使用了 ThreadingMixInForkingMixIn
  • socketserver.UnixStreamServersocketserver.UnixDatagramServer :用于创建基于 Unix 域套接字的服务器类,分别用于处理流式和数据报式的连接。

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

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

相关文章

PyQt实战 创建一个PyQt5项目

前后端分离 参考链接 PyQt5实战(二):创建一个PyQt5项目_pyqt5实战项目_笨鸟未必先飞的博客-CSDN博客 项目目录 创建一个QT项目 调用pyuic工具将dialog.ui文件编译为Python程序文件ui_dialog.py。 # -*- coding: utf-8 -*-# Form implemen…

Android 透明度颜色值对照表

一、透明度对照表 注:00是完全透明,FF就是完全不透明 我们的UI小姐姐就喜欢给「不透明度」,这个需要自己判断一下。 完全透明:0% HEX: 00 透明度:1% HEX: 30 透明度:2% HEX: 50 透明度:3% HEX: 80 透明度:4% HEX: A0 透明度:5…

【离散差分】LeetCode2953:统计完全子字符串

作者推荐 [二分查找]LeetCode2040:两个有序数组的第 K 小乘积 本题其它解法 【滑动窗口】LeetCode2953:统计完全子字符串 涉及知识点 分块循环 离散差分 题目 给你一个字符串 word 和一个整数 k 。 如果 word 的一个子字符串 s 满足以下条件,我们称它是 完全…

云原生之深入解析如何限制Kubernetes集群中文件描述符与线程数量

一、背景 linux 中为了防止进程恶意使用资源,系统使用 ulimit 来限制进程的资源使用情况(包括文件描述符,线程数,内存大小等)。同样地在容器化场景中,需要限制其系统资源的使用量。ulimit: docker 默认支持…

08、分析测试执行时间及获取pytest帮助

官方用例 # content of test_slow_func.py import pytest from time import sleeppytest.mark.parametrize(delay,(1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,1.0,0.1,0.2,0,3)) def test_slow_func(delay):print("test_slow_func {}".format(delay))sleep(delay)assert…

概率论中,相关性和独立性的关系

相关性和独立性是概率统计中两个关键的概念。 相关性(Correlation): 定义: 相关性衡量两个变量之间的线性关系程度。如果两个变量的值在某种趋势下同时变化,我们说它们是相关的。相关性的取值范围在 -1 到 1 之间&…

同旺科技 USB TO SPI / I2C --- 调试W5500_TCP Client测试

所需设备: 内附链接 1、USB转SPI_I2C适配器(专业版); 首先,连接W5500模块与同旺科技USB TO SPI / I2C适配器,如下图: 网关IP地址寄存器(192.168.1.1)子网掩码寄存器(255.255.255.0)源MAC地址寄存器源IP地址寄存器(192.168.1.8)…

Django 模型基础(五)

一、models常用字段类型 (一 ) 索引,字符,数字 1、models.AutoField 自增列 如果没有,默认会生成一个名称为 id 的列, 如果要显示的自定义一个自增列,必须将给列设置为主键 primary_keyTru…

Maven 安装自己的依赖

命令 比如当前目录下的依赖包名称是 mytoolutils.jar&#xff0c;则在当前目录下执行 mvn install:install-file -Dfilemytoolutils.jar -DgroupIdutils -DartifactIdtool -Dversion1.0.0 -Dpackagingjar 引用 pom.xml 中引用 <dependency><groupId>utils</g…

ThreadPoolExecutor应用源码剖析(三)

3.3.5 ThreadPoolExecutor的Worker工作线程 Worker对象主要包含了两个内容 ● 工作线程要执行任务 ● 工作线程可能会被中断&#xff0c;控制中断 // Worker继承了AQS&#xff0c;目的就是为了控制工作线程的中断。 // Worker实现了Runnable&#xff0c;内部的Thread对象&…

QT4和 QT5 槽函数连接的区别

正常连接方式 //QT4官方用列QLabel *label new QLabel;QScrollBar *scrollBar new QScrollBar;QObject::connect(scrollBar, SIGNAL(valueChanged(int)),label, SLOT(setNum(int)));//QT5官方用列QLabel *label new QLabel;QLineEdit *lineEdit new QLineEdit;QObject::c…

STK Components 二次开发-飞行器

1.创建飞机 参数帮助文档 var poitList GetTracksData(); var waypointPropagator new WaypointPropagator(m_earth, poitList); var locationPoint waypointPropagator.CreatePoint();m_aircraft new Platform {Name "MH730",LocationPoint locationPoint,Or…

首次部署Linux系统的经历

我是一名电子信息工程专业的学生&#xff0c;有次在图书馆上自习的时候无意间看到其他同学的电脑屏幕&#xff0c;黑色的屏幕上显示着一行一行的代码&#xff0c;勾起了我无限的好奇&#xff0c;经过询问得知他是用的Linux操作系统&#xff0c;是和Windows完全不同的系统&#…

JDBC操作

本博客主要是介绍JDBC操作&#xff0c;即通过编译器操纵数据库中的数据。接下来以插入操作简单介绍该操作。 首先在创建的项目中&#xff0c;添加下列jar包&#xff08;点击可加载下载页面&#xff09;。 mysql-connector-java-5.1.49.jar 然后编写JDBC代码 public class JDB…

vue3 中使用 sse 最佳实践,封装工具

工具 // 接受参数 export interface SSEChatParams {url: string,// sse 连接onmessage: (event: MessageEvent) > void,// 处理消息的函数onopen: () > void,// 建立连接触发的事件finallyHandler: () > void,// 相当于 try_finally 中的 finally 部分&#xff0c;不…

机器学习(2)回归

0.前提 上一期&#xff0c;我们简单的介绍了一些有关机器学习的内容。学习机器学习的最终目的是为了服务我未来的毕设选择之一——智能小车&#xff0c;所以其实大家完全可以根据自己的需求来学习这门课&#xff0c;我做完另一辆小车后打算花点时间去进行一次徒步行&#xff0…

C++现代模板元编程

序 个人发现很多国外的大佬的演讲或者文章都很不错&#xff0c;但是鲜有人来进行分享&#xff0c;届后本人会时不时拿一些看起来很好的东西来给大家分享&#xff0c;主要也是搬运&#xff0c;不过也省去了大家去读英文的麻烦&#xff0c;同时文章中也会参杂着一些自己的见解。…

游戏mod制作--引擎与解包

摘要 游戏mod的制作过程第一步就是需要将原始的游戏工程文件进行解包&#xff0c;得到相应的资源文件&#xff08;贴图&#xff0c;音频&#xff0c;事件&#xff0c;模型甚至源代码等&#xff09;&#xff0c;这个时候下一步就是需要将解包出来的文件进行分类索引&#xff0c…

服务器内存使用率高的原因及解决方法_Maizyun

服务器内存使用率高的原因及解决方法 在服务器运行过程中&#xff0c;内存使用率过高可能会引发一系列问题&#xff0c;如性能下降、应用程序崩溃等。本文将深入探讨服务器内存使用率高的原因&#xff0c;并提出相应的解决方法。 一、内存使用率高的原因 应用程序缺陷&#…

20:kotlin 类和对象 --泛型(Generics)

类可以有类型参数 class Box<T>(t: T) {var value t }要创建类实例&#xff0c;需提供类型参数 val box: Box<Int> Box<Int>(1)如果类型可以被推断出来&#xff0c;可以省略 val box Box(1)通配符 在JAVA泛型中有通配符?、? extends E、? super E&…