python网络编程

news2025/1/16 19:07:02

文章目录

    • socket套接字
    • 客户端/服务模型
    • linux文件描述符fd
    • Linux网络IO模型详解
    • 网络服务器Apache VS Nginx
    • 生产者消费者-生成器版
    • 客户端/服务端-多线程版
    • IO多路复用TCPServer模型
    • 异步IO多路复用TCPServer模型

socket套接字

  • 套接字(socket)是抽象概念,表示TCP连接的一端

  • 通过套接字可以进行数据发送或接收 {IP:Port}==>> 套接字

  • TCP连接由两个套接字组成
    ={Socket1:Socket2}
    ={IP:Port}{IP:Port}

客户端/服务模型

在这里插入图片描述

linux文件描述符fd

Linux一切皆是文件,文件类型 socket.socket.fileno()

  • 普通文件

  • 目录文件

  • 符号链接

  • 设备文件

  • 套接字

    • 进程级的文件描述符表 :文件
    • 系统级的文件描述符表 :外设
    • 文件系统的i-Node表:目录
  • FIFO

Linux网络IO模型详解

阻塞式IO

在这里插入图片描述

非阻塞式IO

在这里插入图片描述

IO多路复用

在这里插入图片描述

信号驱动式IO

在这里插入图片描述

异步IO

在这里插入图片描述

五种IO比较

在这里插入图片描述

IO多路复用

在这里插入图片描述

  • select:线性扫描所有监听的文件描述符fd

  • poll:同选择性能有所优化

  • Epoll:使用红黑树管理数据结构,性能好

网络服务器Apache VS Nginx

ApacheNginx
多线程多路复用
资源占用大更加轻量
模块多模块化设计,社区活跃
稳定,缺陷少静态资源、反向代理
性能较好性能很好

生产者消费者-生成器版

import time


# 消费者
def consumer():
    cnt = yield
    while True:
        if cnt <= 0:
            # 暂停、让出CPU
            cnt = yield cnt
        cnt -= 1
        time.sleep(1)
        print('consumer consum 1 cnt. cnt =', cnt)


# 生产者 (调度器)
def producer(cnt):
    gen = consumer()
    # 激活生成器
    next(gen)
    gen.send(cnt)
    while True:
        cnt += 1
        print('producer producer 5 cnt. cnt =', cnt)
        # 调度消费者
        current = int(time.time())
        if current % 5 == 0:
            cnt = gen.send(cnt)
        else:
            time.sleep(1)


if __name__ == '__main__':
    producer(0)

客户端/服务端-多线程版

客户端

# -*- encoding=utf-8 -*-
# 客户端

import socket


client = socket.socket()
print('client.fileno:', client.fileno())

client.connect(('127.0.0.1', 8999))

while True:
    content = str(input('>>>'))
    client.send(content.encode())
    content = client.recv(1024)
    print('client recv content:', content)

服务端

import socket
import threading


def thread_process(s):
    while True:
        content = s.recv(1024)
        if len(content) == 0:
            break
        s.send(content.upper())
        print(str(content, encoding='utf-8'))  # 接受来自客户端的消息,并打印出来
        s.close()


server = socket.socket()  # 1. 新建socket
server.bind(('127.0.0.1', 8999))  # 2. 绑定IP和端口(其中127.0.0.1为本机回环IP)
server.listen(5)  # 3. 监听连接

while True:
    s, addr = server.accept()  # 4. 接受连接
    new_thread = threading.Thread(target=thread_process, args=(s,))
    print('new thread process connect addr:{}'.format(addr))
    new_thread.start()

IO多路复用TCPServer模型

# -*- encoding=utf-8 -*-
# IO多路复用TCPServer模型

import select
import socket


def serve():
    server = socket.socket()
    server.bind(('127.0.0.1', 8999))
    server.listen(1)

    epoll = select.epoll()
    epoll.register(server.fileno(), select.EPOLLIN)

    connections = {}
    contents = {}

    while True:
        events = epoll.poll(10)
        for fileno, event in events:
            if fileno == server.fileno():
                # 当fd为当前服务器的描述符时,获取新连接
                s, addr = server.accept()  # 获取套接字和地址
                print(f"new connection from addr:{addr},fileno:{s.fileno()},socket:{s}")
                epoll.register(s.fileno(), select.EPOLLIN)
                connections[s.fileno()] = s
            elif event == select.EPOLLIN:
                # 当fd不为服务器描述符为客户端描述符时,读事件就绪,有新数据可读
                s = connections[fileno]
                content = s.recv(1024)
                if content:
                    # 当客户端发送数据时
                    print(f"recv content is {content}")
                    print(f"fileno:{fileno} event:{event}")
                    epoll.modify(fileno, select.EPOLLOUT)
                    contents[fileno] = content
                else:
                    # 当客户端退出连接时
                    print(f"recv content is null")
                    print(f"fileno;{fileno} event:{event} ")
                    epoll.unregister(fileno)
                    s.close()
                    connections.pop(fileno)
            elif event == select.EPOLLOUT:
                # 当fd不为服务器描述符为客户端描述符时,写事件就绪
                try:
                    content = contents[fileno]
                    s = connections[fileno]
                    s.send(content)
                    epoll.modify(s.fileno(), select.EPOLLIN)
                    print(f"modify content is {content}")
                    print(f"fileno;{fileno} event:{event} ")
                except Exception as error:
                    epoll.unregister(fileno)
                    s.close()
                    connections.pop(fileno)
                    contents.pop(fileno)
                    print(f"modify content is failed")
                    print(f"fileno;{fileno} event:{event} ")


if __name__ == '__main__':
    serve()

异步IO多路复用TCPServer模型

import socket
import select
from collections import deque


class Future:
	"""可等待对象 Future"""
    def __init__(self, loop):
        self.loop = loop
        self.done = False
        self.co = None

    def set_done(self):
        self.done = True

    def set_coroutine(self, co):
        self.co = co

    def __await__(self):
        if not self.done:
            yield self
        return


class EventLoop:
    """调度器:epoll事件驱动"""
    current = None
    runnable = deque()
    epoll = select.epoll()
    handler = {}

    @classmethod
    def instance(cls):
        if not EventLoop.current:
            EventLoop.current = EventLoop()
        return EventLoop.current

    def create_future(self):
        return Future(loop=self)

    def register_handler(self, fileno, events, handler):
        self.handler[fileno] = handler
        self.epoll.register(fileno, events)

    def unregister_handler(self, fileno):
        self.epoll.unregister(fileno)
        self.handler.pop(fileno)

    def add_coroutine(self, co):
        self.runnable.append(co)

    def run_coroutine(self, co):
        try:
            future: Future = co.send(None)
            future.set_coroutine(co)
        except Exception as e:
            print(e)
            print('coroutine {} stopped'.format(co.__name__))

    def run_forever(self):
        while True:
            while self.runnable:
                self.run_coroutine(co=self.runnable.popleft())
            events = self.epoll.poll(1)
            for fileno, event in events:
                handler = self.handler.get(fileno)
                handler()


class SocketWrapper:
	"""套接字协程适配器"""
    def __init__(self, sock: socket.socket, loop: EventLoop):
        self.loop = loop
        self.sock = sock
        self.sock.setblocking(False)

    @property
    def fileno(self):
        return self.sock.fileno()

    def create_future_for_events(self, events):
        future: Future = self.loop.create_future()

        def handler():
            future.set_done()
            self.loop.unregister_handler(self.fileno)
            if future.co:
                self.loop.add_coroutine(future.co)

        self.loop.register_handler(self.fileno, events, handler)

        return future

    async def accept(self):
        while True:
            try:
                sock, addr = self.sock.accept()
                return SocketWrapper(sock, self.loop), addr
            except BlockingIOError:
                future = self.create_future_for_events(select.EPOLLIN)
                await future

    async def recv(self, backlog):
        while True:
            try:
                return self.sock.recv(backlog)
            except BlockingIOError:
                future = self.create_future_for_events(select.EPOLLIN)
                await future

    async def send(self, data):
        while True:
            try:
                return self.sock.send(data)
            except BlockingIOError:
                future = self.create_future_for_events(select.EPOLLOUT)
                await future


class TCPServer:

    def __init__(self, loop: EventLoop):
        self.loop = loop
        self.listen_sock: SocketWrapper = self.create_listen_socket()
        self.loop.add_coroutine(self.serve_forever())

    def create_listen_socket(self, ip='localhost', port=8999):
        sock = socket.socket()
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind((ip, port))
        sock.listen()
        return SocketWrapper(sock, self.loop)

    async def handler_client(self, sock:SocketWrapper):
        while True:
            data = await sock.recv(1024)
            if not data:
                print('client disconnected')
                break
            await sock.send(data.upper())

    async def serve_forever(self):
        while True:
            sock, addr = await self.listen_sock.accept()
            print(f'client connect addr = {addr}')
            self.loop.add_coroutine(self.handler_client(sock))


if __name__ == '__main__':
    loop = EventLoop.instance()
    server = TCPServer(loop)
    loop.run_forever()

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

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

相关文章

【数据分享】1901-2022年1km分辨率的逐月降水栅格数据(免费获取/全国/分省)

气象指标在日常研究中非常常用&#xff0c;之前我们给大家分享过来源于国家青藏高原科学数据中心提供的气象指标栅格数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff1a; 1901-2022年1km分辨率逐月平均气温栅格数据1901-2022年1km分辨率逐年平均气温栅格数据…

wsl中使用宝塔每次都要绑定账号问题解决

环境&#xff1a;windows11、wsl2、Ubuntu20.04、宝塔8.0.24 1、开启Hyper-V&#xff0c;如果是家庭版使用下面代码启用Hyper-V&#xff0c;创建个.cmd文件保存后使用管理员权限运行&#xff08;需要重启电脑&#xff09; pushd "%~dp0" dir /b %SystemRoot%\servi…

QT6配置Android环境的多次尝试

可能用到的链接&#xff1a;https://www.androiddevtools.cn/#&#xff08;Android开发工具&#xff09; https://developer.android.google.cn/studio&#xff08;Android studio 下载&#xff09; https://www.oracle.com/java/technologies/downloads&#xff08;java下载&a…

【pyinstaller 怎么打包python,打包后程序闪退 不打日志 找不到自建模块等问题的踩坑解决】

程序打包踩坑解决的所有问题 问题1 多个目录怎么打包 不管你包含多个层目录&#xff0c;引用多么复杂&#xff0c;只需要打包主程序所在文件即可&#xff0c;pyinstaller会自动寻找依赖包&#xff0c;如果报错自建模块找不到&#xff0c;参照问题3 pyinstaller main.py问题2…

QT创建可移动点类

效果如图所示&#xff1a; 创建新类MovablePoint&#xff0c;继承自QWidget. MovablePoint头文件: #ifndef MOVABLEPOINT_H #define MOVABLEPOINT_H#include <QWidget> #include <QPainter> #include <QPaintEvent> #include <QStyleOption> #includ…

——滑动窗口

滑动窗口 所谓滑动窗口&#xff0c;就是不断的调节子序列的起始位置和终止位置&#xff0c;从而得出我们要想的结果。也可以理解为一种双指针的做法。 leetcode76 class Solution {public String minWindow(String s, String t) {char[] schars s.toCharArray();char[] tc…

需要在Activity间传递大量的数据,能有哪些方法?

在Activity间传递的数据一般比较简单&#xff0c;可是有时分实践开发中也会传一些比较复杂的数据&#xff0c;尤其是面试问道当遇到需求在Activity间传递很多的数据怎么办&#xff1f; Intent 传递数据的巨细是有约束的&#xff0c;它大约能传的数据是1M-8K&#xff0c;原因是…

【狂神】Spring5笔记(10-19)

又是美好而努力的一天呀~ __ /|* * * * * * / * * * / * * * * / * * * * * * * happy valentines day * * * * …

python基础之miniConda管理器

一、介绍 MiniConda 是一个轻量级的 Conda 版本&#xff0c;它是 Conda 的精简版&#xff0c;专注于提供基本的环境管理功能。Conda 是一个流行的开源包管理系统和环境管理器&#xff0c;用于在不同的操作系统上安装、管理和运行软件包。 与完整版的 Anaconda 相比&#xff0c…

CSS transition 过渡

1 前言 CSS过渡(transition)可以在一个元素切换到另一种状态时为其定义平滑的过渡效果。 例如&#xff0c;用户鼠标悬停在按钮上时&#xff0c;按钮颜色平滑的从一个颜色过渡到另一个颜色。 .btn:hover{background-color: red;color: black; }默认悬停效果 添加过渡效果 .b…

2.(Python数模)线性规划问题

Python解决线性规划问题 参考了以下博文 https://blog.csdn.net/m0_46692607/article/details/126784109?spm1001.2014.3001.5506 目标是解决以下的线性规划&#xff0c;程序计算出目标函数的最大值&#xff0c;并在最大值下取得的x1x2x3对应值。 源代码如下&#xff1a; …

MybatisPlus(2)

前言&#x1f36d; ❤️❤️❤️SSM专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring Spring MVC MyBatis_冷兮雪的博客-CSDN博客 上篇我们简单介绍了MybatisPlus的方便之处&#xff0c;这篇来深入了解Myb…

探索生成式人工智能的前景

一、什么是生成式人工智能&#xff1f; 生成式人工智能&#xff08;Generative AI&#xff09;是一类人工智能&#xff08;AI&#xff09;技术和模型&#xff0c;旨在创建新颖的内容。与简单的复制不同&#xff0c;这些模型通过利用从训练数据集中收集到的模式和见解&#xff…

飞桨花滑骨骼点动作识别比赛记 2

基于 PaddleVideo 的花滑骨骼点动作识别 2s-AGCN配置文件节点流配置文件 2s-agcn_ntucs_joint_fsd.yamlMODEL 字段DATASET 字段PIPELINE 和 INFERENCE 字段OPTIMIZER 字段 agcn2s.pygraph输入通道数 骨骼流 Dataset 和 Pipeline配置文件DATASETPIPELINE 源码skeleton.pyskeleto…

Elasticsearch 7.6 - API高阶操作篇

ES 7.6 - API高阶操作篇 分片和副本索引别名添加别名查询所有别名删除别名使用别名代替索引操作代替插入代替查询 场景实操 滚动索引索引模板创建索引模板查看模板删除模板 场景实操一把索引的生命周期数据迁移APIGEO(地理)API索引准备矩形查询圆形查询多边形查询 自定义分词器…

Spring理解,重要概念及图解,2023秋招spring常见八股文

按照自己的需求&#xff0c;找到自己不会的地方去解决 1.Spring的核心 1&#xff09;Spring的两大核心&#xff1a;IoC和AOP Spring框架包含众多模块&#xff0c;如Core、Testing、Data Access、Web Servlet等&#xff0c;其中Core是整个Spring框架的核心模块。Core模块提供…

进程的挂起状态

进程的挂起状态详解 当我们谈论操作系统和进程管理时&#xff0c;我们经常听到进程的各种状态&#xff0c;如“就绪”、“运行”和“阻塞”。但其中一个不那么常被提及&#xff0c;但同样重要的状态是“挂起”状态。本文将深入探讨挂起状态&#xff0c;以及为什么和在何时进程…

linux中安装nodejs,卸载nodejs,更新nodejs,git,linux中安装nginx并配置

文章目录 node的安装与卸载&#xff08;更新版本&#xff09;卸载nodejs安装新版本node git安装与拉取代码安装解决 linux git 每次推拉(push/pull)代码都要输入用户名密码的问题 nginx 安装、配置和卸载安装nginx配置**.conf 文件内容 nginx 卸载 注意&#xff0c;我的是Ubunt…

【Linux】root和子用户都能执行的命令,sudo无法执行(已解决)

全流程帖子 https://ask.oceanbase.com/t/topic/35604437/7 1.问题 如题&#xff0c;在编译miniob的时候遇到如下错误 [muvm-cnt8:~/code/miniob]$ sudo bash build.sh init build.sh init HEAD is now at 5df3037d Merge branch release-2.1.12-stable-pull into patches-2.…

一文搞定全进程间通讯(IPC)八大方式-管道、命名管道、信号、信号量、消息队列、共享内存+内存映射、套接字

进程间通讯&#xff08;IPC&#xff09; 参考 / 引用&#xff1a; 如何在Linux下的进行多进程编程&#xff08;初步&#xff09; - 知乎 (zhihu.com)。浅析进程间通信的几种方式&#xff08;含实例源码&#xff09; - 知乎 (zhihu.com)。 linux基础——linux进程间通信&#…