Python-网络编程:一个简单的http server6(单进程单线程,非堵塞方式 并发 实现,长连接)

参考代码:


import socket, re
import time

# 长连接
# http 1.1版本用的就是长连接
# 浏览器如何知道服务器已经发完数据了,可以通过服务器发送的包中的包含所发送内容长度这个值来判断

# 给新连接进来的客户端返回一个html页面
def communicate(new_socket, request):

    # 将已经解码变为字符串的request进行切割
    request_lines = request.splitlines()
    # print(request_lines)

    # 从列表中取得想要的东西,浏览器想要的文件名
    # GET /index.html HTTP/1.1
    # 这个正则又看不懂了。得查。
    # ^ :不是后面的字符
    # 把需要的用括号括起来
    # 防止没有flie_name ,先设为空字符串
    file_name = ""
    print(">>>>>>",request_lines)
    if request_lines:
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        if ret:
            file_name = ret.group(1)
            print("*"*50, file_name)
            if file_name == '/':
                file_name = '/index.html' 

    # 打开请求的文件, 由于不一定有文件,所以用try except
    try:
        f = open("./MyWeb2" + file_name, 'rb')
    except:
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "-----no page-----"
        new_socket.send(response.encode('utf-8'))
    else:
        html_content = f.read()
        f.close()

        response_body = html_content 
      
        response_header = "HTTP/1.1 200 ok\r\n"
        # 加上body的长度
        response_header += "Content-Length:%d\r\n" % len(response_body)
        response_header += "\r\n"

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

        new_socket.send(response)                   # 
        
        # 注意其实此处我在想渲染一个页面时会要很多文件比如html页面,css页面等等,那是不是就要用一个循环来做,
        # 后来想通了,每次浏览器要什么就回什么,至于展示渲染,就是浏览器的事啦。(所以之前的都是写法所基于的思想都是来一个请求,都执行3握手,4挥手。)
    # 现在要长连接的方式实现,故不要关闭套接字,
    #取而代之的是告诉浏览器你这次请求的数据有多少
    # new_socket.close()

# 充当http server
def main():
    # 1. 创建监听套接字
    tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #此处可以加一句,资源立即释放的语句(略)

    # 2.绑定信息
    local_ip = ""
    local_port = 5000
    local_addr = (local_ip, local_port)
    tcp_socket_server.bind(local_addr)

    # 3.设置为监听模式
    tcp_socket_server.listen(128)

    # 设置套接字是非堵塞的方式
    tcp_socket_server.setblocking(False)

    # 4.准备接客
    # 用单进程单线程,非堵塞实现并发
    client_socket_list = list()
    while True:
        print("等待连接...")
        time.sleep(1)
        # (1) accept等待连接,等待到连接后返回新创建的套接字和连接进来的客户端地址
        # accept默认为堵塞的,现在将监听socket设置为非堵塞的,故要用try except把下面句子包起来
        try:
            new_socket, client_addr = tcp_socket_server.accept()
        except Exception as ret:
            print("---no client--")
        else:
            print("--new coming--")
            new_socket.setblocking(False)  # 设置套接字为非堵塞方式
            client_socket_list.append(new_socket)
        
        for client_socket in client_socket_list:
            try: 
                recv_data = client_socket.recv(1024).decode('utf-8')
            except Exception as ret:
                print("----no data-----")
            else:
                # 当不产生异常时,有两种可能,
                # 第一种:收到了数据
                if recv_data:
                    # print("-------对方发来了数据------")
                    communicate(client_socket, recv_data)
                # 第二种,对方调用了close,导致了recv返回
                else:
                    client_socket.close()
                    client_socket_list.remove(client_socket)
                    print("客户端已经关闭")

    # 6.正常的程序流程要关闭套接字
    tcp_socket_server.close()


if __name__ == "__main__":
    main()

核心关键:

  1. http 协议版本1.1
  2. 在head中告诉浏览器请求的内容长度
  3. 收到浏览器close()后(即:收到数据,不报异常,且数据为空,意味着对方已经关闭了),再关闭为这个浏览器服务的套接字

你可能感兴趣的:(Python,网络编程,长连接,Python)