Введение в сетевое программирование. Сокеты.

Теория

Сокет — это программный интерфейс для обеспечения информационного обмена между процессами.

Виды сокетов:
  • Серверный - сокет, который принимает сообщения.

  • Клиентский - сокет, который отправляет сообщения.

Сокеты работают на транспортном уровне протоколв и соответственно бывают 2 типов:
  • Потоковые (на основе TCP, в коде обозначаются SOCK_STREAM) - сокеты с установленным соединением на основе протокола TCP, передают поток байтов, который может быть двунаправленным - т.е. приложение может и получать и отправлять данные.

  • Дейтаграммные (на основе UDP, в коде обозначаются SOCK_DGRAM) - сокеты, не требующие установления явного соединения между ними. Сообщение отправляется указанному сокету и, соответственно, может получаться от указанного сокета.

Сокет состоит из IP-адреса и порта.

IP-адрес - уникальный сетевой адрес узла в компьютерной сети, построенной по протоколу IP. В версии протокола IPv4 IP-адрес имеет длину 4 байта (например, 192.168.0.3), а в версии протокола IPv6 IP-адрес имеет длину 16 байт (например, 2001:0db8:85a3:0000:0000:8a2e:0370:7334). IP-адрес должен быть уникален.

Порт - натуральное число, записываемое в заголовках протоколов транспортного уровня (TCP, UDP и др.). Порт используется для определения процесса-получателя пакета в пределах одного хоста.

В python для работы с сокетами используется встроенная библиотека socket. Одной из основных функций модуля является функция socket(), которая возвращает объект типа сокет, обладающий соответствующими функциями для работы с соединением.:

class socket.socket
sock = socket.socket()
Функции объекта socket
  • socket.bind(address) - Привязывает сокет к адресу address (инициализирует IP-адрес и порт). Сокет не должен быть привязан до этого.

  • socket.listen([backlog]) - Переводит сервер в режим приема соединений. Параметр``backlog (int)`` – количество соединений, которые будет принимать сервер.

  • socket.accept() - Принимает соединение и блокирует приложение в ожидании сообщения от клиента. В результате возвращает кортеж:

    • conn: объект соединения (сокет), который можно использовать для отправки/получения данных;

    • address: адрес клиента.

  • socket.recv(bufsize[, flags]) - Читает и возвращает данные в двоичном формате (набор байтов) из сокета. Параметр bufsize (int) – максимальное количество байтов в одном сообщении.

  • socket.send(bytes[, flags]) - Отправляет данные клиенту и возвращает количество отправленных байт. Параметр bytes (bytes) – двоичные данные.

  • socket.close() - Закрывает сокет.

Работа с сокетом во многом схожа с работой с файловым объектом. Принцип - открыли соединение - считали данные - закрыли соединение.

Создание серверного сокета: .. code

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # создаем сокет
sock.bind(('', 55000))  # связываем сокет с портом, где он будет ожидать сообщения
sock.listen(10)  # указываем сколько может сокет принимать соединений
print('Server is running, please, press ctrl+c to stop')
while True:
    conn, addr = sock.accept()  # начинаем принимать соединения
    print('connected:', addr)  # выводим информацию о подключении
    data = conn.recv(1024)  # принимаем данные от клиента, по 1024 байт
    print(str(data))
    conn.send(data.upper())  # в ответ клиенту отправляем сообщение в верхнем регистре
conn.close()  # закрываем соединение

Создание клиентского сокета: .. code

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # создаем сокет
sock.connect(('localhost', 55000))  # подключемся к серверному сокету
sock.send(bytes('Hello, world', encoding = 'UTF-8'))  # отправляем сообщение
data = sock.recv(1024)  # читаем ответ от серверного сокета
sock.close()  # закрываем соединение
print(data)

Important

Обратите внимание, клиент отправляет байтовую строку, а не обычную. Сервер так же принимает в качестве сообщения байтовую строку и должен в ответе вернуть объект того же типа.

Задания

  1. Реализовать чат без графического интерфейса, который позволит обмениваться сообщениями только между клиентом и сервером. Клиент должен получать сообщения сервера в том числе.

  2. С помощью модуля easygui (см. полезные ссылки), добавьте в разработанный чат простой графический интерфейс.

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

  4. Добавьте к чату из задачи 2 чат-бота на стороне сервера. Добавьте 4-5 фраз, которые сервер будет отправлять по определённым условиям.