python网络编程之并发服务器丶HTTP协议

python网络编程之并发服务器丶HTTP协议

该文档仅用作学习笔记,转载请表明出处

单进程服务器

  • 完成一个简单的TCP服务器
    案例v11:
'''
完成一个简单的TCP服务器
单进程服务器
'''

from socket import *

serSocket = socket(AF_INET,SOCK_STREAM)

#重复使用绑定的信息
serSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

localAddr = ('',7788)

serSocket.bind(localAddr)

serSocket.listen(5)

while True:
    print('--------主进程:等待客户端到来--------')
    newSocket,destAddr = serSocket.accept()
    print('--------主进程:负责数据的处理--------')
    try:
        while True:
            recvData = newSocket.recv(1024)
            if len(recvData) > 0 :
                print('recv[{}]:{}'.format(str(destAddr),recvData))
            else:
                print('[{}]客户端已经关闭'.format(str(destAddr)))
                break
    finally:
        newSocket.close()
serSocket.close()

这种服务器:

  • 同一时刻只能为一个客户进行服务,布恩那个同时为多个客户服务。
  • 类似于找一个明星签字一样,客户需要耐心等待才可以获取到服务当服务器为一个客户端服务时,而另外的客户端发起了connect,只要服务器listen的队列有空闲的位置,就会为这个新客户端进行连接,并且客户端可以发送数据,但当服务器为这个新客户端服务时,可能一次性把所有数据接收完毕。
  • 当recv接收数据时,返回值为空,即没有返回数据,那么意味着客户端已经调用了close关闭了;因此服务器通过判断recv接收数据是否为空来判断客户端是否已经下线。

多进程服务器

'''
多进程服务器
'''

from socket import *
from multiprocessing import *
from time import sleep

#处理客户端的请求并为其服务
def dealWithClient(newSocket,destAddr):
    while True:
        recvData = newSocket.recv(1024)
        if len(recvData) > 0:
            print('recv[{}]:{}'.format(str(destAddr), recvData))
        else:
            print('[{}]客户端已经关闭'.format(str(destAddr)))
            break
        newSocket.close()
        
#主函数  
def main():
    # 创建服务器套接字  serSocket
    serSocket = socket(AF_INET, SOCK_STREAM)
    # 重复使用绑定的信息
    serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    localAddr = ('', 7788)
    serSocket.bind(localAddr)
    serSocket.listen(5)
    try:
        while True:
            print('--------主进程:等待客户端到来--------')
            newSocket, destAddr = serSocket.accept()
            print('--------进程x:负责数据的处理--------')
            client = Process(target=dealWithClient,args=(newSocket,destAddr))
            client.start()
            newSocket.close()

    finally:
            serSocket.close()

if __name__ == '__main__':
    main()

多线程服务器

'''
多线程服务器
'''

from socket import *
from threading import Thread
from time import sleep

#处理客户端的请求并为其服务
def dealWithClient(newSocket,destAddr):
    while True:
        recvData = newSocket.recv(1024)
        if len(recvData) > 0:
            print('recv[{}]:{}'.format(str(destAddr), recvData))
        else:
            print('[{}]客户端已经关闭'.format(str(destAddr)))
            break
        newSocket.close()
def main():
    serSocket = socket(AF_INET, SOCK_STREAM)
    # 重复使用绑定的信息
    serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    localAddr = ('', 7788)
    serSocket.bind(localAddr)
    serSocket.listen(5)
    try:
        while True:
            print('--------主进程:等待客户端到来--------')
            newSocket, destAddr = serSocket.accept()
            print('--------线程x:负责数据的处理--------')
            client = Thread(target=dealWithClient,args=(newSocket,destAddr))
            client.start()

    finally:
            serSocket.close()

if __name__ == '__main__':
    main()

单进程-非堵塞服务器

  • 多进程多线程的原因:如果没有多线程多进程,那么任务是单任务的,即在为一个顾客服务的时候,不能同时为其他顾客服务。
'''
创建一个单进程非堵塞的服务器
'''

from socket import *
serSocket = socket(AF_INET,SOCK_STREAM)

localAddr = ('',7790)
serSocket.bind(localAddr)

#让socket变成非堵塞
serSocket.setblocking(False)
#设置成被动套接字
serSocket.listen(5)

#用来保存所有已经链接的客户端信息
clientAddrList = []

while True:

    #等待一个新的客户端到来(即完成3次握手的客户端)
    try:
        clientSocket, clientAddr = serSocket.accept()

    except:
        pass
    else:
        print("一个新的客户端到来:{}".format(str(clientAddr)))
        clientSocket.setblocking(False)
        clientAddrList.append(( clientSocket, clientAddr))

    #处理数据
    for clientSocket,clientAddr in clientAddrList:
        try:
            recvData = clientSocket.recv(1024)
        except:
            pass
        else:
        	#数据处理  如果接受到的数据的长度大于0则输出,如果小于0,断开连接
            if len(recvData) > 0:
                print("[{}]:{}".format(str(clientAddr),recvData))
            else:
                clientSocket.close()
                clientAddrList.remove((clientSocket,clientAddr))
                print("[{}]已经下线".format(str(clientAddr)))

select版 - TCP单进程服务器

  • select原理
    • selet可以完成对一些套接字的检测。把套接字列表中可以接受信息的套接字返回。
    • selet的效率高于单进程-非堵塞服务器。
  • selct回显服务器
'''
select回显服务器
'''

import socket
import select
import sys

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('',7789))
server.listen(5)

inputs = [server,]

running = True

while True:
    #调用select函数,阻塞等待
    # readable可以接受的套接字列表
    # writeable可以发送的套接字列表
    # readable可以接受的套接字列表
    readable,writeable,exceptinal = select.select(inputs,[],[])  #3个参数都是列表
                                                                 #第一个是检测列表中的套接字可不可以收数据
                                                                 #第二个是检测列表中的套接字可不可以发数据
                                                                 #第三个是检测列表中的套接字是否产生一些异常
    #数据抵达  循环
    for sock in readable:

        #监听到有新的连接
        if sock == server:
            conn,addr = server.accept()
            #select监听的socket
            inputs.append(conn)

        #监听到键盘有输入
        #elif sock == sys.stdin:
        #    cmd = sys.stdin.readline()
        #    running = False
        #    break

        #有数据到达
        else:
            #读取客服端连接发送的数据
            data =sock.recv(1024)
            if data:
                sock.send(data)
            else:
                # 移除select监听的socket
                inputs.remove(sock)
                sock.close()
        if not running:
            break
server.close()
  • select目前可以在几乎所有的平台。
  • 缺点在于:
    • 单个进程能够监视的文件描述符的数量存在最大限制。
    • 对socket进行扫描的时是依次扫描的,即采用轮询的方式,效率比较低。
    • 套接字多事,select()都要通过遍历FD_SETSIZE 这个Socket来完成调度,不管哪个Socket是活跃的,都要遍历一遍。会浪费很多cpu时间。

epoll版 - TCP单进程服务器

  • select:最多1024,轮询方式
  • poll:解决了套接字有上限的问题,轮询方式
  • epoll:没有1024的限制,事件通知机制(非轮询)
'''
epoll服务器
'''

import socket
import select

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

s.bind(('',7788))

s.listen(5)
epoll = select.epoll

epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET)

connecyions = {}
addresses = {}

while True:
    epoll_list = epoll.poll()
    for fd,exents in epoll_list:
        if fd == s.fileno():
            conn,addr = s.accept()
            print("有新的客服端来到{}".format(str(addr)))

            connecyions[comm.fileno()] = coon
            addresses[comm.fileno()] = addr

            epoll.register(conn.fileno(),select.EPOLLIN|select.EPOLLET)

        elif events == select.EPOLLIN:
            recvData = commections[fd].recv(1024)

            if recvData:
                print("recv:{}".format(recvData))
            else:
                epoll.unregister(fd)
                connecyions[fd].close()
                print("[{}]客户端断开连接".format(addresses[fd]))

协程

  • 协程,又称微线程,纤程。
  • 协程是一个执行单元,它自带cpu上下文,这样只要在合适的时机,我们可以把一个协程切换到另一个协程,只要这个过程中保存或恢复CPU上下文那么程序还是可以运行的。
  • 进程 -----》线程------》协程
  • 计算密集型:需要占用大量的cpu资源 ----》使用多进程
  • IO密集型:需要网络功能,大量时间都在等待网络数据的到来 ----》使用多线程或者协程

猜你喜欢

转载自blog.csdn.net/lz13309833915/article/details/83246059