美文网首页
python 简单的web静态服务器

python 简单的web静态服务器

作者: 全村滴希望 | 来源:发表于2019-12-16 17:20 被阅读0次

python 简单的web静态服务器

原理步骤:

客户端到服务器http请求基本原理

web静态服务器:显示固定页面

准备名为web_server的py文件

#coding=utf-8
import socket
 
 
def handle_client(client_socket):
    "为一个客户端进行服务"
    
    # 1. 返回http格式的数据response给浏览器,分别准备header和body
    recv_data = client_socket.recv(1024).decode("utf-8")
    print(recv_data)
 
    # 2. 返回http格式的数据response给浏览器,分别准备header和body
    response_headers = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
    response_headers += "\r\n"  # 用一个空的行与body进行隔开
    response_body = "hello xinglu"
    response = response_headers + response_body
    
    # 3. 发送组织好的数据并关闭套接字
    client_socket.send(response.encode("utf-8"))
    client_socket.close()
 
 
def main():
    """作为程序的主控制入口"""

    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 3. 绑定ip端口
    tcp_server_socket.bind(("", 7788))

    # 4. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 5. 等待客户端的链接
        client_socket, client_addr = tcp_server_socket.accept()

        # 6.为客户端服务
        handle_client(client_socket)


if __name__ == "__main__":
    main()

然后运行py文件,用浏览器访问ip+端口即可(代码中绑定的端口为7788)

web静态服务器:显示需要的页面

准备名为web_server的py文件

import socket
import re


def handle_client(client_socket):
    """为一个客户端进行服务"""

    # 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
    recv_data = client_socket.recv(1024).decode("utf-8")
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)

    # 2, 获取浏览器需要访问的页面
    http_request_line = request_header_lines[0]
    file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
    print("client request file is: %s" % file_name)

    # 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
    if file_name == "/":
        file_name = DOCUMENTS_ROOT + "/index.html"
    else:
        file_name = DOCUMENTS_ROOT + file_name
    print("the path of the file is: %s" % file_name)
    print("-"*50)

    # 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
    response_header = None
    response_body = None
    try:
        f = open(file_name, "rb")
    except IOError:
        # 读取页面失败,即没有这个页面,返回404
        response_header = "HTTP/1.1 404 not found\r\n"
        response_header += "\r\n"
        response_body = "ERROR 404: file not found"
    else:
        response_header = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
        response_header += "\r\n"  # 用一个空的行与body隔开
        response_body = f.read()
        f.close()
    finally:
        # 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
        response = response_header.encode("utf-8") + response_body
        client_socket.send(response)
        client_socket.close()


def main():
    """作为程序的主控制入口"""

    # 1. 创建套接字
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # 3. 绑定ip端口
    tcp_server_socket.bind(("", 7788))

    # 4. 变为监听套接字
    tcp_server_socket.listen(128)

    while True:
        # 5. 等待客户端的链接
        client_socket, client_addr = tcp_server_socket.accept()

        # 6.为客户端服务
        handle_client(client_socket)


DOCUMENTS_ROOT = "./html"


if __name__ == "__main__":
    main()

再准备如下名为index.html的页面,路径为py文件同级目录的html文件夹下

.
├── web_server.py
├── html
└───── index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>我眼中的世界</title>
<script src="http://www.jq22.com/jquery/jquery-1.10.2.js"></script>
<style>
*, *:before, *:after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  background: #15181A;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
}

.skw-pages {
  overflow: hidden;
  position: relative;
  height: 100vh;
}

.skw-page {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
}
.skw-page__half {
  position: absolute;
  top: 0;
  width: 50%;
  height: 100vh;
  -webkit-transition: -webkit-transform 1s;
          transition: transform 1s;
}
.skw-page__half--left {
  left: 0;
  -webkit-transform: translate3d(-32.4vh, 100%, 0);
          transform: translate3d(-32.4vh, 100%, 0);
}
.skw-page__half--right {
  left: 50%;
  -webkit-transform: translate3d(32.4vh, -100%, 0);
          transform: translate3d(32.4vh, -100%, 0);
}
.skw-page.active .skw-page__half {
  -webkit-transform: translate3d(0, 0, 0);
          transform: translate3d(0, 0, 0);
}
.skw-page__skewed {
  overflow: hidden;
  position: absolute;
  top: 0;
  width: 140%;
  height: 100%;
  -webkit-transform: skewX(-18deg);
      -ms-transform: skewX(-18deg);
          transform: skewX(-18deg);
  background: #000;
}
.skw-page__half--left .skw-page__skewed {
  left: -40%;
}
.skw-page__half--right .skw-page__skewed {
  right: -40%;
}
.skw-page__content {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
      -ms-flex-pack: center;
          justify-content: center;
  -webkit-flex-flow: column wrap;
      -ms-flex-flow: column wrap;
          flex-flow: column wrap;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  padding: 0 30%;
  color: #fff;
  -webkit-transform: skewX(18deg);
      -ms-transform: skewX(18deg);
          transform: skewX(18deg);
  -webkit-transition: -webkit-transform 1s, opacity 1s;
          transition: transform 1s, opacity 1s;
  background-size: cover;
}
.skw-page__half--left .skw-page__content {
  padding-left: 30%;
  padding-right: 30%;
  -webkit-transform-origin: 100% 0;
      -ms-transform-origin: 100% 0;
          transform-origin: 100% 0;
}
.skw-page__half--right .skw-page__content {
  padding-left: 30%;
  padding-right: 30%;
  -webkit-transform-origin: 0 100%;
      -ms-transform-origin: 0 100%;
          transform-origin: 0 100%;
}
.skw-page.inactive .skw-page__content {
  opacity: 0.5;
  -webkit-transform: skewX(18deg) scale(0.95);
      -ms-transform: skewX(18deg) scale(0.95);
          transform: skewX(18deg) scale(0.95);
}
.skw-page__heading {
  margin-bottom: 15px;
  text-transform: uppercase;
  font-size: 25px;
  text-align: center;
}
.skw-page__description {
  font-size: 18px;
  text-align: center;
}
.skw-page__link {
  color: #FFA0A0;
}
.skw-page-1 .skw-page__half--left .skw-page__content {
  background-image: url("https://i.loli.net/2019/12/15/24vOJfGh7wySXcC.jpg");
}
.skw-page-1 .skw-page__half--right .skw-page__content {
  background: #292929;
}
.skw-page-2 .skw-page__half--left .skw-page__content {
  background: #292929;
}
.skw-page-2 .skw-page__half--right .skw-page__content {
  background-image: url("https://i.loli.net/2019/12/15/aizHDLwV2JOo94P.jpg");
}
.skw-page-3 .skw-page__half--left .skw-page__content {
  background-image: url("https://i.loli.net/2019/12/15/yKu3NGwCBYqbUrh.jpg");
}
.skw-page-3 .skw-page__half--right .skw-page__content {
  background: #292929;
}
.skw-page-4 .skw-page__half--left .skw-page__content {
  background: #292929;
}
.skw-page-4 .skw-page__half--right .skw-page__content {
  background-image: url("https://i.loli.net/2019/12/15/A3UdjqcGDMTFtnK.jpg");
}
.skw-page-5 .skw-page__half--left .skw-page__content {
  background-image: url("https://i.loli.net/2019/12/15/A1s6zc29DbNO8Ho.jpg");
}
.skw-page-5 .skw-page__half--right .skw-page__content {
  background: #292929;
}
.skw-page-6 .skw-page__half--left .skw-page__content {
  background: #292929;
}
.skw-page-6 .skw-page__half--right .skw-page__content {
  background-image: url("https://i.loli.net/2019/12/15/8mYk1NHjefGMWzs.jpg");
}
.skw-page-7 .skw-page__half--left .skw-page__content {
  background-image: url("https://i.loli.net/2019/12/15/uYb41PdfwyDpnMl.jpg");
}
.skw-page-7 .skw-page__half--right .skw-page__content {
  background: #292929;
}
</style>
</head>
<body>
<div class="skw-pages">
  <div class="skw-page skw-page-1 active">
    <div class="skw-page__half skw-page__half--left">
      <div class="skw-page__skewed">
        <div class="skw-page__content"></div>
      </div>
    </div>
    <div class="skw-page__half skw-page__half--right">
      <div class="skw-page__skewed">
        <div class="skw-page__content">
          <h2 class="skw-page__heading">这是我眼中的世界</h2>
          <p class="skw-page__description">为了方便观看,请把窗口宽度缩小一半,然后按 ⬇ 键</p>
        </div>
      </div>
    </div>
  </div>
  <div class="skw-page skw-page-2">
    <div class="skw-page__half skw-page__half--left">
      <div class="skw-page__skewed">
        <div class="skw-page__content">
          <h2 class="skw-page__heading">你在路上随便碰到的一个路人</h2>
          <p class="skw-page__description">都是别人做梦都想见到的人 ⬇ </p>
        </div>
      </div>
    </div>
    <div class="skw-page__half skw-page__half--right">
      <div class="skw-page__skewed">
        <div class="skw-page__content"></div>
      </div>
    </div>
  </div>

  <div class="skw-page skw-page-3">
    <div class="skw-page__half skw-page__half--left">
      <div class="skw-page__skewed">
        <div class="skw-page__content"></div>
      </div>
    </div>
    <div class="skw-page__half skw-page__half--right">
      <div class="skw-page__skewed">
        <div class="skw-page__content">
          <h2 class="skw-page__heading">你的眼中 有数不尽的星光</h2>
          <p class="skw-page__description">嘴角上扬 星光便散落心上 ⬇</p>
        </div>
      </div>
    </div>
  </div>
  <div class="skw-page skw-page-4">
    <div class="skw-page__half skw-page__half--left">
      <div class="skw-page__skewed">
        <div class="skw-page__content">
          <h2 class="skw-page__heading">山野千里</h2>
          <p class="skw-page__description">想见你 ⬇</p>
        </div>
      </div>
    </div>
    <div class="skw-page__half skw-page__half--right">
      <div class="skw-page__skewed">
        <div class="skw-page__content"></div>
      </div>
    </div>
  </div>
  <div class="skw-page skw-page-5">
    <div class="skw-page__half skw-page__half--left">
      <div class="skw-page__skewed">
        <div class="skw-page__content"></div>
      </div>
    </div>
    <div class="skw-page__half skw-page__half--right">
      <div class="skw-page__skewed">
        <div class="skw-page__content">
          <h2 class="skw-page__heading">见到你的每一刻</h2>
          <p class="skw-page__description">就像每周五的下午 ⬇</p>
        </div>
      </div>
    </div>
  </div>
  <div class="skw-page skw-page-6">
    <div class="skw-page__half skw-page__half--left">
      <div class="skw-page__skewed">
        <div class="skw-page__content">
          <h2 class="skw-page__heading">从遇见你开始</h2>
          <p class="skw-page__description">凛冬散尽 星河长明 ⬇</p>
        </div>
      </div>
    </div>
    <div class="skw-page__half skw-page__half--right">
      <div class="skw-page__skewed">
        <div class="skw-page__content"></div>
      </div>
    </div>
  </div>
<div class="skw-page skw-page-7">
    <div class="skw-page__half skw-page__half--right">
      <div class="skw-page__skewed">
        <div class="skw-page__content">
          <h2 class="skw-page__heading">我希望与你的一生</h2>
          <p class="skw-page__description">所求皆如愿 所行化坦途 多喜乐 长安宁 ⬇</p>
        </div>
      </div>
    </div>
    <div class="skw-page__half skw-page__half--left">
      <div class="skw-page__skewed">
        <div class="skw-page__content"></div>
      </div>
    </div>
  </div>

</div>




<script>
$(document).ready(function() {

  var curPage = 1;
  var numOfPages = $(".skw-page").length;
  var animTime = 1000;
  var scrolling = false;
  var pgPrefix = ".skw-page-";

  function pagination() {
    scrolling = true;

    $(pgPrefix + curPage).removeClass("inactive").addClass("active");
    $(pgPrefix + (curPage - 1)).addClass("inactive");
    $(pgPrefix + (curPage + 1)).removeClass("active");

    setTimeout(function() {
      scrolling = false;
    }, animTime);
  };

  function navigateUp() {
    if (curPage === 1) return;
    curPage--;
    pagination();
  };

  function navigateDown() {
    if (curPage === numOfPages) return;
    curPage++;
    pagination();
  };

  $(document).on("mousewheel DOMMouseScroll", function(e) {
    if (scrolling) return;
    if (e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) {
      navigateUp();
    } else {
      navigateDown();
    }
  });

  $(document).on("keydown", function(e) {
    if (scrolling) return;
    if (e.which === 38) {
      navigateUp();
    } else if (e.which === 40) {
      navigateDown();
    }
  });

});</script>

</body>
</html>

然后运行py文件,用浏览器访问ip+端口即可(代码中绑定的端口为7788)

将python代码修改为面向对象的形式

import socket
import re


class WSGIServer(object):
    """web静态服务器:返回需要的页面"""

    def __init__(self, server_addr):
        """初始化方法:创建并配置服务器套接字"""
        
        # 1. 创建套接字
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 3. 绑定ip端口
        self.server_socket.bind(server_addr)

        # 4. 变为监听套接字
        self.server_socket.listen(128)

    def accept_client(self):
        """实例方法:循环等待服务器接收客户端的连接"""

        while True:
            # 5. 等待客户端的连接
            client_socket, client_addr = self.server_socket.accept()

            # 6.为客户端服务
            self.handle_client(client_socket)

    @staticmethod
    def handle_client(client_socket):
        """静态方法:为一个客户端进行服务"""

        # 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
        recv_data = client_socket.recv(1024).decode("utf-8")
        request_header_lines = recv_data.splitlines()
        for line in request_header_lines:
            print(line)

        # 2, 获取浏览器需要访问的页面
        http_request_line = request_header_lines[0]
        file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
        print("client request file is: %s" % file_name)

        # 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
        if file_name == "/":
            file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            file_name = DOCUMENTS_ROOT + file_name
        print("the path of the file is: %s" % file_name)
        print("-" * 50)

        # 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
        response_header = None
        response_body = None
        try:
            f = open(file_name, "rb")
        except IOError:
            # 读取页面失败,即没有这个页面,返回404
            response_header = "HTTP/1.1 404 not found\r\n"
            response_header += "\r\n"
            response_body = "ERROR 404: file not found"
        else:
            response_header = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
            response_header += "\r\n"  # 用一个空的行与body隔开
            response_body = f.read()
            f.close()
        finally:
            # 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
            response = response_header.encode("utf-8") + response_body
            client_socket.send(response)
            client_socket.close()


def main():
    """作为程序的主控制入口"""

    httpd = WSGIServer(SERVER_ADDR)
    print("web Server: Serving HTTP on port %d ...\n" % PORT)
    httpd.accept_client()


DOCUMENTS_ROOT = "./html"
SERVER_ADDR = (HOST, PORT) = "", 7788

if __name__ == "__main__":
    main()

web静态服务器:单线程非阻塞

import socket
import re
import time


class WSGIServer(object):
    """web静态服务器:返回需要的页面"""

    def __init__(self, server_addr):
        """初始化方法:创建并配置服务器套接字"""

        # 1. 创建套接字
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 3. 绑定ip端口
        self.server_socket.bind(server_addr)

        # 4. 变为监听套接字
        self.server_socket.listen(128)

        self.server_socket.setblocking(False)
        self.client_socket_list = list()

    def accept_client(self):
        """实例方法:循环等待服务器接收客户端的连接"""

        while True:

            time.sleep(0.5)  # for test
            # 5. 等待客户端的连接
            try:
                client_socket, client_addr = self.server_socket.accept()
            except Exception as ret:
                print("客户端连接的Exception:", ret)
            else:
                client_socket.setblocking(False)
                self.client_socket_list.append(client_socket)

            for client_socket in self.client_socket_list:
                try:
                    recv_data = client_socket.recv(1024).decode("utf-8")
                except Exception as ret:
                    print("客户端发送消息的Exception", ret)
                else:
                    if recv_data:
                        # 6.为客户端服务
                        self.handle_client(recv_data, client_socket)
                    else:
                        client_socket.close()
                        self.client_socket_list.remove(client_socket)

    @staticmethod
    def handle_client(recv_data, client_socket):
        """静态方法:为一个客户端进行服务"""

        # 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
        request_header_lines = recv_data.splitlines()
        for line in request_header_lines:
            print(line)

        # 2, 获取浏览器需要访问的页面
        http_request_line = request_header_lines[0]
        file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
        print("client request file is: %s" % file_name)

        # 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
        if file_name == "/":
            file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            file_name = DOCUMENTS_ROOT + file_name
        print("the path of the file is: %s" % file_name)
        print("-" * 50)

        # 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
        response_header = None
        response_body = None
        try:
            f = open(file_name, "rb")
        except IOError:
            # 读取页面失败,即没有这个页面,返回404
            response_header = "HTTP/1.1 404 not found\r\n"
            response_header += "\r\n"
            response_body = "ERROR 404: file not found"
        else:
            response_header = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
            response_header += "\r\n"  # 用一个空的行与body隔开
            response_body = f.read()
            f.close()
        finally:
            # 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
            response = response_header.encode("utf-8") + response_body
            client_socket.send(response)
            client_socket.close()


def main():
    """作为程序的主控制入口"""

    httpd = WSGIServer(SERVER_ADDR)
    print("web Server: Serving HTTP on port %d ...\n" % PORT)
    httpd.accept_client()


DOCUMENTS_ROOT = "./html"
SERVER_ADDR = (HOST, PORT) = "", 7788

if __name__ == "__main__":
    main()

web静态服务器:多进程、多线程、协程

多线程,变更实例方法代码:

import multiprocessing


class WSGIServer(object):
    def accept_client(self):
        """实例方法:循环等待服务器接收客户端的连接"""

        while True:
            # 5. 等待客户端的连接
            client_socket, client_addr = self.server_socket.accept()

            # 6.使用多进程为客户端服务
            new_process = multiprocessing.Process(target=self.handle_client, args=(client_socket, ))
            new_process.start()

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

多线程, 变更实例方法代码:

import threading


class WSGIServer(object):
    def accept_client(self):
        """实例方法:循环等待服务器接收客户端的连接"""

        while True:
            # 5. 等待客户端的连接
            client_socket, client_addr = self.server_socket.accept()

            # 6.使用多线程为客户端服务
            new_thread = threading.Thread(target=self.handle_client, args=(client_socket, ))
            new_thread.start()

            # 如果使用线程则不能关闭套接字,因为线程不会复制进程的资源
            # client_socket.close()

协程,变更实例方法代码:

import gevent
from gevent import monkey

monkey.patch_all()

class WSGIServer(object):
    def accept_client(self):
        """实例方法:循环等待服务器接收客户端的连接"""

        while True:
            # 5. 等待客户端的连接
            client_socket, client_addr = self.server_socket.accept()

            # 6.使用协程为客户端服务
            gevent.spawn(self.handle_client, client_socket)

长连接、短连接

在HTTP/1.0中,默认使用的是短连接。

也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。

如果客户端访问的某个HTML或其他类型的Web资源,如JavaScript文件、图像文件、CSS文件等。

当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话。

但从HTTP/1.1起,默认使用长连接,用以保持连接特性。

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭。

如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。

Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。

短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。

但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。

长连接和短连接的产生在于client和server采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。

上述代码都属于短连接,因为每次调用handle_client静态方法都会执行client_socket.close()关闭套接字从而结束会话。因此想要改为长连接只需要注释掉client_socket.close() 并且再头文件组织里加入Content-Length来标记body的长度,告诉服务器发完了本次数据,但不需要关闭连接。

# 将以上代码的header组织部分替换如下,然后注释掉client_socket.close() 
        # 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
        response_header = None
        response_body = None
        try:
            f = open(file_name, "rb")
        except IOError:
            # 读取页面失败,即没有这个页面,返回404
            response_body = "ERROR 404: file not found"
            response_header = "HTTP/1.1 404 not found\r\n"
            response_header += "Content-Length:%d\r\n" % len(response_body)  # 用一个空的行与body隔开
            response_header += "\r\n"
        else:
            response_body = f.read()
            f.close()
            response_header = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
            response_header += "Content-Length: %d\r\n" % len(response_body)  # 用一个空的行与body隔开
            response_header += "\r\n"  # 用一个空的行与body隔开
        finally:
            # 因为头信息在组织的时候,是按照字符串组织的,需要进行编码
            response = response_header.encode("utf-8") + response_body
            client_socket.send(response)
            # client_socket.close()

epoll模式

import socket
import re
import select


class WSGIServer(object):
    """web静态服务器:返回需要的页面"""

    def __init__(self, server_addr):
        """初始化方法:创建并配置服务器套接字"""

        # 1. 创建套接字
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # 2. 设置套接字4次挥手关闭连接后,不用等待TIME_WAIT,端口立刻重用
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 3. 绑定ip端口
        self.server_socket.bind(server_addr)

        # 4. 变为监听套接字
        self.server_socket.listen(128)

        # 5. 创建一个epoll对象
        self.epoll = select.epoll()

        # 6. 将tcp服务器套接字的文件描述符fd(fileno)加入到epoll中进行监听
        self.epoll.register(self.server_socket.fileno(), select.EPOLLIN|select.EPOLLET)

        # 7. 创建添加的fd对应的套接字的字典
        self.fd_socket = dict()

    def accept_client(self):
        """实例方法:循环等待服务器接收客户端的连接"""

        while True:
            # 7. epoll 进行 fd 扫描的地方 -- 未指定超时时间则为阻塞等待
            epoll_list = self.epoll.poll()

            # 对事件进行判断
            for fd, event in epoll_list:
                # 如果是服务器套接字可以收数据, 那么意味着可以进行accept
                if fd == self.server_socket.fileno():
                    client_socket, client_addr = self.server_socket.accept()
                    # 向着epoll中注册 连接 socket 的可读事件
                    self.epoll.register(client_socket.fileno(), select.EPOLLIN|select.EPOLLET)
                    # 记录这个信息
                    self.fd_socket[client_socket.fileno()] = client_socket
                # 接收到数据
                elif event == select.EPOLLIN:
                    recv_data = self.fd_socket[fd].recv(1024).decode("utf-8")
                    if recv_data:
                        self.handle_client(recv_data, self.fd_socket[fd])
                    else:
                        # 在epoll中注销客户端的信息
                        self.epoll.unregister(fd)
                        # 关闭客户端的文件句柄
                        self.fd_socket[fd].close()
                        # 在字典中删除已关闭客户端相关的信息
                        del self.fd_socket[fd]


    @staticmethod
    def handle_client(recv_data, client_socket):
        """静态方法:为一个客户端进行服务"""

        # 1.接收浏览器发送过来的请求,即http请求 GET / HTTP/1.1
        request_header_lines = recv_data.splitlines()
        for line in request_header_lines:
            print(line)

        # 2, 获取浏览器需要访问的页面
        http_request_line = request_header_lines[0]
        file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
        print("client request file is: %s" % file_name)

        # 3. 如果没有指定需要访问的页面,那么返回默认页面index.html
        if file_name == "/":
            file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            file_name = DOCUMENTS_ROOT + file_name
        print("the path of the file is: %s" % file_name)
        print("-" * 50)

        # 4. 打开对应页面,返回http格式的数据response给浏览器,分别准备header和body
        response_header = None
        response_body = None
        try:
            f = open(file_name, "rb")
        except IOError:
            # 读取页面失败,即没有这个页面,返回404
            response_body = "ERROR 404: file not found"
            response_header = "HTTP/1.1 404 not found\r\n"
            response_header += "Content-Length: %d\r\n" % len(response_body)  # 用一个空的行与body隔开
            response_header += "\r\n"
        else:
            response_body = f.read()
            f.close()
            response_header = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
            response_header += "Content-Length: %d\r\n" % len(response_body)  # 用一个空的行与body隔开
            response_header += "\r\n"  # 用一个空的行与body隔开
        finally:
            response = response_header.encode("utf-8") + response_body
            client_socket.send(response)
            # client_socket.close()


def main():
    """作为程序的主控制入口"""

    httpd = WSGIServer(SERVER_ADDR)
    print("web Server: Serving HTTP on port %d ...\n" % PORT)
    httpd.accept_client()


DOCUMENTS_ROOT = "./html"
SERVER_ADDR = (HOST, PORT) = "", 7788

if __name__ == "__main__":
    main()

相关文章

网友评论

      本文标题:python 简单的web静态服务器

      本文链接:https://www.haomeiwen.com/subject/vxmdnctx.html