一:返回固定页面的静态web服务器:
1:初始化web服务器:创建套接字—> 设置端口复用—>绑定本地端口—>设置监听模式
2:循环接受用户:accpet —>接受用户数据---->recv
3:读取服务器固定页面信息
4:构造响应报文
5:向客户端发送指定页面信息
6:关闭套接字

import socket

def main():

    # 1:初始化服务器
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    tcp_socket.bind(("",7788))
    tcp_socket.listen(128)

    # 2:循环接收用户
    while True:

        new_socket, client_msg = tcp_socket.accept()
        print("客户端%s发来请求",client_msg)

        recv_data = new_socket.recv(1024).decode("utf-8")
        print(recv_data)

        # 3:读取服务器指定文件

        with open("static/index.html","rb") as file:
            send_data = file.read()

        # 4:构造相应报文
        response_line = "HTTP:/1.1 200 ok\r\n"
        response_head = "NBNB:ASD\r\n"
        response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data

        #5: 发送给客户端
        new_socket.send(response_data)

        # 6关闭套接字
        new_socket.close()

if __name__ == '__main__':
    main()

二:返回指定页面的静态web服务器
思路:要想返回指定页面就需要,分析请求报文:请求报文格式:请求行(请求方式 请求地址 HTTP协议版本)
所以我只需要使用空格将字符串分割一下就好了。分割的第二部分就是请求地址。

#得到请求后分割字符串,设置分割两次
        request_list = recv_data.split(" ",maxsplit=2)
        
		# 取出资源路径部分
        request_addr = request_list[1]


        # 3:读取服务器指定文件
        
		#将指定路径加进去
        with open("static"+ request_addr,"rb") as file:
            send_data = file.read()
import socket

def main():

    # 1:初始化服务器
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    tcp_socket.bind(("",7788))
    tcp_socket.listen(128)

    # 2:循环接收用户
    while True:

        new_socket, client_msg = tcp_socket.accept()
        print("客户端%s发来请求",client_msg)

        recv_data = new_socket.recv(1024).decode("utf-8")
        print(recv_data)
        
		#得到请求后分割字符串,设置分割两次
        request_list = recv_data.split(" ",maxsplit=2)
        
		# 取出资源路径部分
        request_addr = request_list[1]


        # 3:读取服务器指定文件
        
		#将指定路径加进去
        with open("static"+ request_addr,"rb") as file:
            send_data = file.read()

        # 4:构造相应报文
        response_line = "HTTP:/1.1 200 ok\r\n"
        response_head = "NBNB:ASD\r\n"
        response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data

        #5: 发送给客户端
        new_socket.send(response_data)

        # 6关闭套接字
        new_socket.close()


if __name__ == '__main__':
    main()

三:增加默认页面:
思路:如果我输入127.0.0.1:7788/ 会报错,因为服务器找不到这个路径。所以需要设置默认路径。
解决方式就是判断截取到的信息是不是“/” 如果是替换成"/index.html"

request_addr = request_list[1]

        if request_addr == "/":
            request_addr = "/index.html"
import socket

def main():

    # 1:初始化服务器
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    tcp_socket.bind(("",7788))
    tcp_socket.listen(128)

    # 2:循环接收用户
    while True:

        new_socket, client_msg = tcp_socket.accept()
        print("客户端%s发来请求",client_msg)

        recv_data = new_socket.recv(1024).decode("utf-8")
        print(recv_data)

        request_list = recv_data.split(" ",maxsplit=2)

        request_addr = request_list[1]

        if request_addr == "/":
            request_addr = "/index.html"

        # 3:读取服务器指定文件

        with open("static"+ request_addr,"rb") as file:
            send_data = file.read()

        # 4:构造相应报文
        response_line = "HTTP:/1.1 200 ok\r\n"
        response_head = "NBNB:ASD\r\n"
        response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data

        #5: 发送给客户端
        new_socket.send(response_data)

        # 6关闭套接字
        new_socket.close()


if __name__ == '__main__':
    main()

四:解决找不到报错问题:
问题:如果服务器中没有这个文件,那么在with open("static"+ request_addr,"rb") as file: send_data = file.read()这个地方就会报错。导致服务器直接挂掉了。
解决办法:给这段代码加try ... except...else...finally进行捕获,并且让他出错后返回到错误页面。
核心代码:

try:

            with open("static"+ request_addr,"rb") as file:
                send_data = file.read()
        except Exception as e:

            # 4:构造相应报文
            response_line = "HTTP:/1.1 200 ok\r\n"
            response_head = "NBNB:ASD\r\n"
            with open("static/error.html", "rb") as file:
                send_data = file.read()
            response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data

            #5: 发送给客户端
            new_socket.send(response_data)
        else:
            # 4:构造相应报文
            response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
            response_head = "NBNB:ASD\r\n"

            response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

            # 5: 发送给客户端
            new_socket.send(response_data)

        finally:
            # 6关闭套接字
            new_socket.close()
import socket

def main():

    # 1:初始化服务器
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    tcp_socket.bind(("",7788))
    tcp_socket.listen(128)

    # 2:循环接收用户
    while True:

        new_socket, client_msg = tcp_socket.accept()
        print("客户端%s发来请求",client_msg)

        recv_data = new_socket.recv(1024).decode("utf-8")
        print(recv_data)

        request_list = recv_data.split(" ",maxsplit=2)

        request_addr = request_list[1]


        if request_addr == "/":
            request_addr = "/index.html"

        # 3:读取服务器指定文件

        try:

            with open("static"+ request_addr,"rb") as file:
                send_data = file.read()
        except Exception as e:

            # 4:构造相应报文
            response_line = "HTTP:/1.1 200 ok\r\n"
            response_head = "NBNB:ASD\r\n"
            with open("static/error.html", "rb") as file:
                send_data = file.read()
            response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data

            #5: 发送给客户端
            new_socket.send(response_data)
        else:
            # 4:构造相应报文
            response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
            response_head = "NBNB:ASD\r\n"

            response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

            # 5: 发送给客户端
            new_socket.send(response_data)

        finally:
            # 6关闭套接字
            new_socket.close()


if __name__ == '__main__':
    main()

五:解决客户端异常关闭问题:
问题:如果客户端突然关闭,会导致recv_data = new_socket.recv(1024).decode("utf-8")这个地方解阻塞,此时服务器会收到来自客户端的空数据,此时request_addr = request_list[1]这里就会拿不到数据,导致下标越界。

解决方案:
判断是不是空数据,如果是关闭套接字,然后直接退出。

核心代码:

recv_data = new_socket.recv(1024).decode("utf-8")

        if len(recv_data) == 0:
            new_socket.close()
            continue

import socket

def main():

    # 1:初始化服务器
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    tcp_socket.bind(("",7788))
    tcp_socket.listen(128)

    # 2:循环接收用户
    while True:

        new_socket, client_msg = tcp_socket.accept()
        print("客户端%s发来请求",client_msg)

        recv_data = new_socket.recv(1024).decode("utf-8")

        if len(recv_data) == 0:
        
        	new_socket.close()
            continue

            
        print(recv_data)

        request_list = recv_data.split(" ",maxsplit=2)

        request_addr = request_list[1]


        if request_addr == "/":
            request_addr = "/index.html"

        # 3:读取服务器指定文件

        try:

            with open("static"+ request_addr,"rb") as file:
                send_data = file.read()
        except Exception as e:

            # 4:构造相应报文
            response_line = "HTTP:/1.1 200 ok\r\n"
            response_head = "NBNB:ASD\r\n"
            with open("static/error.html", "rb") as file:
                send_data = file.read()
            response_data = (response_line + response_head+"\r\n").encode("utf-8")+send_data

            #5: 发送给客户端
            new_socket.send(response_data)
        else:
            # 4:构造相应报文
            response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
            response_head = "NBNB:ASD\r\n"

            response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

            # 5: 发送给客户端
            new_socket.send(response_data)

        finally:
            # 6关闭套接字
            new_socket.close()


if __name__ == '__main__':
    main()

六:多任务版本
问题:现在是服务器只有服务完第一个客户端,并且关闭new_socket才能为下一个客户端服务。
解决办法:让服务客户端的另外创建一个线程,单独服务。这样主线程还可以继续让客户端连接。
并且,为了保证服务器断掉,必成功,就要保证子线程守护主线程。
核心代码:

 while True:

        new_socket, client_msg = tcp_socket.accept()
        print("客户端%s发来请求",client_msg)

        t1 = threading.Thread(target=service_client, args=(new_socket,))

        t1.daemon = True

        t1.start()
import socket

import threading


def service_client(new_socket):
    recv_data = new_socket.recv(1024).decode("utf-8")

    if len(recv_data) == 0:
        new_socket.close()
        return

    print(recv_data)

    request_list = recv_data.split(" ", maxsplit=2)

    request_addr = request_list[1]

    if request_addr == "/":
        request_addr = "/index.html"

    # 3:读取服务器指定文件

    try:

        with open("static" + request_addr, "rb") as file:
            send_data = file.read()
    except Exception as e:

        # 4:构造相应报文
        response_line = "HTTP:/1.1 200 ok\r\n"
        response_head = "NBNB:ASD\r\n"
        with open("static/error.html", "rb") as file:
            send_data = file.read()
        response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

        # 5: 发送给客户端
        new_socket.send(response_data)
    else:
        # 4:构造相应报文
        response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
        response_head = "NBNB:ASD\r\n"

        response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

        # 5: 发送给客户端
        new_socket.send(response_data)

    finally:
        # 6关闭套接字
        new_socket.close()


def main():

    # 1:初始化服务器
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    tcp_socket.bind(("",7788))
    tcp_socket.listen(128)

    # 2:循环接收用户
    while True:

        new_socket, client_msg = tcp_socket.accept()
        print("客户端%s发来请求",client_msg)

        t1 = threading.Thread(target=service_client, args=(new_socket,))

        t1.daemon = True

        t1.start()


if __name__ == '__main__':
    main()

七:改版成面向对象的格式----搭建静态web服务器

思路:主程序可以定义一个搭建服务器的对象,然后调用它的start() 方法就可以了。
搭建服务器的类:1: init初始化方法 2:start方法 3子线程处理的方法。

import socket

import threading


class  Create_Web_Service(object):

        def __init__(self):
            # 1:初始化服务器
            self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
            self.tcp_socket.bind(("", 7788))
            self.tcp_socket.listen(128)


        def start(self):
            while True:
                new_socket, client_msg = self.tcp_socket.accept()
                print("客户端%s发来请求", client_msg)

                t1 = threading.Thread(target=self.handle_client, args=(new_socket,))

                t1.daemon = True

                t1.start()

        @staticmethod
        def handle_client(new_socket):
            recv_data = new_socket.recv(1024).decode("utf-8")

            if len(recv_data) == 0:
                new_socket.close()
                return

            print(recv_data)

            request_list = recv_data.split(" ", maxsplit=2)

            request_addr = request_list[1]

            if request_addr == "/":
                request_addr = "/index.html"

            # 3:读取服务器指定文件

            try:

                with open("static" + request_addr, "rb") as file:
                    send_data = file.read()
            except Exception as e:

                # 4:构造相应报文
                response_line = "HTTP:/1.1 200 ok\r\n"
                response_head = "NBNB:ASD\r\n"
                with open("static/error.html", "rb") as file:
                    send_data = file.read()
                response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

                # 5: 发送给客户端
                new_socket.send(response_data)
            else:
                # 4:构造相应报文
                response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
                response_head = "NBNB:ASD\r\n"

                response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

                # 5: 发送给客户端
                new_socket.send(response_data)

            finally:
                # 6关闭套接字
                new_socket.close()


def main():

    s = Create_Web_Service()

    s.start()

if __name__ == '__main__':
        main()

八:静态Web服务器-命令行启动动态绑定端口号:

因为命令行启动需要两个参数,所以要判断输入的参数是不是两位,此外还要判断端口是不是整数,如果都符合则将命令行输入的端口替换原来的7788.

1:获取执行python程序的终端命令行参数

 sys.argv:

2:判断参数的类型,设置端口号必须是整型

 if not sys.argv[1].isdigit():
     print("启动命令如下: python3 xxx.py 9090")
     return
 port = int(sys.argv[1])
 
3:给Web服务器类的初始化方法添加一个端口号参数,用于绑定端口号

 def __init__(self, port):
     self.tcp_server_socket.bind((“”, port))
import socket

import threading
import sys


class Create_Web_Service(object):

    def __init__(self, port):
        # 1:初始化服务器
        self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self.tcp_socket.bind(("", port))
        self.tcp_socket.listen(128)

    def start(self):
        while True:
            new_socket, client_msg = self.tcp_socket.accept()
            print("客户端%s发来请求", client_msg)

            t1 = threading.Thread(target=self.handle_client, args=(new_socket,))

            t1.daemon = True

            t1.start()

    @staticmethod
    def handle_client(new_socket):
        recv_data = new_socket.recv(1024).decode("utf-8")

        if len(recv_data) == 0:
            new_socket.close()
            return

        print(recv_data)

        request_list = recv_data.split(" ", maxsplit=2)

        request_addr = request_list[1]

        if request_addr == "/":
            request_addr = "/index.html"

        # 3:读取服务器指定文件

        try:

            with open("static" + request_addr, "rb") as file:
                send_data = file.read()
        except Exception as e:

            # 4:构造相应报文
            print(e)
            response_line = "HTTP:/1.1 200 ok\r\n"
            response_head = "NBNB:ASD\r\n"
            with open("static/error.html", "rb") as file:
                send_data = file.read()
            response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

            # 5: 发送给客户端
            new_socket.send(response_data)
        else:
            # 4:构造相应报文
            response_line = "HTTP:/1.1 404 NOT FOUND\r\n"
            response_head = "NBNB:ASD\r\n"

            response_data = (response_line + response_head + "\r\n").encode("utf-8") + send_data

            # 5: 发送给客户端
            new_socket.send(response_data)

        finally:
            # 6关闭套接字
            new_socket.close()


def main():

    print(sys.argv)
    if len(sys.argv) != 2:
        print("请输入输入两个单词:一个地址,一个端口.")
        return

    if not sys.argv[1].isdigit():
        print("请输入输入两个单词:一个地址,一个端口.")
        return

    port = int(sys.argv[1])

    s = Create_Web_Service(port)

    s.start()

if __name__ == '__main__':
    main()