Skip to content

Latest commit

 

History

History
620 lines (484 loc) · 17.6 KB

53.web服务器简单实现.md

File metadata and controls

620 lines (484 loc) · 17.6 KB

WEB服务器简单实现

一.返回固定的数据的web服务器

# -*- coding:utf-8 -*-
import socket


def request_handler(sock):
    """处理请求"""
    # 接收消息
    request_data = sock.recv(1024).decode('utf-8')
    if not request_data:  # 没有消息
        print("client is disconnected。。。。")
        sock.close()
        return
    print("接收到消息为%s" % request_data)
    # 响应行
    response_line = 'HTTP/1.1 200 OK\r\n'
    # 响应头
    response_header = 'Server: PythonWebServer1.0\r\n'
    # 响应内容
    response_content = 'Hello World!'

    response_data = response_line + response_header + '\r\n' + response_content

    # 发送响应
    sock.send(response_data.encode())
    sock.close()


def main():
    """程序运行入口"""
    # tcp
    # 创建socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置套接字复用地址,设置当服务器先close,即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时,可以立即绑定端口
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 绑定ip地址和端口
    sock.bind(("", 8080))
    # 设置被动套接字
    sock.listen(128)
    # 等待连接
    while True:
        client_socket, client_add = sock.accept()
        # 处理请求
        request_handler(client_socket)


if __name__ == '__main__':
    main()

二.返回固定页面的数据

# -*- coding:utf-8 -*-
import re
import socket


def request_handle(sock):
    """处理数据"""
    request_data = sock.recv(1024).decode('utf-8')
    if not request_data:  # 没有消息
        print("client is disconnected。。。。")
        sock.close()
        return
    request_header_lines = request_data.splitlines()  # 切割请求头
    # 打印测试
    for request_header_line in request_header_lines:
        print(request_header_line)

    http_request_line = request_header_lines[0]
    # 正则切割出请求资源的文件名,eg:GET / HTTP/1.1
    request_file_name = re.match('[^/]+(/[^ ]*)', http_request_line).group(1)
    print(request_file_name)

    # 如果没有指定访问哪个页面。例如index.html
    # GET / HTTP/1.1
    if request_file_name == '/':
        request_file_name = DOCUMENTS_ROOT + "/index.html"
    else:
        request_file_name = DOCUMENTS_ROOT + request_file_name
    print(request_file_name)

    # 读取对应文件并返回
    try:
        f = open(request_file_name, 'rb')
    except IOError:
        # 404表示没有这个界面
        # 响应行
        response_header = 'HTTP/1.1 404  not found\r\n'

        # 响应头
        response_header += 'Server: PythonWebServer1.0\r\n'

        # 空行
        response_header += '\r\n'

        # 响应内容
        response_content = '====sorry ,file not found===='.encode('utf-8')

    else:
        # 404表示没有这个界面
        # 响应行
        response_header = 'HTTP/1.1 200 OK\r\n'

        # 响应头
        response_header += 'Server: PythonWebServer1.0\r\n'

        # 空行
        response_header += '\r\n'

        # 响应内容
        response_content = f.read()
        f.close()
    finally:
        # 拼接发送
        print(response_header)
        response_data = (response_header.encode('utf-8')) + response_content
        sock.send(response_data)
        sock.close()


def main():
    """入口"""
    # 1.创建socket套接字
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.设置套接字复用
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 3.设置端口号和ip
    sock.bind(('', 8080))
    # 4.开启监听模式
    sock.listen(128)
    while True:
        # 5.等待客户端连接
        client_sock, client_ip = sock.accept()
        # 处理请求
        request_handle(client_sock)


# 这里配置服务器
DOCUMENTS_ROOT = "./html/html"  # 全局函数

if __name__ == '__main__':
    main()

三.封装成类

# -*- coding:utf-8 -*-
# 封装成类
import re
import socket


class WsgiServer(object):
    def __init__(self, server_address):
        # 1.创建套接字
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2.允许立即使用上次绑定的port
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 3.绑定ip地址和端口
        self.sock.bind(server_address)
        # 4.设置被动套接字,并制定队列的长度
        self.sock.listen(128)

    def server_forever(self):
        """循环运行web服务器,等待客户端的链接并为客户端服务"""
        while True:
            client_sock, client_ip = self.sock.accept()
            self.request_handler(client_sock)

    def request_handler(self, sock):
        """处理请求"""
        # 接收网络请求消息
        request_data = sock.recv(1024).decode('utf-8')

        # 判断消息是否有数据
        if not request_data:  # 没有消息
            print("client is disconnected。。。。")
            sock.close()
            return

        # 切割请求头
        request_header_lines = request_data.splitlines()

        # 打印测试
        for request_header_line in request_header_lines:
            print(request_header_line)

        # 获取请求行行信息:因为它包含请求的资源
        http_request_line = request_header_lines[0]

        # 正则切割出请求资源的文件名,eg:GET / HTTP/1.1
        request_file_name = re.match('[^/]+(/[^ ]*)', http_request_line).group(1)
        print(request_file_name)

        # 如果没有指定访问哪个页面。例如index.html
        # GET / HTTP/1.1
        if request_file_name == '/':
            request_file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            request_file_name = DOCUMENTS_ROOT + request_file_name
        print(request_file_name)

        # 读取对应文件并返回
        try:
            f = open(request_file_name, 'rb')
        except IOError:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 404  not found\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = '====sorry ,file not found===='.encode('utf-8')

        else:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 200 OK\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = f.read()
            f.close()
        finally:
            # 拼接发送
            print(response_header)
            response_data = (response_header.encode('utf-8')) + response_content
            sock.send(response_data)
            sock.close()


# 设置服务器端口和ip
SERVER_ADDR = ("", 8080)
# 设置静态资源路径
DOCUMENTS_ROOT = './html/html'


def main():
    """程序运行入口"""
    httpd = WsgiServer(SERVER_ADDR)
    httpd.server_forever()


if __name__ == '__main__':
    main()

3.1.多进程分流

# -*- coding:utf-8 -*-
# 封装成类
import re
import socket
import multiprocessing


class WsgiServer(object):
    def __init__(self, server_address):
        # 1.创建套接字
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2.允许立即使用上次绑定的port
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 3.绑定ip地址和端口
        self.sock.bind(server_address)
        # 4.设置被动套接字,并制定队列的长度
        self.sock.listen(128)

    def server_forever(self):
        """循环运行web服务器,等待客户端的链接并为客户端服务"""
        while True:
            client_sock, client_ip = self.sock.accept()
            new_process = multiprocessing.Process(target=self.request_handler, args=(client_sock,))
            new_process.start()

            # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
            client_sock.close()

    def request_handler(self, sock):
        """处理请求"""
        # 接收网络请求消息
        request_data = sock.recv(1024).decode('utf-8')

        # 判断消息是否有数据
        if not request_data:  # 没有消息
            print("client is disconnected。。。。")
            sock.close()
            return

        # 切割请求头
        request_header_lines = request_data.splitlines()

        # 打印测试
        for request_header_line in request_header_lines:
            print(request_header_line)

        # 获取请求行行信息:因为它包含请求的资源
        http_request_line = request_header_lines[0]

        # 正则切割出请求资源的文件名,eg:GET / HTTP/1.1
        request_file_name = re.match('[^/]+(/[^ ]*)', http_request_line).group(1)
        print(request_file_name)

        # 如果没有指定访问哪个页面。例如index.html
        # GET / HTTP/1.1
        if request_file_name == '/':
            request_file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            request_file_name = DOCUMENTS_ROOT + request_file_name
        print(request_file_name)

        # 读取对应文件并返回
        try:
            f = open(request_file_name, 'rb')
        except IOError:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 404  not found\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = '====sorry ,file not found===='.encode('utf-8')

        else:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 200 OK\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = f.read()
            f.close()
        finally:
            # 拼接发送
            print(response_header)
            response_data = (response_header.encode('utf-8')) + response_content
            sock.send(response_data)
            sock.close()


# 设置服务器端口和ip
SERVER_ADDR = ("", 8080)
# 设置静态资源路径
DOCUMENTS_ROOT = './html/html'


def main():
    """程序运行入口"""
    httpd = WsgiServer(SERVER_ADDR)
    httpd.server_forever()


if __name__ == '__main__':
    main()

3.2.多线程分流

# -*- coding:utf-8 -*-
# 封装成类
import re
import socket
import threading


class WsgiServer(object):
    def __init__(self, server_address):
        # 1.创建套接字
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2.允许立即使用上次绑定的port
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 3.绑定ip地址和端口
        self.sock.bind(server_address)
        # 4.设置被动套接字,并制定队列的长度
        self.sock.listen(128)

    def server_forever(self):
        """循环运行web服务器,等待客户端的链接并为客户端服务"""
        while True:
            client_sock, client_ip = self.sock.accept()
            new_tread = threading.Thread(target=self.request_handler, args=(client_sock,))
            new_tread.start()

            # 因为线程是共享同一个套接字,所以主线程不能关闭,否则子线程就不能再使用这个套接字了
            # client_sock.close()

    def request_handler(self, sock):
        """处理请求"""
        # 接收网络请求消息
        request_data = sock.recv(1024).decode('utf-8')

        # 判断消息是否有数据
        if not request_data:  # 没有消息
            print("client is disconnected。。。。")
            sock.close()
            return

        # 切割请求头
        request_header_lines = request_data.splitlines()

        # 打印测试
        for request_header_line in request_header_lines:
            print(request_header_line)

        # 获取请求行行信息:因为它包含请求的资源
        http_request_line = request_header_lines[0]

        # 正则切割出请求资源的文件名,eg:GET / HTTP/1.1
        request_file_name = re.match('[^/]+(/[^ ]*)', http_request_line).group(1)
        print(request_file_name)

        # 如果没有指定访问哪个页面。例如index.html
        # GET / HTTP/1.1
        if request_file_name == '/':
            request_file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            request_file_name = DOCUMENTS_ROOT + request_file_name
        print(request_file_name)

        # 读取对应文件并返回
        try:
            f = open(request_file_name, 'rb')
        except IOError:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 404  not found\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = '====sorry ,file not found===='.encode('utf-8')

        else:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 200 OK\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = f.read()
            f.close()
        finally:
            # 拼接发送
            print(response_header)
            response_data = (response_header.encode('utf-8')) + response_content
            sock.send(response_data)
            sock.close()


# 设置服务器端口和ip
SERVER_ADDR = ("", 8080)
# 设置静态资源路径
DOCUMENTS_ROOT = './html/html'


def main():
    """程序运行入口"""
    httpd = WsgiServer(SERVER_ADDR)
    httpd.server_forever()


if __name__ == '__main__':
    main()

3.3.协程分流

# -*- coding:utf-8 -*-
# 封装成类
import re
import socket
import gevent
from gevent import monkey

# 打补丁
monkey.patch_all()  # 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块


class WsgiServer(object):
    def __init__(self, server_address):
        # 1.创建套接字
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2.允许立即使用上次绑定的port
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 3.绑定ip地址和端口
        self.sock.bind(server_address)
        # 4.设置被动套接字,并制定队列的长度
        self.sock.listen(128)

    def server_forever(self):
        """循环运行web服务器,等待客户端的链接并为客户端服务"""
        while True:
            client_sock, client_ip = self.sock.accept()
            gevent.spawn(self.request_handler, client_sock)  # 创建一个协程准备运行它

    def request_handler(self, sock):
        """处理请求"""
        # 接收网络请求消息
        request_data = sock.recv(1024).decode('utf-8')

        # 判断消息是否有数据
        if not request_data:  # 没有消息
            print("client is disconnected。。。。")
            sock.close()
            return

        # 切割请求头
        request_header_lines = request_data.splitlines()

        # 打印测试
        for request_header_line in request_header_lines:
            print(request_header_line)

        # 获取请求行行信息:因为它包含请求的资源
        http_request_line = request_header_lines[0]

        # 正则切割出请求资源的文件名,eg:GET / HTTP/1.1
        request_file_name = re.match('[^/]+(/[^ ]*)', http_request_line).group(1)
        print(request_file_name)

        # 如果没有指定访问哪个页面。例如index.html
        # GET / HTTP/1.1
        if request_file_name == '/':
            request_file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            request_file_name = DOCUMENTS_ROOT + request_file_name
        print(request_file_name)

        # 读取对应文件并返回
        try:
            f = open(request_file_name, 'rb')
        except IOError:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 404  not found\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = '====sorry ,file not found===='.encode('utf-8')

        else:
            # 404表示没有这个界面
            # 响应行
            response_header = 'HTTP/1.1 200 OK\r\n'

            # 响应头
            response_header += 'Server: PythonWebServer1.0\r\n'

            # 空行
            response_header += '\r\n'

            # 响应内容
            response_content = f.read()
            f.close()
        finally:
            # 拼接发送
            print(response_header)
            response_data = (response_header.encode('utf-8')) + response_content
            sock.send(response_data)
            sock.close()


# 设置服务器端口和ip
SERVER_ADDR = ("", 8080)
# 设置静态资源路径
DOCUMENTS_ROOT = './html/html'


def main():
    """程序运行入口"""
    httpd = WsgiServer(SERVER_ADDR)
    httpd.server_forever()


if __name__ == '__main__':
    main()