Python работа с socket

Сокеты в Python. Что вообще такое сокет?

В сетевом программировании всё просто — клиент отправляет запрос, сервер его получает. Для реализации такого клиент-серверного взаимодействия используется, в том числе, Python, который имеет на борту средства для взаимодействия между вычислительными устройствами, объединенными в сеть. Самую главную роль в этом играет как раз сокет.

Сокет — это программные объекты (интерфейсы), определяющие конечную точку соединения. Сокет — это некий абстрактный файл, через который обеспечивает сетевую связность программное приложение. Программа с помощью сокета может установить входящие и исходящие соединения, а также принимать данные. Сокет работает на уровне операционной системы и имеет два параметра: порт и IP-адрес

Непонятно? Попробуем проще. Вы зашли в МакДональдс. Есть свободные кассы (порты), а есть кассы с кассирами — тоже порты, но за ними висит какое-либо приложение (это приложение постоянно прослушивает порт — нет ли новых «клиентов»).

Если вы попробуете обратиться к конкретному кассиру (приложению), то он установит с вами соединение через сокет именно своего порта. Вас же обслуживают на той кассе, к которой вы подошли, верно?

Протоколы

Протокол — это арбитр, который определяет правила обмена данными между сервером и клиентом — упаковка, передача, распаковка и так далее. В рамках знакомства с сокетом в Python мы будем ориентироваться на протокол TCP (Transmission Control Protocol).

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

Читайте также:  Наивный классификатор байеса python

Если клиент хочет соединиться с сервером, то он отправляет свои данные через «окно» (оно же — сокет). Эти данные летят в протокол TCP, запущенный у клиента. Данные проходят через буфер и отправляются в сторону сервера, где, в свою очередь, данные также попадают через буфер в сокет.

Сокеты в питоне

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

Процедура установки соединения через TCP будет выглядеть так:

Какие будут использованы функции?

Общие для клиента и сервера:

  • socket() — создать сокет (функция возвращает объект сокета, методы которого реализуют различные системные вызовы сокетов)
  • send() — передать данные
  • recv() — получить данные
  • close() — закрыть соединение
  • bind() — привязать сокет к IP-адресу и порту машины
  • listen() — просигнализировать о готовности принимать соединения
  • accept() — принять запрос на установку соединения

Пример использования сокета

Представим простую ситуацию, клиент хочет получить от сервера текущую дату и время. Что нам нужно? Нужно настроить соединение посредством протокола TCP. Через сокет, конечно же.

Серверный код:

from socket import * import time s = socket(AF_INET, SOCK_STREAM) s.bind(('', 7777)) s.listen(5) while True: client, addr = s.accept() print(client) print("Получен запрос на соединение от %s" % str(addr)) timestr = time.ctime(time.time()) + "\n" client.send(timestr.encode('utf-8')) client.close()

s = socket(AF_INET, SOCK_STREAM) — запускаем функцию socket() с двумя параметрами — communication domain и type of socket. В качестве коммуникационного домена, как правило, передается значение AF_INET — оно указывает, что создаваемый сокет будет сетевым. Тип сокета мы указали SOCK_STREAM, как понятно из названия — сокет у нас будет потоковый, то есть реализующий последовательный, надежный двусторонний поток байтов по протоколу TCP.

В результате функции socket() создается конечная точка соединения и возвращается файловый дескриптор (некоторый целочисленный идентификатор, однозначно определяющий файл в текущем процессе), который позволяет работать с сокетом, как с файлом — записывать и считывать данные в/из него.

s.bind((», 7777)) — присваиваем сокету 7777 порт

s.listen(5) — режим ожидания. Одновременно можем работать с 5 запросами. Слушающий процесс обычно находится в цикле ожидания, то есть просыпается при появлении нового соединения

timestr.encode(‘utf-8’) — в сетевых протоколах обмен данными должен выполняться в байтовом формате. Поэтому надо кодировать строки, передаваемые через сеть. Именно по этой причине в программе сервера к отправляемым данным применяется метод encode.

Результат подключения клиента в IDEшке:

Клиентский код:

from socket import * s = socket(AF_INET, SOCK_STREAM) s.connect(('localhost', 7777)) tm = s.recv(1024) s.close() print("Текущее время: %s" % tm.decode('utf-8'))

s = socket(AF_INET,SOCK_STREAM) — создаем сокет

s.connect((‘localhost’, 7777)) — запускаем функцию коннект, где указываем сервер и порт

tm = s.recv(1024) — принимаем не более 1024 байта данных

tm.decode(‘utf-8’) — прежде чем работать с данными, их необходимо декодировать. Для этого в программе клиента к принимаемым данным применяется метод decode(‘utf-8’).

Немного про буфер

Немаловажный фактор при работе с сокетом — это размер буфера. Здесь можно разжиться проблемами на пустом месте. Маленький размер буфера будет обеспечивать скоростную передачу данных, но их объем будет слишком мал.

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

Для установки размера буфера используется метод socket.setsockopt(level, optname, None, optlen: int). Этот метод получает три аргумента: уровень, имя и числовая переменная (сам размер буфера непосредственно)

socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192) # Размер буфера 8192 # или s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1024)

Немного про тайм-аут

Для сокета можно задать таймаут для активности (времени прослушивания) в течение которой он ожидает отправку или получения данных — socket.settimeout(value).Тайм-аут применяется к одному вызову операции чтения/записи сокета.

Аргумент value определяется в секундах. После установленного времени сокет блокируется для операций с ним. Если задан ноль, сокет переводится в неблокирующий режим, в котором операции должны быть выполнены немедленно.

s = socket.create_connection(("kite.com", 80)) s.settimeout(0.00001) try: message = "GET /text HTTP/2.0\r\n\r\n".encode() s.send(message) print(s.recv(1024)) except socket.timeout as e: print(e) # здесь сработает тайм-айут на выходе

Источник

Сокеты в Python для начинающих

В далеком для меня 2010 году я писал статью для начинающих про сокеты в Python. Сейчас этот блог канул в небытие, но статья мне показалась довольно полезной. Статью нашел на флешке в либровском документе, так что это не кросспост, не копипаст — в интернете ее нигде нет.

Что это

Для начала нужно разобраться что такое вообще сокеты и зачем они нам нужны. Как говорит вики, сокет — это программный интерфейс для обеспечения информационного обмена между процессами. Но гораздо важнее не зазубрить определение, а понять суть. Поэтому я тут постараюсь рассказать все как можно подробнее и проще.

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

Рассмотрим это на простом примере. Представим себе большой зал с множеством небольших окошек, за которыми стоят девушки. Есть и пустые окна, за которыми никого нет. Те самые окна — это порты. Там, где стоит девушка — это открытый порт, за которым стоит какое-то приложение, которое его прослушивает. То есть, если, вы подойдете к окошку с номером 9090, то вас поприветствуют и спросят, чем могут помочь. Так же и с сокетами. Создается приложение, которое прослушивает свой порт. Когда клиент устанавливает соединение с сервером на этом порту именно данное приложение будет ответственно за работу этим клиентом. Вы же не подойдете к одному окошку, а кричать вам будут из соседнего 🙂

После успешной установки соединения сервер и клиент начинают обмениваться информацией. Например, сервер посылает приветствие и предложение ввести какую-либо команду. Клиент в свою очередь вводит команду, сервер ее анализирует, выполняет необходимые операции и отдает клиенту результат.

Сервер

Сейчас создайте два файла — один для сервера, а другой для клиента.

В Python для работы с сокетами используется модуль socket:

Прежде всего нам необходимо создать сокет:

Здесь ничего особенного нет и данная часть является общей и для клиентских и для серверных сокетов. Дальше мы будем писать код для сервера. Это вполне логично — зачем нам писать клиентское приложение, если некуда подключаться 🙂

Теперь нам нужно определиться с хостом и портом для нашего сервера. Насчет хоста — мы оставим строку пустой, чтобы наш сервер был доступен для всех интерфейсов. А порт возьмем любой от нуля до 65535. Следует отметить, что в большинстве операционных систем прослушивание портов с номерами 0 — 1023 требует особых привилегий. Я выбрал порт 9090. Теперь свяжем наш сокет с данными хостом и портом с помощью метода bind, которому передается кортеж, первый элемент (или нулевой, если считать от нуля) которого — хост, а второй — порт:

Теперь у нас все готово, чтобы принимать соединения. С помощью метода listen мы запустим для данного сокета режим прослушивания. Метод принимает один аргумент — максимальное количество подключений в очереди. Напряжем нашу бурную фантазию и вспомним про зал с окошками. Так вот этот параметр определяет размер очереди. Если он установлен в единицу, а кто-то, явно лишний, пытается еще подстроится сзади, то его пошлют 🙂 Установим его в единицу:

Ну вот, наконец-то, мы можем принять подключение с помощью метода accept, который возвращает кортеж с двумя элементами: новый сокет и адрес клиента. Именно этот сокет и будет использоваться для приема и посылке клиенту данных.

Вот и все. Теперь мы установили с клиентом связь и можем с ним «общаться». Т.к. мы не можем точно знать, что и в каких объемах клиент нам пошлет, то мы будем получать данные от него небольшими порциями. Чтобы получить данные нужно воспользоваться методом recv, который в качестве аргумента принимает количество байт для чтения. Мы будем читать порциями по 1024 байт (или 1 кб):

while True: data = conn.recv(1024) if not data: break conn.send(data.upper()) 

Как мы и говорили для общения с клиентом мы используем сокет, который получили в результате выполнения метода accept. Мы в бесконечном цикле принимаем 1024 байт данных с помощью метода recv. Если данных больше нет, то этот метод ничего не возвращает. Таким образом мы можем получать от клиента любое количество данных.

Дальше в нашем примере для наглядности мы что-то сделаем с полученными данными и отправим их обратно клиенту. Например, с помощью метода upper у строк вернем клиенту строку в верхнем регистре.

Теперь можно и закрыть соединение:

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

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket sock = socket.socket() sock.bind(('', 9090)) sock.listen(1) conn, addr = sock.accept() print 'connected:', addr while True: data = conn.recv(1024) if not data: break conn.send(data.upper()) conn.close() 

Клиент

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

#!/usr/bin/env python # -*- coding: utf-8 -*- import socket sock = socket.socket() sock.connect(('localhost', 9090)) sock.send('hello, world!') data = sock.recv(1024) sock.close() print data 

Думаю, что все понятно, т.к. все уже разбиралось ранее. Единственное новое здесь — это метод connect, с помощью которого мы подключаемся к серверу. Дальше мы читаем 1024 байт данных и закрываем сокет.

Источник

Оцените статью