http协议的简介

1.http协议的简介
HTTP,HyperText Transfer Protocol。超文本传输协议,是互联网上应用最为广泛的一种网络协议。基于TCP的协议,HTTP是一个客户端和服务器端请求和应答的标准
2.TCP三次握手过程
http协议的简介
SYN是请求同步的意思,synchronize(同步)的缩写
ACK是确认同步的意思,acknowledgement(确认)的缩写
TCP是主机对主机层的传输控制协议,提供可靠的连接服务
TCP的三次握手
第一次握手:*(客户端:服务器在吗?)
建立连接时,客户端A发生SYN包(SYN=j)到服务器B
并进入SYN_SEND状态,等待服务器B确认
第二次握手:*(服务器:收到了,在的,)
服务器B收到SYN包,必须确认客户A的SYN,ACK=j+1
同时自己也发送一个SYN包,SYN=k
即,SYN+ACK包,此时服务器进入SYN_RECV状态
第三次握手:*(客户端:我要发数据了)
客户端A收到服务器B的SYN+ACK包
向服务器B发送确认包ACK(ACK=k+1)
此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手

3.TCP四次挥手过程
http协议的简介
客户端A发送一个FIN.用来关闭客户A到服务器B的数据传送(报文段4)(客户端:我要关了)
服务器B收到这个FIN. 它发回一个ACK,确认序号为收到的序号+1(报文段5)。和SYN一样,一个FIN将占用一个序号(服务端:好的,收到)
服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)(服务端:我也要关了)
客户端A发回ACK报文确认,并将确认序号设置为序号加1(报文段7)(客户端:好的,收到)
4.服务器如何复用同一个端口而不会提示端口被占用
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
允许地址重用
5.str.splitlines()按照行(’\r’, ‘\n’, ‘\r\n’)分隔,返回一个包含各行作为元素的列表

补充:
在response_header中加入
Content-Type: text/html; charset=utf-8 # 让浏览器按照utf-8格式解码

Content-Length:len(response_body) # 在单进程.单线程非堵塞长链接中使用,表示发送 body的长度,告诉浏览器数据已发送完毕,此时不用再关闭套接字
套接字变为非堵塞:
tcp_service_socket.setblocking(False)

单线程-单线程-非堵塞长链接代码:
import socket, re


def service_client(new_cs, request):
    """为浏览器发送数据"""
    request_lists = request.splitlines()
    if request_lists:
        ret = re.match(r"[^/]+(/[^ ]*)", request_lists[0])
        file_name = ""
        if ret:
            file_name = ret.group(1)
            if file_name == "/":
                file_name = "/index.html"
        try:
            f = open("./html" + file_name, "rb")
            print("正在准备为浏览器发送请求数据。。。")
        except:
            response_body = "-------请求没有被找到------"
            response_heard = "HTTP/1.1 404 NOT FOUND\r\n"
            response_heard += "Content-Type: text/html; charset=utf-8\r\n"
            response_heard += "Content-length:%d\r\n" % len(response_body)
            response_heard += "\r\n"
            response = response_body + response_heard             
            new_cs.send(response.encode("utf-8"))
        else:
            response_body = f.read()
            f.close()
            response_heard = "HTTP/1.1 200 OK\r\n"
            response_heard += "Content-Type: text/html; charset=utf-8\r\n"
            response_heard += "Content-Length:%d\r\n" % len(response_body)
            response_heard += "\r\n"
            response = response_heard.encode("utf-8")+ response_body
            new_cs.send(response)
            print("浏览器的请求数据已发送完毕!")


def main():
    # 1.创建套接字
    tcp_ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 2.绑定
    tcp_ss.bind(("", 9900))
    # 3.监听
    tcp_ss.listen(128)
    tcp_ss.setblocking(False)  # # 将监听套接字变为非堵塞

    client_socket_list = list()  # 创建一个存放浏览器服务的套接字列表
    while True:
        # 4.等待浏览器链接
        try:
            new_cs, c_addr = tcp_ss.accept()  # 等待浏览器的链接
        except Exception as ret:  # 因监听套接字为非堵塞套接字,当没有新浏览器连接时就会报错,处理报错信息
            pass
        else:
            new_cs.setblocking(False)  # 将为浏览器服务的套接字设置为非堵塞
            client_socket_list.append(new_cs)  # 将为浏览器服务的套接字加入到列表
        for client_socket in client_socket_list:  # 遍历为浏览器服务的套接字列表
            try:
                recv_data = client_socket.recv(1024).decode("utf-8")  # 接收浏览器发送的请求信息
            except:
                pass
            else:
                if recv_data:  # 接收到浏览器请求,则调用为浏览器发送请求的数据的函数
                    service_client(client_socket, recv_data)
                else:  # 表示浏览器已关闭
                    print("浏览器已退出")
                    client_socket.close()  # 关闭为浏览器服务的套接字
                    client_socket_list.remove(client_socket)  # 从列表中删除该套接字
    tcp_ss.close()


if __name__ == '__main__':
    main()