Не работают сокеты в python
Во первых этот материал для тех, кто уже пробовал программировать сокеты, а во вторых здесь будет говорится только о сокетах INET (то есть IPv4) STREAM (т.е. TCP), так как они составляют не менее 99% используемых сокетов. От сокета STREAM можно получить лучшую производительность, чем от какого-то другого. Так же приоткроем тайну того, что такое сокет и дадим несколько советов, как работать с блокирующими и неблокирующими сокетами. Начнем разбираться с блокирующих сокетов, т.к. необходимо знать, как они работают, прежде чем работать с неблокирующими сокетами,
Использование сокета.
Первое, на что следует обратить внимание, это то, что клиентский сокет браузера и клиентский сокет веб-сервера - полностью идентичны. То есть, это диалог одноранговый. Обычно сокет, который подключается к серверу начинает диалог, отправляя запрос или возможно, вход в систему. Но это уже решение программиста, а не сокета, как построить диалог.
Для приема/передачи данных можно использовать методы объекта сокета Socket.send() и Socket.recv() , а можно превратить клиентский сокет в файловый объект и использовать чтение/запись. По поводу использования сокета, как файлового объекта, необходимо сделать предупреждение, что в сокетах, при выполнении записи, нужно использовать вызов file.flush() . Сокеты имеют дело с буферизованными "файлами" и распространенной ошибкой является - записать что-то и не вызвать file.flush() , а затем перейти в режим чтения ответа. При этом можно бесконечно долго ждать ответа, т. к. записанные данные все еще могут оставаться в выходном буфере.
Но если в планах повторно использовать открытый сокет для каких-то задач, то нужно понимать, что в сокетах нет EOT - "end of trensfer" (конец файла). ЕЩЕ РАЗ: если сокет после вызова методов Socket.send() или Socket.recv() возвращает 0 байтов, то соединение было разорвано. Если соединение не было разорвано, то можно вечно ждать получения данных вызовом Socket.recv() , т. к. сокет не может сказать, что читать больше нечего.
В интересах создания первого приложения с использованием сокетов, вышесказанные улучшения оставлены как упражнение для читателя.
Пример асинхронного сервера с вызовом select.select() .
В примере вызывается select.select() , для опроса сокетов операционной системой, готовы ли они к записи, чтению или есть ли какое-то ошибки в сокетах. Этот вызов блокирует программу (если не передан аргумент тайм-аута) до тех пор, пока не будут готовы какие нибудь из переданных сокетов. По готовности хоты-бы одного из сокетов, вызов select.select() вернет три списка с сокетами для указанных операций. Затем программа последовательно перебирает эти списки и выполняет соответствующие операции.
Так работают сокеты на низком уровне. В большинстве случаев нет необходимости реализовывать логику на таком низком уровне. Рекомендуется использовать некоторые абстракции более высокого уровня, такие как | Twisted |, | Tornado | или | ZeroMQ |, в зависимости от ситуации.
Простой 3 комментария
Вы бы хоть с редактором тостера разобрались. Код тут можно оформлять по нормальному, и капс в названии необязателен.
И то что возможно указать 5 тегов - вовсе не обозначает что действительно нужно пихать 5 тегов.
Дорогой пользователь, настоятельно рекомендуем еще раз обратить самое пристальное внимание на п. 3.1 регламента работы сервиса (и, в особенности, на его последний абзац).
В противном случае, ваши вопросы будут удаляться по причине тег-спама, а систематические нарушения приведут к блокировке учетной записи.
Так же хочу обратить внимание, что шанс получить ответ на вопрос на много больше в том случае, когда заголовок действительно является вопросительным предложением, кратко передающем суть вопроса, а не просто заканчивается вопросительным знаком.
Внешний IP, скорее всего на роутере, а на роутере - NAT. Нужно настроить проброс портов на комп внутри локальной сети.
Возможно, что внешний IP вообще у провайдера, а вам пров. выдает приватный адрес. В этом случае нужно за деньги покупать белый адрес у оператора.
У меня нет роутера. И без покупки белого адреса можно ли как то обойтись ?
Например программа Radmin. Она имеет клиентскую и серверную часть. И когда человек запускает сервер , то другой пользователь может следить удаленно за его ПК ИЛИ ХАКЕР когда скидует троян(бэкдор) своей жертве.
Как вы подключаетесь к интернет?
Без покупки белого адреса можно обойтись, если завести сервер в интернете уже с белым адресом, например арендовать VPS.
Когда-то пользовался радмином - там стандартная клиент-серверная архитектура, использует протокол TCP, все проблемы с NAT там так же присутствуют. Возможно в современных версиях они каким-то образом обходят это, например так как это делает teamviewer.
И еще: NAT нужно преодолевать только на одной стороне соединения, на той что принимает соединения, т.е. на сервере. Клиенты свой NAT обычно проходят спокойно.
Говорю же у меня кабельный интернет.
А как же бэкдоры, трояны ?
Slavik0981, Пусть будет кабельный, это не мешает иметь роутер, но этое важно.
Какой адрес конкретно на вашем компе вы можете посмотреть в свойствах сети в винде (или ipconfig в cmd) или по команде ifconfig в линуксе.
Бэкдоры и трояны держат снаружи свой сервер, а сами трояны являются клиентом, поэтому НАТ проходят без проблем.
Для TCP трафика преодоление NAT реальная проблема.
Для UDP есть вариант использовать промежуточный STUN/TURN/ICE сервер, но это требует их поддержки в программном обеспечении, так что простым этот вариант тоже назвать нельзя, но он вполне реальный.
В далеком для меня 2010 году я писал статью для начинающих про сокеты в Python. Сейчас этот блог канул в небытие, но статья мне показалась довольно полезной. Статью нашел на флешке в либровском документе, так что это не кросспост, не копипаст — в интернете ее нигде нет.
Что это
Для начала нужно разобраться что такое вообще сокеты и зачем они нам нужны. Как говорит вики, сокет — это программный интерфейс для обеспечения информационного обмена между процессами. Но гораздо важнее не зазубрить определение, а понять суть. Поэтому я тут постараюсь рассказать все как можно подробнее и проще.
Существуют клиентские и серверные сокеты. Вполне легко догадаться что к чему. Серверный сокет прослушивает определенный порт, а клиентский подключается к серверу. После того, как было установлено соединение начинается обмен данными.
Рассмотрим это на простом примере. Представим себе большой зал с множеством небольших окошек, за которыми стоят девушки. Есть и пустые окна, за которыми никого нет. Те самые окна — это порты. Там, где стоит девушка — это открытый порт, за которым стоит какое-то приложение, которое его прослушивает. То есть, если, вы подойдете к окошку с номером 9090, то вас поприветствуют и спросят, чем могут помочь. Так же и с сокетами. Создается приложение, которое прослушивает свой порт. Когда клиент устанавливает соединение с сервером на этом порту именно данное приложение будет ответственно за работу этим клиентом. Вы же не подойдете к одному окошку, а кричать вам будут из соседнего :)
После успешной установки соединения сервер и клиент начинают обмениваться информацией. Например, сервер посылает приветствие и предложение ввести какую-либо команду. Клиент в свою очередь вводит команду, сервер ее анализирует, выполняет необходимые операции и отдает клиенту результат.
Сервер
Сейчас создайте два файла — один для сервера, а другой для клиента.
В Python для работы с сокетами используется модуль socket:
Прежде всего нам необходимо создать сокет:
Здесь ничего особенного нет и данная часть является общей и для клиентских и для серверных сокетов. Дальше мы будем писать код для сервера. Это вполне логично — зачем нам писать клиентское приложение, если некуда подключаться :)
Теперь нам нужно определиться с хостом и портом для нашего сервера. Насчет хоста — мы оставим строку пустой, чтобы наш сервер был доступен для всех интерфейсов. А порт возьмем любой от нуля до 65535. Следует отметить, что в большинстве операционных систем прослушивание портов с номерами 0 — 1023 требует особых привилегий. Я выбрал порт 9090. Теперь свяжем наш сокет с данными хостом и портом с помощью метода bind, которому передается кортеж, первый элемент (или нулевой, если считать от нуля) которого — хост, а второй — порт:
Теперь у нас все готово, чтобы принимать соединения. С помощью метода listen мы запустим для данного сокета режим прослушивания. Метод принимает один аргумент — максимальное количество подключений в очереди. Напряжем нашу бурную фантазию и вспомним про зал с окошками. Так вот этот параметр определяет размер очереди. Если он установлен в единицу, а кто-то, явно лишний, пытается еще подстроится сзади, то его пошлют :) Установим его в единицу:
Ну вот, наконец-то, мы можем принять подключение с помощью метода accept, который возвращает кортеж с двумя элементами: новый сокет и адрес клиента. Именно этот сокет и будет использоваться для приема и посылке клиенту данных.
Вот и все. Теперь мы установили с клиентом связь и можем с ним «общаться». Т.к. мы не можем точно знать, что и в каких объемах клиент нам пошлет, то мы будем получать данные от него небольшими порциями. Чтобы получить данные нужно воспользоваться методом recv, который в качестве аргумента принимает количество байт для чтения. Мы будем читать порциями по 1024 байт (или 1 кб):
Как мы и говорили для общения с клиентом мы используем сокет, который получили в результате выполнения метода accept. Мы в бесконечном цикле принимаем 1024 байт данных с помощью метода recv. Если данных больше нет, то этот метод ничего не возвращает. Таким образом мы можем получать от клиента любое количество данных.
Дальше в нашем примере для наглядности мы что-то сделаем с полученными данными и отправим их обратно клиенту. Например, с помощью метода upper у строк вернем клиенту строку в верхнем регистре.
Теперь можно и закрыть соединение:
Собственно сервер готов. Он принимает соединение, принимает от клиента данные, возвращает их в виде строки в верхнем регистре и закрывает соединение. Все просто :) В итоге у вас должно было получиться следующее:
Клиент
Думаю, что теперь будет легче. Да и само клиентское приложение проще — нам нужно создать сокет, подключиться к серверу послать ему данные, принять данные и закрыть соединение. Все это делается так:
Думаю, что все понятно, т.к. все уже разбиралось ранее. Единственное новое здесь — это метод connect, с помощью которого мы подключаемся к серверу. Дальше мы читаем 1024 байт данных и закрываем сокет.
I'm new to Sockets, please excuse my complete lack of understanding.
I have a server script(server.py):
and client script (client.py):
I go to my desktop terminal and start the script by typing:
after which, I go to my laptop terminal and start the client script:
but I get the following error:
File "client.py", line 9, in
s.connect((host,port))
File "/usr/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
socket.error: [Errno 111] Connection refused
I've tried using different port numbers to no avail. However, I was able to get the host name using the same ip and the gethostname() method in the client script and I can ping the desktop (server).
Try using telnet. It often helps me. Type in your terminal telnet [IP] 12397 (replace IP with what gethostname() returns). If you do it so you should see Thank you for connecting . If not, please show me what telnet returned.
Когда умирают сокеты.
Худшее в использовании блокирующих сокетов - это то, что происходит, когда одна из сторон соединения резко падает (без закрытия). Сокет скорее всего зависнет. TCP - надежный протокол и он будет долго ждать, прежде чем отказаться от соединения. Если использовать потоки, то весь поток практически умрет. С этим ничего не поделаешь и если не делать глупостей, таких как держать блокировку при выполнении чтения, то поток не съест много ресурсов.
Не пытайтесь убить поток - одна из причин того, что потоки более эффективны, чем процессы, заключается в том, что у них нет накладных расходов, связанных с автоматическим повторным использованием ресурсов. Другими словами, если удастся убить поток, то вся программа, скорее всего, упадет.
Что такое сокет и как он создается?
Грубо говоря, когда происходит переход по ссылке на сайте, браузер делает что-то вроде следующего:
Когда устанавливается соединение sock.connect() , то сокет sock можно использовать для отправки текста страницы. Тот же сокет прочитает ответ, а затем будет уничтожен. Клиентские сокеты обычно используются только для разового обмена данными или небольшого набора последовательных обменов данными.
То, что происходит на веб-сервере, немного сложнее. Сначала веб-сервер создает серверный сокет:
Следует отметить пару вещей: в коде выше использовалась функция socket.gethostname() , чтобы сокет был виден внешнему миру. Если использовать вызов serv_sock.bind(('localhost', 443)) или serv_sock.bind(('127.0.0.1', 443)) , то серверный сокет был бы виден только локальной машине. Вызов serv_sock.bind(('', 443)) указывает, что сокет доступен по любому адресу.
Наконец, аргумент 5 вызова serv_sock.listen(5) , говорит модулю socket , чтобы сервер поставил в очередь до 5 клиентов на подключение (нормальный максимум), прежде чем отклонять остальные запросы. Если остальная часть кода написана правильно, то этого должно быть достаточно.
Теперь, когда есть серверный сокет, прослушивающий 443 порт, можно войти в основной цикл веб-сервера:
Существует 3 основных способа, которыми этот цикл может работать:
- диспетчеризация потока для работы с клиентским сокетом,
- создание нового процесса для работы с клиентским сокетом,
- реструктуризация всего приложения для использования неблокирующих сокетов и мультиплексирование между серверным сокетом и любыми активными клиентскими сокетами используя модуль select .
Подробнее об этом позже. Сейчас важно понять, что это все, что делает серверный сокет. Он не отправляет и не получает никаких данных. Он просто воспроизводит/создает клиентские сокеты. Каждый клиентский сокет создается в ответ на то, что какой-то новый клиентский сокет выполняет подключение sock.connect() к серверу на определенный хост и порт. На этот запрос, сервер создает новый клиентский сокет, и как только он это сделает то сразу возвращается к прослушиванию следующих подключений. Два клиента могут свободно общаться, например на каком-нибудь динамически выделенном порту, который будет закрыт после общения.
Неблокирующие сокеты
Чтобы сделать сокет неблокирующим, в Python используют вызов Socket.setblocking(False) . Вызов делается после создания сокета, но перед его использованием. Если программист не очень умный, то скорее всего попытается переключаться туда и обратно (с блокирующего сокета на неблокирующий).
Основное механическое отличие состоит в том, что вызовы Socket.send() , Socket.recv() , Socket.connect() и Socket.accept() могут возвращать результат без каких-либо действий. И здесь есть несколько вариантов. Можно проверить ответ, который вернул соответствующий вызов и код ошибки и вообще свести себя с ума. Если не верите, попробуйте как-нибудь. Приложение будет разрастаться, глючить и загружать процессор. Так что давайте пропустим безумные решения и сделаем все правильно.
Одним из возможных решений является делегирование работы с клиентами отдельным потокам. Однако создание потоков и переключение контекстов между ними на самом деле не является дешевой операцией. Для решения этой проблемы существует так называемый асинхронный способ работы с сокетами. Основная идея состоит в том, чтобы делегировать поддержание состояния сокета операционной системе и позволить ей уведомлять программу, когда есть что-то для чтения из сокета или когда он готов к записи. Для этого можно использовать вызов операционной системы select , подробнее о нем можно посмотреть командой терминала Unix $ man select
В Python такой вызов сделать совсем несложно, для этой цели используйте встроенный модуль select , в частности вызов select.select() :
Здесь передается в select.select() три списка:
- potential_readers содержит все сокеты, которые нужно прочитать,
- potential_writers содержит все сокеты, в которые надо что-то записать,
- potential_errs - которые нужно проверять на наличие ошибок (обычно оставляют пустым).
Следует отметить, что один сокет может входить в разные списки. Вызов select.select() блокируется, но можно задать ему таймаут. Как правило, это разумный поступок - дайте ему длительный таймаут (скажем, минуту), если нет веских причин поступить иначе.
Взамен получаем три списка. Они содержат сокеты, которые действительно доступны для чтения, записи и содержат ошибки. Каждый из этих списков является подмножеством, возможно пустым, соответствующего переданного списка в select.select() .
Если есть "серверный" сокет, то поместим его в список potential_readers . Если он появится в списке ready_to_read , то вызов .accept почти наверняка сработает. Если сервер создал новый сокет для подключения, то поместим его в список potential_writers . Если он отображается в списке ready_to_write , то есть неплохие шансы, что он подключился.
Предупреждение о переносимости: в Unix, модуль select работает как с сокетами, так и с файлами. Не пытайтесь использовать это в Windows. В Windows, модуль select работает только с сокетами. Также обратите внимание, что в языке C многие из более продвинутых параметров сокетов в Windows выполняются иначе. Фактически, в Windows обычно используют потоки, которые работают очень и очень хорошо с сокетами.
Межпроцессорное взаимодействие (IPC).
Если необходим быстрый IPC между двумя процессами на одной машине, то следует изучить каналы Pipe() или общую память (объекты Value() и Array() ). Если все же решите использовать socket.AF_INET сокеты, то необходимо привязать серверный сокет к localhost . На большинстве платформ это позволит сократить несколько уровней сетевого кода и будет работать немного быстрее.
Смотрите также модуль multiprocessing , который интегрирует межплатформенный IPC в API более высокого уровня.
Закрытие соединения сокета.
Строго говоря, сначала необходимо использовать вызов объекта сокета shutdown() , прежде чем закрыть его командой Socket.close() . Вызов Socket.shutdown() - это предупреждение для сокета на другом конце. В зависимости от аргумента, который передавать, это может означать "Я больше не буду отправлять, но я все равно буду слушать" или "Я не слушаю, мне по барабану". Разработчики библиотек сокетов настолько привыкли к тому, что программисты пренебрегают этим элементом этикета, что у некоторых, обычный вызов Socket.close() - означает последовательность вызовов: Socket.shutdown() ; Socket.close() . Поэтому в большинстве ситуаций явный вызов Socket.shutdown() не требуется.
Один из способов эффективного использования shutdown() , это обмен данными, подобный HTTP. Клиент отправляет запрос и затем завершает работу вызовом Socket.shutdown(1) . Это сообщает серверу: "Этот клиент завершил отправку, но все еще может получать". Сервер читая запрос, в конце получает 0 байтов, это сигнализирует о том, что от клиента весь запрос получен и надо готовить и отправлять ответ. Если отправка ответа завершилась успешно, то клиент действительно слушал и получил все отправленные данные.
Python делает еще один шаг к автоматическому завершению соединения, это постоянный мониторинг открытых сокетов сборщиком мусора. Сборщик мусора автоматически закрывает соединение, если это необходимо. Но полагаться на это - очень плохая привычка. Если сокет просто исчезнет без закрытия, то сокет на другом конце может зависнуть бесконечно думая, что сервер просто медленно работает и когда закончит, то закроет сокет.
9 Answers 9
so that the listening socket isn't too restricted. Maybe otherwise the listening only occurs on one interface which, in turn, isn't related with the local network.
One example could be that it only listens to 127.0.0.1 , which makes connecting from a different host impossible.
@PunitSoni Yes, this is standard. If you have a look at, say, a server which offers some services you want to connect to from "everywhere", such as a web server and/or mail and imap server, and you execute netstat -tulpen , you'll notice that there are entries like 0.0.0.0:143 or . 80 . These are bound to the "all zeros" address and do that exactly as noticed aboce.
This error means that for whatever reason the client cannot connect to the port on the computer running server script. This can be caused by few things, like lack of routing to the destination, but since you can ping the server, it should not be the case. The other reason might be that you have a firewall somewhere between your client and the server - it could be on server itself or on the client. Given your network addressing, I assume both server and client are on the same LAN, so there shouldn't be any router/firewall involved that could block the traffic. In this case, I'd try the following:
- check if you really have that port listening on the server (this should tell you if your code does what you think it should): based on your OS, but on linux you could do something like netstat -ntulp
- check from the server, if you're accepting the connections to the server: again based on your OS, but telnet LISTENING_IP LISTENING_PORT should do the job
- check if you can access the port of the server from the client, but not using the code: just us the telnet (or appropriate command for your OS) from the client
and then let us know the findings.
Lack of routing causes a different error, as do all the other things. There is only one thing that causes this error: nothing was listening at that IP:port.
Assume s = socket.socket() The server can be bound by following methods: Method 1:
If you do not exactly use same method on the client side, you will get the error: socket.error errno 111 connection refused.
So, you have to use on the client side exactly same method to get the host, as you do on the server. For example, in case of client, you will correspondingly use following methods:
В разделе рассмотрены ошибки и исключения, определяемые модулем socket , а так же возможные причины их возникновения.
Содержание.
Проблема с пониманием работы сокетов заключается в том, что сокет может означать несколько разных тонких вещей в зависимости от контекста. Итак, сначала проведем различие между клиентским сокетом - конечной точкой диалога и серверным сокетом, который больше похож на оператора коммутатора. Клиентское приложение, например браузер, использует исключительно клиентские сокеты, а веб-сервер, с которым он разговаривает, использует как серверные, так и клиентские сокеты.
Содержание.
Проблема с пониманием работы сокетов заключается в том, что сокет может означать несколько разных тонких вещей в зависимости от контекста. Итак, сначала проведем различие между клиентским сокетом - конечной точкой диалога и серверным сокетом, который больше похож на оператора коммутатора. Клиентское приложение, например браузер, использует исключительно клиентские сокеты, а веб-сервер, с которым он разговаривает, использует как серверные, так и клиентские сокеты.
Содержание:
- socket.error устарело, псевдоним OSError ,
- socket.herror ошибки функций gethostbyname_ex() и gethostbyaddr() ,
- socket.gaierror ошибки функций getaddrinfo() и getnameinfo() ,
- socket.timeout ошибки функций settimeout() и setdefaulttimeout() .
socket.error :
Исключение socket.error устаревший псевдоним исключения OSError .
socket.herror :
Исключение socket.herror являющееся подклассом исключения OSError , возникает для ошибок, связанных с адресом, то есть для функций, использующих h_errno в POSIX C API, включая функции socket.gethostbyname_ex() и socket.gethostbyaddr() .
Сопутствующее значение - это кортеж (h_errno, string) , представляющая ошибку, возвращаемую вызовом библиотеки. Значение h_errno - это числовое значение, в то время как string представляет описание h_errno , возвращаемое функцией hstrerror() языка C.
socket.gaierror :
Исключение socket.gaierror являющееся подклассом исключения OSError , возникает для ошибок, связанных с адресом, в функциях socket.getaddrinfo() и socket.getnameinfo() .
Сопутствующее значение - это парный кортеж (error, string) , представляющая ошибку и возвращаемую вызовом библиотеки. Строка string представляет собой описание ошибки error , возвращаемое функцией gai_strerror() языка C. Числовое значение ошибки будет соответствовать одной из констант socket.EAI_* , определенных в модуле socket .
socket.timeout :
Исключение socket.timeout являющееся подклассом исключения OSError , возникает когда в сокете происходит тайм-аут, для которого были включены тайм-ауты с помощью предыдущего вызова Socket.settimeout() или неявно через функцию socket.setdefaulttimeout() .
Сопутствующее значение представляет собой строку, значение которой в настоящее время всегда: "timed out”.
Прием/передача двоичных данных.
Вполне возможно отправлять двоичные данные через сокет. Основная проблема заключается в том, что не все машины используют одни и те же форматы для двоичных данных. Например, чип Motorola будет представлять 16-битное целое число, например 1 в виде двух шестнадцатеричных байтов - 00 01. Intel и DEC переворачивают байты - то же самое число 1 здесь будет выглядеть как 01 00. Модуль socket имеет функции для преобразования 16 и 32-битных целых чисел - socket.ntohl() , socket.htonl() , socket.ntohs() и socket.htons() , где в названиях первая буква означает n - сеть, а h - хост, а последняя s - короткий, а l - длинный. Там, где сетевой порядок является порядком хоста, функции ничего делать не будут, но там, где машина перевернула байты, они соответствующим образом все поменяют.
Читайте также: