欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

web服务器--并发web服务器实现--单进程和单线程实现非堵塞的原理

程序员文章站 2022-05-04 18:21:20
...

之前实现了多进程 多线程 以及协程。其实多任务是为了解决阻塞问题。那么单进程就不能解决堵塞问题吗?答案是可以的:

import socket
import time


tcp_service_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_service_socket.bind(('',7890))
tcp_service_socket.listen(128)
tcp_service_socket.setblocking(False)  # 设置套接字为非阻塞的方式

client_socket_list = list()


while True:
    
    time.sleep(1)

    try:
        new_socket, new_addr = tcp_service_socket.accept()
    except Exception as ret:
        print('---没有新的客户端到来---')
    else:
        print('---只要没有产生异常,就意味着来了一个新的客户端---')
        new_socket.setblocking(False)  #设置套接字为非阻塞的方式
        client_socket_list.append(new_socket)

        for client_socket in client_socket_list:
            try:
                recv_data = new_socket.recv(1024)
            except Exception as ret:
                print('---这个客户端没有发送数据---')
            else:
                if recv_data:
                    # 对方发来了数据
                    print('---如果没有产生异常,那么意味着客户端发来了数据---')
                else:
                    # 对方调用了close 导致了 recv 返回
                    client_socket.close()
                    client_socket_list.remove(client_socket)
                    print('---客户端已经关闭---')

长连接 短连接
之前讲过 HTTP协议以前是1.0版本,现在大多使用1.1版本。1.0版本是短连接。1.1版本是长连接。
长连接:三次握手,获取数据,再次获取数据…四次挥手。
短连接:为了获取一个数据,三次握手得到后四次挥手。再获取数据,就再次三次握手得到后四次挥手…

web服务器--并发web服务器实现--单进程和单线程实现非堵塞的原理
web服务器--并发web服务器实现--单进程和单线程实现非堵塞的原理
服务器一般都是长连接。 占用资源少。
web服务器--并发web服务器实现--单进程和单线程实现非堵塞的原理
单进程 --单线程–非阻塞–长连接–服务器实现

之前写的程序发完数据就close 属于短连接。接下来写长连接,不能使用close 了。

import socket
import re

def service_client(new_client_sock,requst):
    '''为这个客户服务'''

    # 转为列表
    requst_lines = requst.splitlines()

    # 寻找文件名  GET /index.html HTTP/1.1
    # 开头有:GET POST PUT DEL 不一定是GET 所以应该不能用GET匹配。
    file_name = ''
    ret = re.match(r'[^/]+(/[^ ]*)',requst_lines[0])
    if ret:
        file_name = ret.group(1)
        if file_name == '/ ':
            file_name = '/index.html'

    # 2.3 准备body,且这里准备的body不能和之前的字符串一样,直接相加,而是要单独发送。
    # 找到相对应的.html文件
    try:
        f = open('./html'+file_name,'rb')
    except:
        response = 'HTTP/1.1 404 NOT FOUND\r\n'
        response += '\r\n'
        response += '---file not found--'
    else:
        html_content = f.read()
        f.close()

        # 2.返回数据给浏览器,http 格式
        response_body = html_content

        response_header = 'HTTP/1.1 200 OK\r\n'
        response_header += 'Content-Length:%d\r\n' % len(response_body)
        response_header += '\r\n'

        response = response_header.encode('utf-8') + response_body

        # 2.4 发送数据给浏览器
        new_client_sock.send((response))

    # 3.长连接不能使用close关闭套接字,那么我们怎么知道数据已经发完了呢?
    # 在 header 中加入发送数据的长度 Content-Length:
    # new_client_sock.close()


def main():
    '''用来完成整体的控制'''

    # 1.创建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2.绑定
    tcp_socket.bind(('', 7890))

    # 3.变为监听套接字
    tcp_socket.listen(128)
    tcp_socket.setblocking(False)  # 将套接字变为非堵塞

    client_socket_list = list()

    while True:

        # 4.等待客户端的链接
        try:
            new_client_sock, client_addr = tcp_socket.accept()
        except Exception as ret:
            pass
        else:
            new_client_sock.setblocking(False)
            client_socket_list.append(new_client_sock)

        for client_socket in client_socket_list:
            try:
                recv_data = client_socket.recv(1024).decode('utf-8')
            except Exception as ret:
                pass
            else:
                if recv_data:
                    service_client(new_client_sock,recv_data)
                else:
                    client_socket.close()
                    client_socket_list.remove(client_socket)

    # 4.关闭监听套接字
    tcp_socket.close()


if __name__ == '__main__':
    main()