天天看點

傳回使用者指定頁面的web伺服器

import socket
import re
import os

def handle_client(socket_con):
    """
     接收來自用戶端的請求,并接收請求封包,解析,傳回
    """
    # 1、伺服器接收用戶端的請求封包
    request = socket_con.recv(4096).decode()
    # 以行切割請求封包為清單
    res = request.split('\r\n')
    # 取第一位(請求行):GET / HTTP/1.1,并用正則切割GET / HTTP/1.1,取出路徑位置
    path = re.match('\w+\s(\S+)',res[0])
    path = path.group(1)
    # 判斷路徑長度,大于一則拼接出路徑,小于等于一則顯示首頁
    if len(path) > 1:
        # 路徑取出,開始拼接資源路徑(絕對路徑自己填寫)
        path = '# 檔案夾絕對路徑' + path
        print(path)
    else:
        # 顯示首頁代碼
        response_line = 'HTTP/1.1 200 OK\r\n'
        response_head = 'Content-Type:text/html;charset=utf-8\r\n'
        response_body = '''
            <html>
            <head>
            <title>首頁</title>
            <style>
            body {
                    width: 35em;
                    margin: 0 auto;
                    font-family: Tahoma, Verdana, Arial, sans-serif;
                 }
            </style>
            </head>
            <body>
            <h1>首頁</h1>
            <p>歡迎來到首頁</p>
            <p><em>感謝你的使用</em></p>
            </body>
            </html>'''
        response = response_line + response_head + '\r\n' + response_body
        socket_con.send(response.encode())
        socket_con.close()
    # 路徑大于一并取出之後判斷資源是否存在
    if not os.path.exists(path):
        # 資源不存在則顯示資源不存在界面
        response_line = 'HTTP/1.1 404 NOT FOUND\r\n'
        response_head = 'Content-Type:text/html;charset=utf-8\r\n'
        response_body = '''
            <html>
            <head>
            <title>錯誤</title>
            <style>
                body {
                    width: 35em;
                    margin: 0 auto;
                    font-family: Tahoma, Verdana, Arial, sans-serif;
                }
            </style>
            </head>
            <body>
            <h1>你請求的資源不存在!</h1>
            <p>如果你想通路一個資源,請輸入正确的資源路徑</p>
            <p><em>感謝你的使用</em></p>
            </body>
            </html>'''
        response = response_line + response_head + '\r\n' + response_body
        socket_con.send(response.encode())
        socket_con.close()
        return
    else:
        # 資源存在以後判斷是否是檔案,是檔案則直接讀取
        if os.path.isfile(path):
            response_line = 'HTTP/1.1 200 OK\r\n'
            response_head = 'Server:skylark 2.0\r\n'
            response_head += 'Content-Type:*/*;charset:utf-8\r\n'
            f = open(path, 'rb')
            response_body = f.read()
            response = response_line.encode() + response_head.encode() + '\r\n'.encode() + response_body
            socket_con.send(response)
            socket_con.close()
            return
        else:
            if path.endswith('/'):
                 # 如果是檔案夾
                 # 判斷檔案夾下是否有預設檔案,如果有則傳回,如果沒有則判斷伺服器是否開啟了目錄浏覽
                 # 預設檔案:index.html  default.html
                 # 是否可以通路預設檔案開關,True 開 ,False 關
                 default_document = False
                 if default_document:
                     # 判斷使用者通路的檔案夾下是否有index.html 或者 default.html
                     if os.path.exists(path + 'index.html'):
                         response_line = 'HTTP/1.1 200 OK\r\n'
                         response_head = 'Server:skylark 2.0\r\n'
                         response_head += 'Content-Type:*/*;charset:utf-8\r\n'
                         f = open(path + 'index.html', 'rb')
                         response_body = f.read()
                         response = response_line.encode() + response_head.encode() + '\r\n'.encode() + response_body
                         socket_con.send(response)
                         socket_con.close()
                         return
                     elif os.path.exists(path + 'default.html'):
                         response_line = 'HTTP/1.1 200 OK\r\n'
                         response_head = 'Server:skylark 2.0\r\n'
                         response_head += 'Content-Type:*/*;charset:utf-8\r\n'
                         f = open(path + 'default.html', 'rb')
                         response_body = f.read()
                         response = response_line.encode() + response_head.encode() + '\r\n'.encode() + response_body
                         socket_con.send(response)
                         socket_con.close()
                         return
                     else:
                         # 如果沒有上述兩個頁面,則可以傳回404錯誤,或者302重定向
                         response_line = "HTTP/1.1 404 Not Found\r\n"
                         response_head = "Server:skylark 2.0\r\n"
                         response_body = "index.html or default.html is not exist!!!"
                         response = response_line + response_head + "\r\n" + response_body
                         socket_con.send(response.encode())
                         socket_con.close()
                # 不能通路預設檔案情況下,判斷伺服器是否開啟了目錄浏覽
                 else:
                     dir_browsing = True
                     if dir_browsing:
                         # 把使用者請求的檔案夾中所有的檔案和檔案夾以目錄的形式傳回到頁面中
                         # 擷取使用者請求的檔案夾
                         list_names = os.listdir(path)
                         response_line = 'HTTP/1.1 200 OK\r\n'
                         response_head = 'Server:skylark 2.0\r\n'
                         # 動态的拼接頁面,将目錄中的檔案或者檔案夾的名稱以HTML頁面的方式傳回給浏覽器
                         response_body = '<html><head><body><ul>'
                         for item in list_names:
                             response_body += "<li><a href = '#'>" + item + "</a></li>"
                         response_body += '</ul></body></head></html>'
                         response = response_line + response_head + "\r\n" + response_body
                         socket_con.send(response.encode())
                         socket_con.close()
                         return
            else:
                # 使用者請求的路徑沒有斜線
                # 重定向到+斜線的目錄下,并顯示重定向以後的路徑(此處可以增加有斜線目錄處理方式也就是上面的方法)
                response_line = 'HTTP/1.1 302 Found\r\n'
                response_head = 'Server:skylark 2.0\r\n'
                response_head += 'Content-Type:text/html;charset=utf-8\r\n'
                response_body = '重定向' + path + '/'
                response = response_line + response_head + '\r\n' + response_body
                socket_con.send(response.encode())
                socket_con.close()


def main():
    # 1、伺服器建立負責監聽的socket
    socket_watch = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 2、設定位址重用
    socket_watch.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 3、綁定監聽的端口
    socket_watch.bind(("",8888))
    # 4、設定監聽隊列
    socket_watch.listen(128)
    # 5、通過循環,不停的接收來自用戶端的連接配接請求
    while True:
        socket_con,con_adds = socket_watch.accept()
        # 注意将con_adds轉成字元串
        print("用戶端:%s連接配接成功!!!" % str(con_adds))
        # 接收來自用戶端的請求,并接收請求封包,解析,傳回
        handle_client(socket_con)

if __name__ == '__main__':
    main()