Как закрыть сокет python

Обновлено: 06.07.2024

Сегодня мы рассмотрим пример программирования сокетов Python. Мы создадим серверные и клиентские приложения на Python.

Программирование сокетов

Чтобы понять программирование сокетов Python, нам нужно знать о трех интересных темах – Socket Server, Socket Client и Socket.

Итак, что такое сервер? Сервер – это программное обеспечение, которое ожидает запросов клиентов и обслуживает или обрабатывает их соответственно.

С другой стороны, клиент запрашивает эту услугу. Клиентская программа запрашивает некоторые ресурсы к серверу, и сервер отвечает на этот запрос.

Socket – это конечная точка двунаправленного канала связи между сервером и клиентом. Сокеты могут обмениваться данными внутри процесса, между процессами на одной машине или между процессами на разных машинах. Для любого взаимодействия с удаленной программой мы должны подключаться через порт сокета.

Основная цель этого руководства по программированию сокетов – познакомить вас с тем, как сервер сокетов и клиент взаимодействуют друг с другом. Вы также узнаете, как написать программу сервера сокетов в Python.

Пример

Ранее мы говорили, что клиент сокета запрашивает некоторые ресурсы у сервера, и сервер отвечает на этот запрос.

Итак, мы разработаем и серверную, и клиентскую модель, чтобы каждый мог общаться с ними. Шаги можно рассматривать так:

Сервер сокетов

Мы сохраним программу сервера сокетов, как socket_server.py. Чтобы использовать соединение, нам нужно импортировать модуль сокета.

Затем последовательно нам нужно выполнить некоторую задачу, чтобы установить соединение между сервером и клиентом.

Мы можем получить адрес хоста с помощью функции socket.gethostname(). Рекомендуется использовать адрес порта пользователя выше 1024, поскольку номер порта меньше 1024 зарезервирован для стандартного интернет-протокола.

Смотрите приведенный ниже пример кода сервера:

Итак, наш сервер сокетов работает на порту 5000 и будет ждать запроса клиента. Если вы хотите, чтобы сервер не завершал работу при закрытии клиентского соединения, просто удалите условие if и оператор break. Цикл while используется для бесконечного запуска серверной программы и ожидания клиентского запроса.

Клиент сокета

Мы сохраним клиентскую программу сокета python как socket_client.py. Эта программа похожа на серверную, за исключением привязки.

Основное различие между серверной и клиентской программой состоит в том, что в серверной программе необходимо связать адрес хоста и адрес порта вместе.

Смотрите ниже пример кода клиента сокета:

Вывод

Чтобы увидеть результат, сначала запустите программу сервера сокетов. Затем запустите клиентскую программу. После этого напишите что-нибудь из клиентской программы. Затем снова напишите ответ от серверной программы.

Наконец, напишите «до свидания» из клиентской программы, чтобы завершить обе программы. Ниже короткое видео покажет, как это работало на моем тестовом прогоне примеров программ сервера сокетов и клиента.

python socket programming, python socket server

Обратите внимание, что сервер сокетов работает на порту 5000, но клиенту также требуется порт сокета для подключения к серверу. Этот порт назначается случайным образом при вызове клиентского соединения. В данном случае это 57822.

Сокеты используются почти повсюду, но это одна из самых неправильно понимаемых технологий. Это обзор сокетов c высоты 10 000 футов. На самом деле это не учебное пособие — вам всё равно придется поработать, чтобы всё заработало. Он не охватывает тонких моментов (а их много), но я надеюсь, что он даст вам предоставит информации, чтобы начать их прилично использовать.

Сокеты¶

Я собираюсь говорить только о сокетах INET (то есть IPv4), но они составляют не менее 99% используемых сокетов. И я буду говорить только о сокетах STREAM (т.е. TCP) — если вы действительно не знаете, что делаете (в этом случае это HOWTO не для вас!), вы получите лучшее поведение и производительность от сокета STREAM, чем что-нибудь ещё. Я постараюсь раскрыть тайну того, что такое сокет, а также несколько подсказок, как работать с блокирующими и неблокирующими сокетами. Но начну с блокировки сокетов. Вам нужно знать, как они работают, прежде чем иметь дело с неблокирующими сокетами.

Отчасти проблема с пониманием этих вещей заключается в том, что «сокет» может означать несколько разных вещей в зависимости от контекста. Итак, сначала давайте проведём различие между «клиентским» сокетом — конечной точкой диалога и «серверным» сокетом, который больше похож на оператора коммутатора. Клиентское приложение (например, ваш браузер) использует исключительно «клиентские» сокеты; веб-сервер, с которым он разговаривает, использует как «серверные», так и «клиентские» сокеты.

История¶

Они были изобретены в Беркли как часть разновидности BSD Unix. Они распространяются по интернету, как лесной пожар. Не зря комбинация сокетов с INET делает общение с произвольными машинами по всему миру невероятно простым (по крайней мере, по сравнению с другими схемами).

Создание сокета¶

Грубо говоря, когда вы нажимали на ссылку, которая привела вас на эту страницу, ваш браузер делал что-то вроде следующего:

После завершения connect сокет s можно использовать для отправки запроса текста страницы. Тот же сокет прочитает ответ и затем будет уничтожен. Правильно, уничтожен. Клиентские сокеты обычно используются только для одного обмена (или небольшого набора последовательных обменов).

То, что происходит на веб-сервере, немного сложнее. Сначала веб-сервер создаёт «серверный сокет»:

Пара замечаний: мы использовали socket.gethostname() , чтобы сокет был виден внешнему миру. Если бы мы использовали s.bind(('localhost', 80)) или s.bind(('127.0.0.1', 80)) , у нас всё ещё был бы «серверный» сокет, но тот, который был бы виден только на той же машине. s.bind(('', 80)) указывает, что сокет доступен по любому адресу, который есть у машины.

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

Теперь, когда у нас есть «серверный» сокет, прослушивающий порт 80, мы можем войти в основной цикл веб-сервера:

На самом деле существует 3 общих способа, которыми этот цикл может работать — диспетчеризация потока для обработки clientsocket , создание нового процесса для обработки clientsocket или реструктуризация этого приложения для использования неблокирующих сокетов и мультиплексирование между нашим «серверным» сокетом и любым активным clientsocket с использованием select . Подробнее об этом позже. Сейчас важно понять следующее: это всё, что делает «серверный» сокет. Он не отправляет никаких данных. Он не получает никаких данных. Он просто производит «клиентские» сокеты. Каждый clientsocket создается в ответ на то, что какой-то другой «клиентский» сокет выполняет connect() для хоста и порта, к которому мы привязаны. Как только мы создали clientsocket , мы вернёмся к прослушиванию дополнительных подключений. Два «клиента» могут свободно общаться — они используют какой-то динамически выделяемый порт, который будет повторно использован по окончании разговора.

Если вам нужен быстрый IPC между двумя процессами на одной машине, вам следует изучить каналы (pipes) или общую память. Если вы всё же решите использовать сокеты AF_INET, привяжите «серверный» сокет к 'localhost' . На большинстве платформ это позволит сократить несколько уровней сетевого кода и будет работать немного быстрее.

multiprocessing интегрирует межплатформенный IPC в API более высокого уровня.

Использование сокета¶

Первое, что следует отметить, это то, что «клиентский» сокет веб-браузера и «клиентский» сокет веб-сервера — идентичные объекты. Т. е. это «одноранговый» диалог. Или, другими словами, вам как дизайнеру придется решать, каковы правила этикета для разговора. Обычно сокет connect начинает диалог, отправляя запрос или, возможно, вход в систему. Но это дизайнерское решение — это не правило сокетов.

Теперь есть два набора глаголов, которые можно использовать для общения. Вы можете использовать send и recv , или вы можете превратить свой клиентский сокет в файловый объект и использовать read и write . Последний способ представления сокетов в Java. Я не собираюсь здесь об этом говорить, кроме как предупредить вас, что вам нужно использовать flush на сокетах. Это буферизованные «файлы», и распространенная ошибка - сначала что-то write , а затем read для ответа. Без flush вы можете бесконечно ждать ответа, потому что запрос может всё ещё находиться в вашем выходном буфере.

В интересах пространства, построения вашего персонажа (и сохранения моей конкурентной позиции) эти улучшения оставлены в качестве упражнения для читателя. Переходим к уборке.

Двоичные данные¶

Вполне возможно отправлять двоичные данные через сокет. Основная проблема заключается в том, что не все машины используют одинаковые форматы двоичных данных. Например, микросхема Motorola будет представлять 16-битное целое число со значением 1 как два шестнадцатеричных байта 00 01. Intel и DEC, однако, перевернуты байтами, т. е. та же 1 равна 01 00. В библиотеках сокетов есть вызовы для преобразования 16 и 32-битные целые числа — ntohl, htonl, ntohs, htons , где «n» означает сеть, а «h» означает хост, «s» означает короткий, а «l» означает длинный. Там, где сетевой порядок — это порядок хоста, они ничего не делают, но если машина с инвертированными байтами, они меняют байты соответствующим образом.

Отключение¶

Строго говоря, вы должны использовать shutdown на сокете, прежде чем использовать close . shutdown — это совет для сокетов на другом конце. В зависимости от аргумента, который вы передаёте, это может означать «Я больше не буду отправлять, но я все равно буду слушать» или «Я не слушаю, скатертью дорога!». Однако большинство библиотек сокетов настолько используются программистами, которые пренебрегают этим правилом этикета, что обычно close совпадает с shutdown(); close() . Поэтому в большинстве случаев явный shutdown не требуется.

Python делает автоматическое завершение ещё одним шагом вперед и говорит, что когда сокет собирает мусор, он автоматически выполнит close , если это необходимо. Но полагаться на это — очень плохая привычка. Если ваш сокет просто исчезнет, не выполнив close , сокет на другом конце может зависнуть бесконечно, думая, что вы просто медлите. Пожалуйста, close ваши сокеты, когда закончите.

Когда сокеты умирают¶

Вероятно, худшее в использовании блокирующих сокетов — это то, что происходит, когда другая сторона жёстко прерывает (без выполнения close ). Ваш сокет, скорее всего, зависнет. TCP — надежный протокол, и он будет долго ждать, прежде чем отказаться от соединения. Если вы используете потоки, весь поток практически мертв. Вы мало что можете с этим поделать. Пока вы не делаете ничего глупого, например, удерживаете блокировку при выполнении блокирующего чтения, поток на самом деле не потребляет много ресурсов. Не пытайтесь убить поток — одна из причин того, что потоки более эффективны, чем процессы, заключается в том, что они избегают накладных расходов, связанных с автоматическим повторным использованием ресурсов. Другими словами, если вам удастся убить поток, весь ваш процесс, скорее всего, будет испорчен.

Неблокирующие сокеты¶

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

В Python вы используете socket.setblocking(0) , чтобы сделать его неблокирующим. В C это более сложно (с одной стороны, вам нужно будет выбирать между версией BSD O_NONBLOCK и почти неотличимой версией POSIX O_NDELAY , которая полностью отличается от TCP_NODELAY ), но это точно такая же идея. Вы выполняете это после создания сокета, но перед его использованием. (На самом деле, если вы не в себе, вы можете переключаться туда и обратно.)

Основное механическое отличие состоит в том, что send , recv , connect и accept могут вернуться, ничего не сделав. У вас (конечно) есть несколько вариантов. Вы можете проверить код возврата и коды ошибок и вообще свести себя с ума. Если вы мне не верите, попробуйте как-нибудь. Ваше приложение будет разрастаться, глючить и забирать процессор. Так что давайте пропустим безумные решения и сделаем всё правильно.

В C кодирование select довольно сложно. В Python проще, но он настолько близок к версии C, что, если вы понимаете select в Python, у вас не будет проблем с этим в C:

Вы передаёте select три списка: первый содержит все сокеты, которые вы, возможно, захотите прочитать; второй — все сокеты, в которые вы, возможно, захотите попробовать писать, и последний (обычно оставленный пустым) те, которые вы хотите проверить на наличие ошибок. Следует отметить, что сокет может входить в несколько списков. Вызов select блокируется, но вы можете дать ему тайм-аут. Как правило, это разумный поступок — дайте ему длительный тайм-аут (скажем, минуту), если у вас нет веской причины поступить иначе.

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

Если у вас есть сокет «server», поместите его в список potential_readers. Если он появится в списке для чтения, ваш accept (почти наверняка) будет работать. Если вы создали новый сокет для connect для кого-то ещё, поместите его в список potential_writers. Если он отображается в списке, доступном для записи, у вас есть неплохие шансы, что он подключился.

Собственно, select может пригодиться даже с блокировкой сокетов. Это один из способов определить, будете ли вы блокировать — сокет возвращается как доступный для чтения, когда в буферах что-то есть. Однако это всё ещё не помогает с проблемой определения, занят ли другой конец или просто занят чем-то другим.

Предупреждение о переносимости: в Unix select работает как с сокетами, так и с файлами. Не пытайтесь это сделать в Windows. В Windows select работает только с сокетами. Также обратите внимание, что в C многие из более продвинутых параметров сокетов в Windows выполняются иначе. Фактически, в Windows я обычно использую потоки (которые работают очень и очень хорошо) с моими сокетами.

Для создания сокета существует функция, называемая socket . Она принимает аргументы family , type и proto (подробнее см. в документации). Чтобы создать TCP-сокет, нужно использовать socket.AF_INET или socket.AF_INET6 для family и socket.SOCK_STREAM для type .

Пример Python socket:

Функция возвращает объект сокета, который имеет следующие основные методы:

  • bind()
  • listen()
  • accept()
  • connect()
  • send()
  • recv()

Здесь мы создаем серверный сокет, привязываем его к localhost и 50000-му порту и начинаем прослушивать входящие соединения.

Чтобы принять входящее соединение, мы вызываем метод accept() , который будет блокироваться до тех пор, пока не подключится новый клиент. Когда это произойдет, метод создаcт новый сокет и вернет его вместе с адресом клиента.

Затем он в бесконечном цикле считывает данные из сокета партиями по 1024 байта, используя метод recv() , пока не вернет пустую строку. После этого он отправляет все входящие данные обратно, используя метод sendall() , который в свою очередь многократно вызывает метод send() . И после этого сервер просто закрывает клиентское соединение. Данный пример может обрабатывать только одно входящее соединение, потому что он не вызывает accept() в цикле.

Код на стороне клиента выглядит проще:

Вместо методов bind() и listen() он вызывает только метод connect() и сразу же отправляет данные на сервер. Затем он получает обратно 1024 байта, закрывает сокет и выводит полученные данные.

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

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

Существует множество интерфейсов для разных операционных систем:

Все они примерно одинаковы, поэтому давайте создадим сервер с помощью Python select. Пример Python select :

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

Создание серверного сокета происходит так же, кроме одной строки: server.setblocking(0) . Это нужно для того, чтобы сокет не блокировался. Такой сервер более продвинутый, поскольку он может обслуживать более одного клиента. Главная причина заключается в сокетах selecting :

Этот вызов (если не передан аргумент timeout ) блокирует программу до тех пор, пока какие-либо из переданных сокетов не будут готовы. В этот момент вызов вернет три списка сокетов для указанных операций.

Так работают сокеты на низком уровне. Однако в большинстве случаев нет необходимости реализовывать настолько низкоуровневую логику. Рекомендуется использовать более высокоуровневые абстракции, такие как Twisted, Tornado или ZeroMQ, в зависимости от ситуации.

Протокол

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

Протокол UDP — низкоуровневый. С его помощью компьютеры могут отправлять и получать информацию в виде отдельных пакетов, не создавая логическое соединение. В отличие от TCP, взаимодействия по протоколу UDP не отличаются надежностью. Это усложняет управление ими в приложениях, в которых при обмене информацией нужны гарантии. Поэтому большинство интернет-приложений используют TCP.


Работа с сокетами в Python

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

ОбщиеСерверныеКлиентские
socket — создать сокетbind — привязать сокет к IP-адресу и порту машиныconnect — установить соединение
send — передать данныеlisten — просигнализировать о готовности принимать соединения
recv — получить данныеaccept — принять запрос на установку соединения
close — закрыть соединение

Работа ТСР протокола

Чтобы понять, как с сокетом работает протокол ТСР, посмотрим на изображение ниже. Пояснение будет в коде программы (для примера мы отправляем клиенту текущее время)


Серверная часть:

Если вы работаете в среде программирования, то разрешите вашему серверу работать в вашей локальной сети:


Клиентская часть

from socket import *

Результат клиентской части (после запуска сервера):


Результат серверной части (после подключения клиента):

Как происходит кодирование/декодирование данных?

Строки, байты, изменяемые строки байтов:

from socket import *
import time

JSON Instant Messaging

Весь скомпилированный JSON-объект должен уложиться в 640 символов.

Аутентификация

Для того, чтобы инициализировать процесс аутентификации, надо создать такой JSON-объект:

Ответы сервера будут содержать поле response, и может быть еще одно (необязательное) поле alert/error с текстом ошибки.

Подключение, отключение, авторизация

В сети/ не в сети

В свою очередь, сервер посылает специальный probe-запрос для проверки доступности клиента:

Алерты и ошибки сервера

Тоже самое, что и отправка обычному пользователю, только в поле to ставится решетка с названием чатрума

Читайте также: