Скрипт python не получает ответ от http сервера на сокетах после обновления страницы в браузере

Есть вот такой код сервера:

import socket
import os

ROUTES = [
    "/",
    "/index",
    "/favicon.ico",
    "/blog",
    "/api"
    ]

FAVICON = "favicon.ico"

def getResponse(method,route):
    bmsg = b""
    if method == "GET":
        if route in ROUTES:
            if "/favicon.ico" == route:
                if os.path.exists(FAVICON):
                    with open(FAVICON,"rb") as f:
                        data = f.read()
                    bmsg = "HTTP/1.1 200 OK\nContent-Type: image/ico\nContent-Length: ".encode("utf-8")+ str(len(data)).encode("utf-8") + "\r\n\r\n".encode("utf-8")
                    bmsg += data
            if "/blog" == route:
                data = "<h1><p>Blog Page</p></h1><p><a href=\"/index\">Back to main page</a></p>".encode("utf-8")
                bmsg = "HTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\nContent-Length: ".encode("utf-8") + str(len(data)).encode("utf-8") + "\r\n\r\n".encode("utf-8") + data
            if "/" == route or "/index" == route:
                data = "<h1><p>Index Page</p></h1>".encode("utf-8")
                bmsg = "HTTP/1.1 200 OK\nContent-Type: text/html; charset=utf-8\nContent-Length: ".encode("utf-8") + str(len(data)).encode("utf-8") + "\r\n\r\n".encode("utf-8") + data
            if "/api" == route:
                data = "{\"server_message\":\"OK\"}".encode("utf-8")
                bmsg = "HTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: ".encode("utf-8") + str(len(data)).encode("utf-8") + "\r\n\r\n".encode("utf-8") + data
        else:
            data = "<h2>404</h2><h1><p>Not Found</p></h1>".encode("utf-8")
            bmsg = "HTTP/1.1 404 false\nContent-Type: text/html; charset=utf-8\nContent-Length: ".encode("utf-8") + str(len(data)).encode("utf-8") + "\r\n\r\n".encode("utf-8") + data
    else:
        data = "<h2>405</h2><h1><p>Method not allowed</p></h2>".encode("utf-8")
        bmsg = "HTTP/1.1 405 false\nContent-Type: text/html; charset=utf-8\nContent-Length: ".encode("utf-8") + str(len(data)).encode("utf-8") + "\r\n\r\n".encode("utf-8") + data
    return bmsg

def start():
    server_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server_sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    server_sock.bind(("localhost",5000))
    server_sock.listen(10)
    step = 0
    while True:
        step += 1
        print(step)
        client_sock,addr = server_sock.accept()
        request = client_sock.recv(4096)
        
        request_list = request.decode("utf-8").split("\n")
        for line in request_list:
            print(line)
            
        method,route = (request_list[0].split(" ")[0],request_list[0].split(" ")[1])
        
        bmsg = getResponse(method,route)
        
        client_sock.sendall(bmsg)
        client_sock.close()
    server_sock.close()

start()

и вот такой код на питоне:

import requests

def get():
    msg = '{"command":"C:/GhostAssistent/nircmd.exe setsysvolume 6553"}'.encode("cp1251")
    print(msg)
    r = requests.get("http://localhost:5000/api",data = msg)
    if r.status_code == 200:
        print("headers - ",r.headers)
        if "application/json" in r.headers.get("content-type"):
            print("json - ",r.json())
        else:
            print("text - ",r.text)
    else:
        print("some error has happend")

Когда выполняю GET запрос питоновским скриптом, то все нормально, но стоит послать GET запрос из браузера, так питоновским скриптом получаю ответ только после того как снова сделаю запрос из браузера. Если браузер вообще не использовать после старта сервера, то все нормально - запускаю питоновский скрипт - получаю ответ, снова посылаю запрос - снова получаю ответ. Получается браузер блокирует соединение для питоновкого скрипта пока сам не получит ответ. Но почему тогда сам себя он не блокирует? Я могу две вкладки в браузере открыть и обновлять их в любом порядке и буду получать ответы сразу после запроса.

Как сделать так чтобы питоновский скрипт получал ответы независимо от запросов из браузера?


Ответы (1 шт):

Автор решения: Roman Konoval

Самая большая проблема в этой реализации, что у вас сервер однопоточный. Он может обрабатывать только один запрос одновременно.

Можно вынести обработку запроса от клиента в отдельный поток:

import threading

# тут все как раньше
...

def process_request(client_sock):
    request = client_sock.recv(4096)

    request_list = request.decode("utf-8").split("\n")
    for line in request_list:
        print(line)

    method,route = (request_list[0].split(" ")[0],request_list[0].split(" ")[1])

    bmsg = getResponse(method,route)

    client_sock.sendall(bmsg)
    client_sock.close()


def start():
    server_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server_sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    server_sock.bind(("localhost",5000))
    server_sock.listen(10)
    step = 0
    while True:
        step += 1
        print(step)
        client_sock,_ = server_sock.accept()
        thread = threading.Thread(target=process_request, args=(client_sock,))
        thread.start()

    server_sock.close()

Но самому, конечно, реализовывать http сервер, это то еще веселье. Посмотрите, например, flask. Вы напишите в 20 раз меньше кода, а работать будет лучше. Или хотя бы встроенный в python. Там все эти проблемы (и еще куча других, с которыми столкнетесь) решены уже, а от вас вообще ничего не требуется - бери и пользуйся.

→ Ссылка