@Drovosek01

Почему получение данных в python socket останавливается?

Пишу небольшой сервер на python 3 с использованием модуля socket.

server.py

import socket
from views import *


URLS = {
    '/': index,
    '/blog': blog,
    '/favicon.ico': favicon,
    '/index.css': index_css
}

def generate_content(code, url):
    if code == 404:
        return '<h1>404</h1><p>Not found</p>'
    if code == 405:
        return '<h1>405</h1><p>Method not allowed</p>'
    # return '<h1>{}</h1>'.format(URLS[url])
    return URLS[url]()

def generate_headers(method, url):
    if method != 'GET':
        return ('HTTP/1.1 405 Method not allowed\r\n\r\n', 405)

    if url not in URLS:
        return('HTTP/1.1 404 Not found\r\n\r\n', 404)

    return ('HTTP/1.1 200 OK\r\n\r\n', 200)

def parse_request(request):
    parsed = request.split(' ')
    method = parsed[0]
    url = parsed[1]
    return (method, url)

def generate_response(request):
    method, url = parse_request(request)
    headers, code = generate_headers(method, url)
    body = generate_content(code, url)
    return (headers + body).encode()

def run():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('localhost', 5000))
    server_socket.listen(10)

    while True:
        client_socket, addr = server_socket.accept()
        while True:
            # client_socket, addr = server_socket.accept()
            request = client_socket.recv(1024)
            if not request:
                break
            print(request.decode('utf-8'))
            print(addr)

            response = generate_response(request.decode('utf-8'))
            client_socket.sendall(response)
            # client_socket.close()
        client_socket.close()

if __name__ == '__main__':
    run()



views.py

import os


dir = os.path.dirname(__file__)


def index():
    with open(os.path.join(dir,'templates/index.html'), encoding='utf-8') as template:
        return template.read()

def blog():
    with open(os.path.join(dir,'templates/blog.html'), encoding='utf-8') as template:
        return template.read()

def index_css():
    with open(os.path.join(dir,'templates/index.css'), encoding='utf-8') as index_css:
        return index_css.read()

def favicon():
    with open(os.path.join(dir,'templates/favicon.ico')) as favicon:
        return favicon.read()


Проблема внутри цикла whlie.
Если я запускаю код как есть и перехожу на localhost:5000, то завершается первая итерация внутреннего цикла while, и во время второй итерации код останавливается на получении запроса request = client_socket.recv(1024). Запрос со стороны клиента есть, это видно в Chrome DevTools. Там первый запрос завершился нормально, а у второй на рассмотрении.
5c8f60e05dd72327719385.png
И на этом все. В браузере страница просто бесконечно грузится, а в консоли никаких ошибок.

Если основной код немного изменить на такой вариант:
while True:
    # client_socket, addr = server_socket.accept()
    while True:
        client_socket, addr = server_socket.accept()
        request = client_socket.recv(1024)
        if not request:
            break
        print(request.decode('utf-8'))
        print(addr)

        response = generate_response(request.decode('utf-8'))
        # client_socket.sendall('hello world'.encode())
        client_socket.sendall(response)
        client_socket.close()
    # client_socket.close()

То все работает нормально, но это немного нелогично, на мой взгляд, т.к. при получении каждого нового пакета соединение с клиентом устанавливается заново.
Посмотрел примеры в документации и там тоже не делают socket.accept() и recv() внутри одного цикла.

Так почему мой код останавливается на второй итерации на получении запроса request = client_socket.recv(1024), хотя запрос отправлен?
  • Вопрос задан
  • 555 просмотров
Решения вопроса 1
Drovosek01, то как Вы написали веб сервер не годится для реальных задач. Вам легче и лучше было бы переписать сервер используя библиотеку или фреймворк (bottle, falcon, flask, ...). Но если целью стоит изучение работы сокетов, то:
  1. Ваш сервер однопоточен и блокируемый. Причина может крыться в этом, т.к. браузер вполне может соединяться с веб сервером параллельно в несколько потоков. Посмотрите примеры неблокируемого режима сокет сервера и/или используйте нити (threads). Если будет сложно, как я и писал переходите на высокоуровневое программирование.
  2. Причина остановки может быть пассивное закрытие сокета из за TTL к примеру. Проверяйте работы сокетов не на высоком уровне(Chrome), а на низком используя WireShark. Т.к. у Вас не отлаженный низкоуровневый код.
  3. И по поводу возможного TTL, способ которым Вы создаете страницу, уж простите, ужасный. Хотя бы перепишите возврат шаблонов в таком виде:
    def index():
        with open(os.path.join(dir,'templates/index.html'), encoding='utf-8') as template:
            buffer = template.read()
        while True:
           yield buffer
    Чтение с диска на кажде соединение, даже если у Вас SSD, это как то черезчур там более в таком простом случае
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы