Перейти к содержимому

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

  • автор:

Как закрыть сокет если к нему не подключились за определённый промежуток времени?

Есть функция которая ожидает когда к сокету подключатся несколько раз, (в примере ниже — 4 раза) Эта функция работает, но как сделать так чтобы если к сокету за минуту ничего не подключилось то функция завершалась Код:

from socket import socket def waitcon(num): sock = socket() sock.bind(('127.0.0.1', 45686)) sock.listen(num) for _ in range(num): sock.accept() print("Accepted") waitcon(4) 

Отслеживать
Павел Лядов
задан 2 ноя 2021 в 13:10
Павел Лядов Павел Лядов
91 6 6 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

если это линукс, то тут просто — заводим таймер и запускаем. процедуру accept. По выходу с процедуры таймер сбрасываем. Если accept задерживается, то прервём его исключением. Код будет такой

from socket import socket import signal, os def handler(signum, frame): print("alarm", signum) raise OSError("No connection") def waitcon(num): sock = socket() sock.bind(('127.0.0.1', 45686)) sock.listen(num) for _ in range(num): signal.alarm(5) try: sock.accept() print("Accepted") except OSError: print("sorry, no one wants to connect to you") signal.alarm(0); signal.signal(signal.SIGALRM, handler) waitcon(4) 

Но в целом, в питоне есть батарейки из коробки:) И по ссылке https://stackoverflow.com/questions/7354476/python-socket-object-accept-time-out можно найти пример. Я его чуточку адаптировал, но не проверял полностью в отличии от первого примера.

import socket tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) tcpServer.settimeout(60) # timeout for listening tcpServer.bind(('127.0.0.1', 45686)) # IP and PORT tcpServer.listen(1) stopped = False retry = 0 while not stopped: if retry == 4: break try: (conn, (ip, port)) = tcpServer.accept() except socket.timeout: pass except: raise else: # work with the connection, create a thread etc. retry=retry+1 

Python как закрыть упавший сокет?

Сервер открывает сокет и слушает его, потом из-за ошибки закрывается программа, при перезапуске он пишет что адрес уже используется.

p.s. на моменте разработки и тестирования пока не ставлю with и try

Лучший ответ

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

При использовании менеджера контекста гарантируется освобождение ресурса, потому как метод __exit__ вызывается всегда, даже при возникновении исключения. Например:

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
__s.bind((HOST, PORT))
__s.listen()
__conn, addr = s.accept()
__with conn:
____print(f»Connected by «)
____while True:
______data = conn.recv(1024)
______if not data: break
______conn.sendall(data)

Менеджеры контекста в данном случае использовать удобней. Тут подробнее:
medium.com/swlh/3-ways-to-create-context-managers-in-python-a88e3ba536f3
docs.python.org/3/library/contextlib.html

При использовании обработки исключений блок finally выполняется в любом случае, потому в нем можно гарантированно закрыть сокет. Например:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
__s.connect((TCP_IP, TCP_PORT))
__s.sendall(MESSAGE)
__data = s.recv(BUFFER_SIZE)
except socket.error:
__print(‘ERROR!’)
finally:
__s.close()

Если у тебя уже повис открытый сокет, можно вручную закрыть через cmd, например:

netstat -ano | findstr :твой_порт
taskkill /pid айди_процесса /F

Остальные ответы

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

зависит от того, как выглядит падение
вообще говоря, для перебития TIME_WAIT существует SO_REUSEADDR, но не стоит умышленно использовать её в качестве синей изоленты для говнокода

ошибки нужно по возможности обрабатывать нормально и не падать, так что вот это «на моменте разработки и тестирования пока не ставлю with и try» звучит как какой-то бред
попробуй всё-таки ставить для разнообразия, может, проблема решится сама собой)

Михаил БарашковУченик (238) 1 год назад

пока я экспериментирую со схемами и алгоритмами, не ставлю try, чтобы видеть конкретно все ошибки, просто не удобно каждый раз при перезапуске сервера ждать time_wait

user49913 Просветленный (38243) ну тебе ничто не мешает словить исключение, распечатать стектрейс, но падать не целиком, а только уронить хендлер конкретного канала с клиентом не знаю, что ты пишешь, но http-серверы, например, именно так и делают, когда пятисотятся впрочем, ладно, тебе видней

Михаил БарашковУченик (238) 1 год назад

Когда я делаю except Exception as err: print(err)
он выводит только ошибку, а если без try except то там пишет и в какой строке и все такое

Как правильно закрыть сокет в Python?

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

  • Вопрос задан более трёх лет назад
  • 4346 просмотров

2 комментария

Простой 2 комментария

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

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

«Со́кеты (англ. socket — разъём) — название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения.» © Википедия

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

Для работы с сокетами нам нужно импортировать соответствующий модуль.

import socket

Теперь нужно создать сам сокет.

sock = socket.socket()

Теперь у нас есть сокет в переменной sock, и мы можем работать с ним дальше.

Сервер TCP

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

Данные в TCP — это поток байтов. Разделять его на отдельные сообщения придётся самой программе.

Сокет для TCP-соединения создаётся как обычно. Для создания сервера нужно связать сокет с одним или всеми из имеющихся у компьютера хостов (IP-адресов) и каким-либо свободным портом. Если не указать хост или указать «0.0.0.0», сокет будет прослушивать все хосты. Если указать «127.0.0.1», то подключиться можно будет только с этого же компьютера.

Для привязки используется функция bind сокета, которая принимает массив, содержащий два элемента: хост и порт.

sock.bind( ("", 14900) )

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

sock.listen(10)

Теперь принимаем соединения с помощью функции accept. Она ждёт появление входящего соединения и возвращает связанный с ним сокет и адрес подключившегося. Адрес — массив, состоящий из IP-адреса и порта.

conn, addr = sock.accept()

В объекте conn теперь у нас сокет, через который мы можем обмениваться данными с клиентом, в addr[0] — IP-адрес подключившегося клиента. Чтобы получить следующего клиента, нужно вызвать функцию accept ещё раз, при этом необязательно закрывать соединение с предыдущим клиентом: соединений может быть условно неограниченное количество.

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

data = conn.recv(16384)

Тип возвращаемых данных — bytes. У этого типа есть почти все методы, что и у строк, но для того, чтобы использовать из него текстовые данные с другими строками (складывать, например, или искать строку в данных, или печатать), придётся декодировать данные (или их часть, если вы обработали байты и выделили строку) и использовать уже полученную строку. (Здесь и далее используется кодировка utf-8, если вы вдруг по какой-то глупости используете другую кодировку — указывайте свою.)

udata = data.decode("utf-8")
print("Data: " + udata)

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

>>> print("Data: "+data)
Traceback (most recent call last):
File "", line 1, in module>
TypeError: Can't convert 'bytes' object to str implicitly

Для отправки данных в сокет используется функция send. Принимает она тоже bytes, поэтому для отправки строки вам придётся её закодировать.

conn.send(b"Hello!\n")
conn.send(b"Your data: " + udata.encode("utf-8"))

Вот так с помощью функций recv и send и осуществляется весь обмен данными в TCP-соединении.

После всего и клиенту, и серверу необходимо закрыть сокет с помощью функции close.

conn.close()

Теперь этот сокет использовать нельзя.

В случае, если другая сторона сторона закроет сокет, функция recv вернёт пустой объект bytes.

Если данных приходится ждать слишком долго, можно перед использованием функции recv задать (однократно) таймаут с помощью функции settimeout.

conn, addr = sock.accept() # старая строка получения сокета
conn.settimeout(60) # установка таймаута
data = conn.recv(16384) # получение данных, про это рассказано выше

Теперь, если за 60 секунд не придут никакие данные, функция recv вернёт пустой объект bytes, как и при закрытом соединении.

if not data: 
print("No data")
conn.close()

Для примера и закрепления всего прочитанного привожу простенький HTTP-сервер, возвращающий текущие дату и время.

Запустите его и наберите в браузере адрес http://localhost:8080/time.html

Показать код

#!/usr/bin/env python3

import time
import socket

def send_answer(conn, status="200 OK", typ="text/plain; charset=utf-8", data=""):
data = data.encode("utf-8")
conn.send(b"HTTP/1.1 " + status.encode("utf-8") + b"\r\n")
conn.send(b"Server: simplehttp\r\n")
conn.send(b"Connection: close\r\n")
conn.send(b"Content-Type: " + typ.encode("utf-8") + b"\r\n")
conn.send(b"Content-Length: " + bytes(len(data)) + b"\r\n")
conn.send(b"\r\n")# после пустой строки в HTTP начинаются данные
conn.send(data)

def parse(conn, addr):# обработка соединения в отдельной функции
data = b""

while not b"\r\n" in data: # ждём первую строку
tmp = conn.recv(1024)
if not tmp: # сокет закрыли, пустой объект
break
else:
data += tmp

if not data: # данные не пришли
return # не обрабатываем

udata = data.decode("utf-8")

# берём только первую строку
udata = udata.split("\r\n", 1)[0]
# разбиваем по пробелам нашу строку
method, address, protocol = udata.split(" ", 2)

if method != "GET" or address != "/time.html":
send_answer(conn, "404 Not Found", data="Не найдено")
return

answer = """"""
answer += """Время

"""
answer += time.strftime("%H:%M:%S %d.%m.%Y")
answer += """

"""


send_answer(conn, typ="text/html; charset=utf-8", data=answer)


sock = socket.socket()
sock.bind( ("", 8080) )
sock.listen(5)

try:
while 1: # работаем постоянно
conn, addr = sock.accept()
print("New connection from " + addr[0])
try:
parse(conn, addr)
except:
send_answer(conn, "500 Internal Server Error", data="Ошибка")
finally:
# так при любой ошибке
# сокет закроем корректно
conn.close()
finally: sock.close()
# так при возникновении любой ошибки сокет
# всегда закроется корректно и будет всё хорошо
TCP-клиент

HTTP-сервер с браузером это, конечно, хорошо, но вы же тут все хотите онлайн-игры делать ;D Поэтому придётся научиться делать программу клиентом. Сокет создаётся точно так же:

conn = socket.socket()

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


conn.connect( ("127.0.0.1", 14900) )

А дальше всё как обычно: для установки таймаута используется settimeout, для обмена данными send и recv, для закрытия close.

conn.send(b"Hello! \n")
data = b""
tmp = conn.recv(1024)
while tmp:
data += tmp
tmp = conn.recv(1024)
print( data.decode("utf-8") )
conn.close()

Работающий пример

Те, кто собрался делать крутые онлайн-игры в Blender Game Engine, столкнутся с тем, что функции accept и recv ждут соединения и данных, и в результате игра виснет на время ожидания, что плохо. Блокировку с recv поможет снять функция setblocking(0). Тогда в случае отсутствия данных функция не будет ждать, а выкинет исключение socket.error, которое можно будет поймать в блоке try-except, после чего спокойно завершить скрипт, не вешая игру.

# при открытии соединения:
conn = socket.socket()
conn.connect( ("yandex.ru", 80) )
conn.setblocking(0)

# в скрипте, читающем данные:
try: data = conn.recv(1024)
except socket.error: # данных нет
pass # тут ставим код выхода
else: # данные есть
print(data)
# если в блоке except вы выходите,
# ставить else и отступ не нужно
# скрипт, читающий данные, запускаем на каждом кадре

Аналогично с функцией accept.

# при создании сервера:
conn = socket.socket()
conn.bind( ("", 8989) )
conn.listen(5)
conn.setblocking(0)

# в скрипте, который получает клиентов:
try: client, addr = conn.accept()
except socket.error: # данных нет
pass # тут ставим код выхода
else: # данные есть
client.setblocking(0) # снимаем блокировку и тут тоже
parse(client, addr)
# если в блоке except вы выходите,
# ставить else и отступ не нужно
# скрипт, получающий клиентов, запускаем на каждом кадре

Но, конечно, для сервера обрабатывать, например, под сотню клиентов, у каждого просить recv и прочее это издевательство, и тут будет лучше использовать асинхронный сервер на базе asyncio или gevent, а тяжёлые CPU-bound задачи выносить в отдельные потоки (хотя в контексте Blender Game Engine это не обязательно). Но это уже темы для других постов.

Для обработки текстовых данных ознакомьтесь с функцями для работы со строками, с помощью них уже можно будет составить простой текстовый протокол и, закодироав координаты кубика с помощью str, соединив через join, отправив закодированное по сокету и разбив присланное на другом компьютере, двигать кубик по сети. (Но лучше, конечно, какой-нибудь pickle использовать, а для серьёзной игры свой бинарный протокол, чтобы трафик впустую не тратился.)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *