用python编写http server(1)-简单服务器

2020-04-14  本文已影响0人  shallows2013

对于wsgi来说,http server主要是处理了http请求,并提取了http请求的部分信息。http server最重要的还是通过多线程、多进程、协程等来处理并发请求。我们可以通过自己编写http server来熟悉整个请求处理流程,这里我们只关心tcp层。
先来个最简单的server

import socket


class SimpleServer():

    def __init__(self, host, port):
        self.connect = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
        # 处理TIME_WAIT
        self.connect.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.connect.bind((host, port))

    def serve_forever(self):
        self.connect.listen()
        while 1:
            try:
                conn, addr = self.connect.accept()
            except KeyboardInterrupt:
                break
            print('Connected by', addr)
            self.handle_request(conn)
        self.connect.close()

    def handle_request(self, conn):
        data = conn.recv(1024)
        print(data)
        conn.close()


if __name__ == "__main__":
    server = SimpleServer("localhost", 8000)
    server.serve_forever()

socket.socket可以创建多种类型的socket,这里主要测试ipv4的tcp。
可以看到创建完socket使用了setsockopt函数。这里有一个很有趣的现象是,如果去掉这函数,重新启动server时,会提示端口已绑定。这是因为tcp四次挥手时,首先关闭的一方有个TIME_WAIT2,设置了端口重用后就可以立刻重新启动了。

新开启个console:$ curl http://localhost:8000
可以看到server有了反应:

Connected by ('127.0.0.1', 59490)
b'GET / HTTP/1.1\r\nHost: localhost:8000\r\nUser-Agent: curl/7.58.0\r\nAccept: /\r\n\r\n'

为了模拟单进程单线程server的问题,我们在handle_request函数里增加个time.sleep(5)

    def handle_request(self, conn):
        data = conn.recv(1024)
        print(data)
        time.sleep(5)
        conn.close()

同时启动多个console,输入curl http://localhost:8000
可以看到server在第一个curl 处理完后才开始处理第二个curl ,期间第二个curl一直被阻塞。
这里必须明确的是,其实第二个curl的请求已经发送到server端了,但因为curl一直在等待server结束连接,所以一直在阻塞。
程序调用socket的write函数时,数据从程序缓冲区复制至socket发送缓冲区,在对端ack返回确认发送成功后就从write函数返回。
可以写个简单的client验证:

import socket


class SimpleClient():

    def __init__(self, host, port):
        self.connect = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
        self.connect.connect((host, port))

    def sendall(self, _bytes):
        self.connect.sendall(_bytes)
        self.connect.close()


if __name__ == "__main__":
    server = SimpleClient("localhost", 8000)
    server.sendall(b"hello,word")

先启动curl,然后启动client,可以发现client发送完数据后直接结束。

上一篇 下一篇

猜你喜欢

热点阅读