美文网首页
python实现并发服务器实现方式(多线程/多进程/select

python实现并发服务器实现方式(多线程/多进程/select

作者: 747大雄 | 来源:发表于2018-12-14 19:44 被阅读0次

并发服务器开发

并发服务器开发,使得一个服务器可以近乎同一时刻为多个客户端提供服务。实现并发的方式有多种,下面以多进程,多线程,IO多路复用等方式实现并发。这里使用网络编程中的TCP服务器和客户端通信为例子。

多进程并发阻塞

利用进程把客户端和服务器进行管理,当有新的客户端连接到服务器时,就创建一个新的进程来管理,通过操作系统的调度,从而实现了并发的操作

from multiprocessing import Process
from socket import *


def recv_data(new_socket, client_info):
    print("客户端{}已经连接".format(client_info))
    # 接受数据
    raw_data = new_socket.recv(1024)
    while raw_data:
        print(f"收到来自{client_info}的数据:{raw_data}")
        raw_data = new_socket.recv(1024)
    new_socket.close()


def main():
    # 实例化socket对象
    socket_server = socket(AF_INET, SOCK_STREAM)
    # 设置端口复用
    socket_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    # 绑定IP地址和端口
    socket_server.bind(("", 7788))
    # 改主动为被动,监听客户端
    socket_server.listen(5)
    while True:
        # 等待连接
        new_socket, client_info = socket_server.accept()
        p = Process(target=recv_data, args=(new_socket, client_info))
        p.start()
        # 多进程会复制父进程的内存空间,所以父进程中new_socket也必须关闭
        new_socket.close()


if __name__ == '__main__':
    main()

多线程并发阻塞

多线程和多进程类似,只是线程间共享内存空间,要注意变量的管理

from threading import Thread
from socket import *


def recv_data(new_socket, client_info):
    print("客户端{}已经连接".format(client_info))
    # 接受数据
    raw_data = new_socket.recv(1024)
    while raw_data:
        print(f"收到来自{client_info}的数据:{raw_data}")
        raw_data = new_socket.recv(1024)
    new_socket.close()


def main():
    # 实例化socket对象
    socket_server = socket(AF_INET, SOCK_STREAM)
    # 设置端口复用
    socket_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    # 绑定IP地址和端口
    socket_server.bind(("", 7788))
    # 改主动为被动,监听客户端
    socket_server.listen(5)
    while True:
        # 等待连接
        new_socket, client_info = socket_server.accept()
        p = Thread(target=recv_data, args=(new_socket, client_info))
        p.start()
        # 多线程共享一片内存区域,所以这里不用关闭
        # new_socket.close()


if __name__ == '__main__':
    main()

多路复用IO---select模型

在操作系统层面上,系统提供了一个select接口,它会轮询给定的文件描述符状态,如果其中有描述符的状态改变,select()就会返回有变化的文件描述符。

from socket import *
import select


# 实例化对象
socket_server = socket(AF_INET, SOCK_STREAM)
# 绑定IP和端口
socket_server.bind(("", 7788))
# 将主动模式改为被动模式
socket_server.listen(5)
# 创建套接字列表
socket_lists = [socket_server]
# 等待客户端连接
while True:
    # 只监听读的状态,程序阻塞在这,不消耗CPU,如果列表里面的值读状态变化后,就解阻塞
    read_lists, _, _ = select.select(socket_lists, [], [])
    # 循环有变化的套接字
    for sock in read_lists:
        # 判断是否是主套接字
        if sock == socket_server:
            # 获取新连接
            new_socket, client_info = socket_server.accept()
            print(f"客户端:{client_info}已连接")
            # 添加到监听列表中
            socket_lists.append(new_socket)
        else:
            # 不是主客户端,即接收消息
            raw_data = sock.recv(1024)
            if raw_data:
                print(f"接收数据:{raw_data.decode('gb2312')}")
            else:
                # 如果没有数据,则客户端断开连接
                sock.close()
                # 从监听列表中删除该套接字
                socket_lists.remove(sock)

优点:良好的跨平台支持

缺点:1.监测的文件描述符数量有最大限制,Linux系统一般为1024,可以修改宏定义或者内核进行修改,但是会造成效率低下;2.对文件描述符采用轮询机制,每个文件描述符都会询问一遍,这样很消耗CPU时间

多路复用IO---epoll模型

为了解决select轮询机制造成的效率低下问题,则引入了epoll接口。相较于select的两大优势。1.没有文件描述符最大数量的限制(最大数量则看内存大小);2.采用时间通知机制,当文件描述符状态有变时,主动通知内核进行调度。其中print注释是为了打印对象,查看对象是什么。

from socket import *
import select


# 创建socket对象
sock_server = socket(AF_INET, SOCK_STREAM)
# 绑定IP和端口
sock_server.bind(("", 7788))
# 将主动模式设置为被动模式,监听连接
sock_server.listen(5)
# 创建epoll监测对象
epoll = select.epoll()
# print("未注册epoll对象:{}".format(epoll))
# 注册主套接字,监控读状态
epoll.register(sock_server.fileno(), select.EPOLLIN)
# print("注册了主套接字后:{}".format(epoll))
# 创建字典,保存套接字对象
sock_dicts = {}
# 创建字典,保存客户端信息
client_dicts = {}
while True:
    # print("所有套接字:{}".format(sock_dicts))
    # print("所有客户端信息:{}".format(client_dicts))
    # 程序阻塞在这,返回文件描述符有变化的对象
    poll_list = epoll.poll()
    # print("有变化的套接字:{}".format(poll_list))
    for sock_fileno, events in poll_list:
        # print("文件描述符:{},事件:{}".format(sock_fileno, events))
        # 判断是否是主套接字
        if sock_fileno == sock_server.fileno():
            # 创建新套接字
            new_sock, client_info = sock_server.accept()
            print(f"客户端:{client_info}已连接")
            # 注册到epoll监测中
            epoll.register(new_sock.fileno(), select.EPOLLIN)
            # 添加到套接字字典当中
            sock_dicts[new_sock.fileno()] = new_sock
            client_dicts[new_sock.fileno()] = client_info
        else:
            # 接收消息
            raw_data = sock_dicts[sock_fileno].recv(1024)
            if raw_data:
                print(f"来自{client_dicts[sock_fileno]}的数据:{raw_data.decode('gb2312')}")
            else:
                # 关闭连接
                sock_dicts[sock_fileno].close()
                # 注销epoll监测对象
                epoll.unregister(sock_fileno)
                # 数据为空,则客户端断开连接,删除相关数据
                del sock_dicts[sock_fileno]
                del client_dicts[sock_fileno]

相关文章

  • 03-web服务器v3.1--03

    多进程、多线程实现Http服务器 使用gevent实现Http服务器开启多任务 单进程、线程、非堵塞实现并发 长链...

  • python实现并发服务器实现方式(多线程/多进程/select

    并发服务器开发 并发服务器开发,使得一个服务器可以近乎同一时刻为多个客户端提供服务。实现并发的方式有多种,下面以多...

  • python网络爬虫:多任务-进程、线程

    一 、实现多任务的方式 多线程多进程协程多线程+多进程 并行,并发 并行:同时发起同时执行,(4核,4个任务)并发...

  • 看完这篇文章还不懂异步IO (asyncio) 协程?

    python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程。无论多线程和多进程,I...

  • Python黑魔法 --- 异步IO( asyncio) 协程

    python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程。无论多线程和多进程,I...

  • Python爬虫之并发下载

    并发下载 多线程和多进程回顾 在前面的《进程和线程》一文中,我们已经对在Python中使用多进程和多线程实现并发编...

  • 多进程和多线程编程

    多任务的实现方式: 多进程模式 多线程模式 多进程 + 多线程 模式python即支持多进程,又支持多线程,如下进...

  • Python多进程(Multiprocessing)的简单使用

    Python由于GIL的存在,多线程(Thread)、协程(Asyncio)可以实现并发,并行则依赖多进程(Mul...

  • 浅析python的GIL

    Python中的GIL锁 在Python中,可以通过多进程、多线程和多协程来实现多任务。 在多线程的实现过程中,为...

  • 多任务

    实现多任务的方式 多线程多进程协程多线程+多进程 并行:同时发起,同时执行(4核4个任务)并发:同时发起,单个执行...

网友评论

      本文标题:python实现并发服务器实现方式(多线程/多进程/select

      本文链接:https://www.haomeiwen.com/subject/kjhnhqtx.html