5 Python高级 Http协议 web

1 篇文章 0 订阅

HTML是一种用来定义网页的文本。http是在网络上传输·html的协议,用于浏览器和服务器的通信。

请求的发送都包含header和body。

浏览器--->服务器发送的请求如下:

GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ······

服务器--->浏览器回送的数据格式:header是告诉浏览器的,body是浏览器显示的内容。

HTTP/1.1 200 OK
Bdpagetype: 2
Bdqid: 0xbf90656d00086de4
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Fri, 29 Oct 2021 19:34:43 GMT
Expires: Fri, 29 Oct 2021 19:34:43 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=314; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=34883_34444_34919_34067_31660_34861_34584_34505_34916_34812_26350_34971_34867; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1635536083037869799413803644376716963300
X-Frame-Options: sameorigin
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked

#空一格 下面是<body>

实现简单的http协议(python充当服务器):

import socket

def send_data(new_socket):#为这个客户端返回数据
    #1 接受浏览器发送过来的请求 即http请求
    # GET / HTTP/1.1
    #.....
    request = new_socket.recv(1024)
    print(request)
    #2 返回http格式的数据 给浏览器
    #2.1 准备发送给浏览器的数据---header
    response = "HTTP/1.1 200 OK\r\n"
    response += '\r\n'#空行加body
    response += '<b1>hehehe</b1>'#2.2 发送给浏览器的数据body
    new_socket.send(response.encode('utf-8'))
    new_socket.close()

def main():
    #1 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #设置套接字 让套接字可以重复利用这个资源
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    #2 绑定
    tcp_server_socket.bind(('',7878))
    #3 默认套接字主动改为被动
    tcp_server_socket.listen(128)
    while True:
        #4 等待客户端连接
        new_socket,client_add = tcp_server_socket.accept()
        #5 为这个客户端服务
        send_data(new_socket)
    #6 关闭监听套接字
    tcp_server_socket.close()

if __name__ == "__main__":
    main()

返回浏览器需要的页面http服务器:

import socket
import re
def send_data(new_socket):#为这个客户端返回数据
    #1 接受浏览器发送过来的请求 即http请求
    # GET / HTTP/1.1
    #.....
    request = new_socket.recv(1024).decode('utf-8')
    print(request)
    request_line = request.splitlines()
    print(request_line)#返回的是一个列表
    ret = re.match(r'[^/]+(/[^ ]*)',request_line[0])#GET /index.html HTTP.1.1
    if ret:
        file_name = ret.group(1)#提取客户端想要的html文件名
        if file_name == '/':
           file_name = '/index.html'#如果没有输入页面 就默认index.html当作页面

    #2 返回http格式的数据 给浏览器

    try:
    #打开想要的文件
        f = open(file_name,'rb')#二进制的形式直接读取 后面就不用编码了
    except:#找不到文件就返回NOT FOUND
        response = 'HTTP/1.1 404  NOT FOUND\r\n'
        response += '\r\n'
        response += '-----not found---'
        new_socket.send(response.encode('utf-8'))
    else:#如果能打开文件就读取并发送
        html_contend= f.read()
        f.close()
        # 2.1 准备发送给浏览器的数据---header
        response = "HTTP/1.1 200 OK\r\n"
        response += '\r\n'  # 空行加body
        # response += '<b1>hehehe</b1>'#2.2 发送给浏览器的数据body
        new_socket.send(response.encode('utf-8'))
        new_socket.send(html_contend)  # 不能和response一起发过去 因为是二进制的

    new_socket.close()

def main():
    #1 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #设置套接字 让套接字可以重复利用这个资源
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    #2 绑定
    tcp_server_socket.bind(('',7878))
    #3 默认套接字主动改为被动
    tcp_server_socket.listen(128)
    while True:
        #4 等待客户端连接
        new_socket,client_add = tcp_server_socket.accept()
        
        #多进程
        #不用多进程的时候 需要等待服务完之后才能服务下一个
        #可以在接受连接的时候开一个进程 让进程里面的线程服务
        #accept还是一个 分配任务的时候多个人去做
        p = multiprocessing.Process(target=send_data,args=(new_socket,))#5 为这个客户端服务
        p.start()
        new_socket.close()#需要再次关闭 因为子进程会复制父进程的资源 在创建进程之前的父进程的所有变量都会被复制一份
        #所以子进程中也有new_socket 但是都标记的一个客户端
        

        ##5 为这个客户端服务
        #send_data(new_socket)
    #6 关闭监听套接字
    tcp_server_socket.close()

if __name__ == "__main__":
    main()

以上案例 如果使用多线程 因为是共享变量 所以不需要再次close。

协程实现:

import gevent
from gevent import monkey
monkey.patch_all()
def send_data(new_socket):
......
def main():
...
gevent.spawn(send_data,new_socket)
...

单进程 单线程 非堵塞实现并发:

import socket

def main():
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_socket.bind(('',7890))
    tcp_socket.listen(128)
    tcp_socket.setblocking(False)#设置套接字为非堵塞的方式
    client_socket_list = list()
    while True:#循环监听有没有客户端到来 和循环接受客户端有没有发送数据过来
        try:
            new_socket,new_add = tcp_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 = client_socket.recv(1024)
            except Exception as ret:
                print('这个客户端没有发送来数据')
            else:
                if recv_data:
                    #对方发送过来数据
                    print('客户端发送的数据')
                else:#对方调用了close 导致recv返回
                    client_socket_list.remove(client_socket)
                    client_socket.close()
if __name__ == '__main__':
    main()

长连接和短连接

短连接的操作步骤是:

建立连接--数据传输--关闭连接--建立连接--数据传输--关闭连接(就相当于每次请求数据前都要建立连接)

长连接操作步骤是:

建立连接--数据传输--(保持连接)--数据传输(可以多次数据传输)--关闭连接

单线程 进程 非堵塞 长连接的http服务器:长连接:浏览器不断开的话 就会一直用一个套接字发送请求 可以减少服务器资源。

import socket
import re

def send_data(new_socket,request):  # 为这个客户端返回数据
    # 1 接受浏览器发送过来的请求 即http请求
    # GET / HTTP/1.1
    # .....
    # request = new_socket.recv(1024).decode('utf-8')
    # print(request)
    request_line = request.splitlines()
    print(request_line)  # 返回的是一个列表
    ret = re.match(r'[^/]+(/[^ ]*)', request_line[0])  # GET /index.html HTTP.1.1
    if ret:
        file_name = ret.group(1)  # 提取客户端想要的html文件名
        if file_name == '/':
            file_name = '/index.html'  # 如果没有输入页面 就默认index.html当作页面

    # 2 返回http格式的数据 给浏览器
    try:
        # 打开想要的文件
        f = open(file_name, 'rb')  # 二进制的形式直接读取 后面就不用编码了
    except:  # 找不到文件就返回NOT FOUND
        response = 'HTTP/1.1 404  NOT FOUND\r\n'
        response += '\r\n'
        response += '-----not found---'
        new_socket.send(response.encode('utf-8'))
    else:  # 如果能打开文件就读取并发送
        html_contend = f.read()
        f.close()
        # 2.1 准备发送给浏览器的数据---header
        response_body = html_contend
        response_header = "HTTP/1.1 200 OK\r\n"
        response_header += 'Content-Length:%d\r\n' % len(response_body)#告诉浏览器body有多长
        response_header += '\r\n'  # 空行加body 
        response = response_header.encode('utf-8') + response_body #response_header是字符串 要变成二进制的才能和body相加
        # response += '<b1>hehehe</b1>'#2.2 发送给浏览器的数据body
        new_socket.send(response)

    #new_socket.close() 如果在这里close的话 还是短连接


def main():
    # 1 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置套接字 让套接字可以重复利用这个资源
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2 绑定
    tcp_server_socket.bind(('', 7878))
    # 3 默认套接字主动改为被动
    tcp_server_socket.listen(128)
    tcp_server_socket.setblocking(False)#套接字变为非堵塞
    client_lists = list()
    while True:
        # 4 等待客户端连接
        try:
            new_socket, client_add = tcp_server_socket.accept()
        except Exception as ret:
            pass
        else:
            new_socket.setblocking(False)#新的套接字变为非堵塞
            client_lists.append(new_socket)
        for client_list in client_lists:
            try:#套接字为非堵塞 没有数据到来 会有异常
                recv_data = client_list.recv(1024).decode('utf-8')
            except Exception as ret:
                pass
            else:#有数据到来
                if recv_data:
                    send_data(client_list,recv_data)#因为之前已经收过数据了 所以函数里不需要再次收取
                else:
                    client_list.close()
                    client_lists.remove(client_list)
    # 6 关闭监听套接字
    tcp_server_socket.close()

if __name__ == "__main__":
    main()

epoll实现服务器(Linux服务器里使用的方式):单进程 单线程 实现高并发

epoll就是linux里有一个特殊的内存,这个内存是kernel(内核)和操作系统共享的,在这个内存里面所有添加的 要监听的 要判断数据是否到来的套接字对应的文件描述符,我在检测的时候不是轮询(我们之前的用的非阻塞的方式)而是事件通知 我什么时候收到数据了 看一下这个内存中对应数据是谁的 这样就不用遍历套接字。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值