python实现多进程与多线程HTTP服务器,浏览器通过HTTP与之通信

因为网络间通信是基于TCP协议传输数据的,而服务器与浏览器之间通信是基于HTTP协议的,那么下面基于python实现一个多进程或多线程tcp服务器,浏览器可以基于http协议进行发送请求和解析。浏览器展示返回的一个标准的HTML网页,此外实现服务器解析客户端多次请求并且返回请求结果。即:客户端根据HTML里面的各种链接,再发送HTTP请求给服务器,拿到相应的图片、视频、Flash、JavaScript脚本、CSS等各种资源,最终显示出一个完整的页面。

1.代码实现多进程HTTP服务器与测试

如下所谓多进程HTTP服务器和之前的单进程区别就是,这里使用一个主进程接受accept()请求,但是使用多进程分别去解析,响应浏览器的请求。举个简单例子就是:之前是一个人接受请求,并且处理请求;现在是一个人负责接受请求,然后分发给不同人的人去处理请求。这样整体来说响应和处理的速度要快很多,这既是多进程多线程在这里的体现。因为之前如果使用单进程单线程一旦recv()函数堵塞时,这个通信都会堵塞,现在使用多线程与多进程则解决了这个堵塞问题。

import socket
import re
import multiprocessing
def service_client(new_socket):
    """为这个客户端返回数据"""
    # 1. 接收浏览器发送过来的请求 ,即http请求
    # GET / HTTP/1.1
    request = new_socket.recv(1024).decode("utf-8")
    # print(request)
    request_lines = request.splitlines()
    print("")
    print(">" * 20)
    print(request_lines)
    # GET /index.html HTTP/1.1
    # get post put del
    file_name = ""
    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"
    # 2. 返回http格式的数据,给浏览器
    try:
        f = open("./html" + file_name, "rb")
    except: #对于没有找到请求文件路径的返回结果
        response = "HTTP/1.1 404 NOT FOUND\r\n"
        response += "\r\n"
        response += "------file not found-----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()
        # 2.1 准备发送给浏览器的数据---header
        response = "HTTP/1.1 200 OK\r\n"
        response += "\r\n"
        # 2.2 准备发送给浏览器的数据---boy
        # response += "hahahhah"
        # 将response header发送给浏览器
        new_socket.send(response.encode("utf-8"))
        # 将response body发送给浏览器
        new_socket.send(html_content)
    # 关闭套接
    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(("", 7890))
    # 3. 变为监听套接字
    tcp_server_socket.listen(128)
    while True:
        # 4. 等待新客户端的链接
        new_socket, client_addr = tcp_server_socket.accept()
        # 5. 为这个客户端服务,这里启动多进程去处理服务器接收的请求
        p = multiprocessing.Process(target=service_client, args=(new_socket,))
        p.start()  #同样,子进程都是用start()方法启动
        new_socket.close()  #注意使用多进程以后这里多了一个close().之前单进程没有的。主进程和子进程都要关闭。
    # 关闭监听套接字
    tcp_server_socket.close()
if __name__ == "__main__":
    main()
'''浏览器请求后服务器打印显示的请求记录
D:\software\python3\python.exe D:/pythoyworkspace/file_demo/Class_Demo/pachong/bbb.py
>>>>>>>>>>>>>>>>>>>>
['GET / HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'Upgrade-Insecure-Requests: 1', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /classic.css HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: text/css,*/*;q=0.1', 'Referer: http://192.168.1.1:7890/', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /images/qt-logo.png HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: image/webp,image/apng,image/*,*/*;q=0.8', 'Referer: http://192.168.1.1:7890/', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /images/mainwindow-vertical-tabs.png HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: image/webp,image/apng,image/*,*/*;q=0.8', 'Referer: http://192.168.1.1:7890/qt4-3-intro.html', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
>>>>>>>>>>>>>>>>>>>>
['GET /images/mainwindow-custom-dock.png HTTP/1.1', 'Host: 192.168.1.1:7890', 'Connection: keep-alive', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36', 'Accept: image/webp,image/apng,image/*,*/*;q=0.8', 'Referer: http://192.168.1.1:7890/qt4-3-intro.html', 'Accept-Encoding: gzip, deflate', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8', '']
...........
'''

 

使用浏览器去访问服务器网址,浏览器成功解析,并且网页中的子链接也可以打卡,成功处理,完成响应。

 

下面基于实际开发,将上面的程序使用类进行封装,结果如下,实际访问功能和上面一样。

#coding=utf-8
import socket
import re
import multiprocessing
class WSGIServer(object):
    def __init__(self, server_address):
        # 创建一个tcp套接字
        self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 允许立即使用上次绑定的port
        self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定
        self.listen_socket.bind(server_address)
        # 变为被动,并制定队列的长度
        self.listen_socket.listen(128)
    def serve_forever(self):
        "循环运行web服务器,等待客户端的链接并为客户端服务"
        while True:
            # 等待新客户端到来
            client_socket, client_address = self.listen_socket.accept()
            print(client_address)  # for test
            new_process = multiprocessing.Process(target=self.handleRequest, args=(client_socket,))
            new_process.start()
            # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
            client_socket.close()
    def handleRequest(self, client_socket):
        "用一个新的进程,为一个客户端进行服务"
        recv_data = client_socket.recv(1024).decode('utf-8')
        print(recv_data)
        requestHeaderLines = recv_data.splitlines()
        for line in requestHeaderLines:
            print(line)
        request_line = requestHeaderLines[0]
        get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)
        print("file name is ===>%s" % get_file_name) # for test
        if get_file_name == "/":
            get_file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            get_file_name = DOCUMENTS_ROOT + get_file_name
        print("file name is ===2>%s" % get_file_name) # for test
        try:
            f = open(get_file_name, "rb")
        except IOError:
            response_header = "HTTP/1.1 404 not found\r\n"
            response_header += "\r\n"
            response_body = "====sorry ,file not found===="
        else:
            response_header = "HTTP/1.1 200 OK\r\n"
            response_header += "\r\n"
            response_body = f.read()
            f.close()
        finally:
            client_socket.send(response_header.encode('utf-8'))
            client_socket.send(response_body)
            client_socket.close()
# 设定服务器的端口
SERVER_ADDR = (HOST, PORT) = "", 8888
# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./html"
def main():
    httpd = WSGIServer(SERVER_ADDR)
    print("web Server: Serving HTTP on port %d ...\n" % PORT)
    httpd.serve_forever()
if __name__ == "__main__":
    main()

 

3.python通过多线程的方式实现http服务器

多进程改成多线程代码上改动的很少,只是将multiprocessing改成threading即可,如下:

#coding=utf-8
import socket
import re
import threading
class WSGIServer(object):
    def __init__(self, server_address):
        # 创建一个tcp套接字
        self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 允许立即使用上次绑定的port
        self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 绑定
        self.listen_socket.bind(server_address)
        # 变为被动,并制定队列的长度
        self.listen_socket.listen(128)
    def serve_forever(self):
        "循环运行web服务器,等待客户端的链接并为客户端服务"
        while True:
            # 等待新客户端到来
            client_socket, client_address = self.listen_socket.accept()
            print(client_address)
            new_process = threading.Thread(target=self.handleRequest, args=(client_socket,))
            new_process.start()
            # 因为线程是共享同一个套接字,所以主线程不能关闭,否则子线程就不能再使用这个套接字了
            # client_socket.close()
    def handleRequest(self, client_socket):
        "用一个新的进程,为一个客户端进行服务"
        recv_data = client_socket.recv(1024).decode('utf-8')
        print(recv_data)
        requestHeaderLines = recv_data.splitlines()
        for line in requestHeaderLines:
            print(line)
        request_line = requestHeaderLines[0]
        get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)
        print("file name is ===>%s" % get_file_name) # for test
        if get_file_name == "/":
            get_file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            get_file_name = DOCUMENTS_ROOT + get_file_name
        print("file name is ===2>%s" % get_file_name) # for test
        try:
            f = open(get_file_name, "rb")
        except IOError:
            response_header = "HTTP/1.1 404 not found\r\n"
            response_header += "\r\n"
            response_body = "====sorry ,file not found===="
        else:
            response_header = "HTTP/1.1 200 OK\r\n"
            response_header += "\r\n"
            response_body = f.read()
            f.close()
        finally:
            client_socket.send(response_header.encode('utf-8'))
            client_socket.send(response_body)
            client_socket.close()
# 设定服务器的端口
SERVER_ADDR = (HOST, PORT) = "", 8888
# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./html"
def main():
    httpd = WSGIServer(SERVER_ADDR)
    print("web Server: Serving HTTP on port %d ...\n" % PORT)
    httpd.serve_forever()
if __name__ == "__main__":
    main()

 

同样使用浏览器可以访问这个多线程服务器,功能上来说,也是差不多。多线程同时处理请求。

统一声明:关于原创博客内容,可能会有部分内容参考自互联网,如有原创链接会声明引用;如找不到原创链接,在此声明如有侵权请联系删除哈。关于转载博客,如有原创链接会声明;如找不到原创链接,在此声明如有侵权请联系删除哈

版权申明:本站文章均来自网络,如有侵权,请联系01056159998 邮箱:itboby@foxmail.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

猜你还会喜欢下面的内容

    无相关信息

中国领先的互联网域名及云服务提供商

为您提供域名,比特币,P2P,大数据,云计算,虚拟主机,域名交易最新资讯报道

域名注册云服务器