import socketserver socketserver.StreamRequestHandler socketserver.DatagramRequestHandler
Классы StreamRequestHandler
и DatagramRequestHandler
представляют собой подклассы BaseRequestHandler
, которые переопределяют методы setup()
и finish()
и предоставляют атрибуты self.rfile
и self.wfile
.
Атрибуты self.rfile
и self.wfile
позволяют получить данные запроса или вернуть данные клиенту соответственно.
Атрибуты .rfile
обоих классов поддерживают интерфейс io.BufferedIOBase
доступный для чтения, а метод .wfile
обоих классов поддерживает интерфейс, доступный для записи.
Изменено в Python 3.6: Метод StreamRequestHandler.wfile
также стал поддерживать доступный для записи интерфейс io.BufferedIOBase
.
Обычно, чтобы реализовать службу, необходимо сначала наследоваться от класса BaseRequestHandler
, что бы создать пользовательский обработчик запросов, переопределив метод .handle()
. Затем, уже можно запускать различные версии службы, объединив один из классов сервера с созданным классом обработчика запросов.
Для служб datagram или stream класс обработчика запросов должен быть другим. Это различие можно скрыть с помощью подклассов обработчиков StreamRequestHandler
или DatagramRequestHandler
.
Файлоподобные объекты, упрощающие обмен данными за счет предоставления стандартного файлового интерфейса.
В классе, привеенном ниже, вызов метода .readline()
будет автоматически вызывать метод объекта сокета Socket.recv()
несколько раз, пока не встретит символ новой строки.
Серверная сторона:
# server.py import socketserver class MyTCPHandler(socketserver.StreamRequestHandler): """ Класс обработчика запросов для сервера. Он создается один раз при каждом подключении к серверу и должен Переопределить метод `handle()` для реализации связи с клиентом. """ def handle(self): # self.rfile - это файловый объект, созданный обработчиком; # теперь можем использовать, например, `.readline()` # вместо необработанных вызовов метода сокета `.recv()` self.data = self.rfile.readline().strip() print(f"{self.client_address[0]} прислал:") data = self.data.decode("utf-8") print(data) # Аналогично, `self.wfile` - это файлоподобный объект, # используемый для обратной отправки сообщения клиенту self.wfile.write(data.upper().encode("utf-8")) if __name__ == "__main__": HOST, PORT = "localhost", 9999 # Создаем сервер, привязанный к `localhost` на порту 9999 with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server: # Активируем сервер. Работа будет продолжаться до тех пор, # пока не прервать программу при помощи Ctrl-C server.serve_forever()
Что бы проверить работу сервера, используйте следующий код клиентской стороны или напишите свой. Первым запускается серверная сторона. Клиент запускается в другом терминале. Сообщение клиенту формируется как параметр командной строки при запуске клиентского скрипта, например $ python3 client.py Hello world!
import socket, sys HOST, PORT = "localhost", 9999 # Данные для отправки получим # как параметры командной строки data = " ".join(sys.argv[1:]) # Создаем сокет (`SOCK_STREAM` означает сокет TCP) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: # Подключение к серверу и отправка данных sock.connect((HOST, PORT)) sock.sendall(bytes(data + "\n", "utf-8")) # Получаем данные с сервера и завершаем работу received = str(sock.recv(1024), "utf-8") print(f"Отправлено: {data}") print(f"Получено: {received}")