Сокеты. Создание сервера

Последнее обновление: 03.05.2023

Для создания сервера на языке Python, как и для клиента, также используется класс socket, однако общая работа будет несколько отличаться от работы с сокетом клиента. Рассмотрим определение и работу с сокетом сервера поэтапно.

Привязка сервера

Сервер прослушивает входящие подключения, некоторым образом обрабатывает их и отправляет ответ. Чтобы сервер начал свою работу, вначале нам надо определить для него адрес, по которому он будет прослушивать подключения. Для этого применяется метод bind()

socket.bind(address)

Данный метод принимает адрес, по которому будет запущен сервер. По умолчанию адрес представляет кортеж из двух элементов:

(host, port)

Первый элемент - хост в виде строки. Это может быть, например, IP-адрес в виде "127.0.0.1" или название локального хоста. Второй параметр - числовой номер порта. Порт представляет 2-х байтное значение от 0 до 65535. Поскольку по одному и то же адресу (на одной и той же машине) может быть запущено несколько различных сетевых приложений, то порт позволяет разграничить эти приложения. Например:

import socket

server = socket.socket()            # создаем объект сокета сервера
hostname = socket.gethostname()     # получаем имя хоста локальной машины
port = 12345                        # устанавливаем порт сервера
server.bind((hostname, port))       # привязываем сокет сервера к хосту и порту

Здесь сервер будет запускаться на порту 12345. Следует учитывать, что не все порты могут быть свободны. Но, как правило, занятых портов не так много.

В качестве адреса используем имя текущего хоста. Для его получения применяется функция socket.gethostname() (обычно это имя текущего компьютера).

Прослушивание подключений

После привязки сервера его надо запустить на прослушивание подключений. Для этого применяется метод listen()

socket.listen([backlog])

Он принимает параметр backlog - максимальное количество входящих подключений в очереди, разрешенное для сокета. То есть, когда будут покдлючаться клиенты, они будут попадать в очередь и ждать, пока сервер не обработает текущего клиента. Если в очереди уже есть указанное количество клиентов, ожидающих обработки сервером, то все новые клиенты отклоняются. Например:

import socket

server = socket.socket()            # создаем объект сокета сервера
hostname = socket.gethostname()     # получаем имя хоста локальной машины
port = 12345                        # устанавливаем порт сервера
server.bind((hostname, port))       # привязываем сокет сервера к хосту и порту
server.listen(5)    # начинаем прослушиваение входящих подключений

Получение и обработка клиента

Для получения входящих подключений применяется метод accept(). Этот метод возращает кортеж из двух элементов

(conn, address)

Первый элемент - conn представляет еще один объект socket, через который сервер взаимодействует с клиентом. Второй элемент - address - адрес подключившегося клиента. Стоит отметить, что после завершения взаимодействия с клиентом сокет conn надо закрыть методом close()

Используя первый элемент кортежа - conn можно отправлять клиенту сообщения или наоборот получать данные. Для получения данных у сокета применяется метод socket.recv()

bytes = socket.recv(bufsize)

В качестве обязательного параметра он принимает максимальный размер буфера в байтах, которые могут быть получены за раз от другого сокета. Возвращаемое значение - набор байтов, полученых от другого сокета.

Для отправки данных применяется метод socket.send(), который в качестве параметра получает набор отправляемых данных.

socket.send(bytes)

Теперь посмотрим все на примере. Пусть в файле server.py будет определен сервер со следующим кодом:

import socket

server = socket.socket()            # создаем объект сокета сервера
hostname = socket.gethostname()     # получаем имя хоста локальной машины
port = 12345                        # устанавливаем порт сервера
server.bind((hostname, port))       # привязываем сокет сервера к хосту и порту
server.listen(5)                    # начинаем прослушиваение входящих подключений

print("Server starts")

con, addr = server.accept()     # принимаем клиента
print("connection: ", con)
print("client address: ", addr)

message = "Hello Client!"       # сообщение для отправки клиенту
con.send(message.encode())      # отправляем сообщение клиенту
con.close()                     # закрываем подключение

print("Server ends")
server.close()

Здесь сервер принимает клиента, выводит информацию о его подключении и отправляет клиенту в ответ строку "Hello Client!".

А в файле client.py определим следующий код клиента:

import socket

client = socket.socket()            # создаем сокет клиента
hostname = socket.gethostname()     # получаем хост сервера
port = 12345                        # устанавливаем порт сервера
client.connect((hostname, port))    # подключаемся к серверу
data = client.recv(1024)            # получаем данные с сервера
print("Server sent: ", data.decode())    # выводим данные на консоль
client.close()                      # закрываем подключение

Поскольку у нас сервер и клиент будут запускать на одном и том же комптьютере, то для определения адреса сервера для подключения применяется функция socket.gethostname() и порт 12345 - так же как и в коде сервера. После соединения с сервером получаем от него данные и выводим на консоль.

Сначала запустим код сервера. А затем запустим код клиента. В результате сервер примет подключение, выведет информацию о нем на консоль и отправит клиенту сообщение:

c:\python>python server.py
Server starts
connection:  <socket.socket fd=432, family=2, type=1, proto=0, laddr=('192.168.0.102', 12345), raddr=('192.168.0.102', 61824)>
client address:  ('192.168.0.102', 61824)
Server ends

В частности, здесь видим, что сервер и клиент запущены по адресу 192.168.0.102, причем клиент использует порт 61824.

А клиент получит от сервера сообщение и выведет его на консоль:

c:\python>python client.py
Server sent:  Hello Client!

Обработка множества клиентов

В данном случае сервер обслуживает одного клиента и прекращает работу. Если мы хотим, чтобы сервер обрабатывал множество клиентов, то мы можем использовать бесконечный цикл:

import socket
from datetime import datetime

server = socket.socket()            # создаем объект сокета сервера
hostname = socket.gethostname()     # получаем имя хоста локальной машины
port = 12345                        # устанавливаем порт сервера
server.bind((hostname, port))       # привязываем сокет сервера к хосту и порту
server.listen(5)                    # начинаем прослушиваение входящих подключений

print("Server running")
while True:
    con, addr = server.accept()     # принимаем клиента
    print("client address: ", addr)
    message = datetime.now().strftime("%H:%M:%S")  # отправляем текущее время
    con.send(message.encode())      # отправляем сообщение клиенту
    con.close()                     # закрываем подключение

В данном случае для примера с помощью встроенного модуля datetime и функции datetime.now().strftime() получаем текущее время в виде строки, которая затем отправляется клиенту. В итоге при запросе клиент будет получать текущее время.

Двунаправленная связь

В примерах выше связь была однонаправленная - сервер отправлял данные, а клиент получал их. Рассмотрим простейшую двунаправленную связь, когда и клиент и сервер и отправляют, и получают данные. Пусть сервер получает от клиента некоторую строку, инвертирует ее и отправляет обратно клиенту:

import socket

server = socket.socket()            # создаем объект сокета сервера
hostname = socket.gethostname()     # получаем имя хоста локальной машины
port = 12345                        # устанавливаем порт сервера
server.bind((hostname, port))       # привязываем сокет сервера к хосту и порту
server.listen(5)                    # начинаем прослушиваение входящих подключений

print("Server running")
while True:
    con, _ = server.accept()     # принимаем клиента
    data = con.recv(1024)           # получаем данные от клиента
    message = data.decode()         # преобразуем байты в строку
    print(f"Client sent: {message}")
    message = message[::-1]         # инвертируем строку
    con.send(message.encode())      # отправляем сообщение клиенту
    con.close()                     # закрываем подключение

А клиент пусть определяет код для ввода строки с консоли и ее отправки на сервер:

import socket

client = socket.socket()            # создаем сокет клиента
hostname = socket.gethostname()     # получаем хост локальной машины
port = 12345                        # устанавливаем порт сервера
client.connect((hostname, port))    # подключаемся к серверу
message = input("Input a text: ")   # вводим сообщение
client.send(message.encode())       # отправляем сообщение серверу
data = client.recv(1024)            # получаем данные с сервера
print("Server sent: ", data.decode()) 
client.close()                      # закрываем подключение

Результат работы. Клиент:

c:\python>python client.py
Input a text: hello
Server sent:  olleh

c:\python>

Сервер:

c:\python>python server.py
Server running
Client sent: hello

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850