Есть ли способ открыть сокет? - PullRequest
7 голосов
/ 11 сентября 2009

Я создаю много «краткосрочных» сокетов в некотором коде, который выглядит так:

nb=1000
for i in range(nb):
    sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sck.connect((adr, prt)
    sck.send('question %i'%i)
    sck.shutdown(SHUT_WR)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
    sck.close()

Это прекрасно работает, пока nb достаточно "маленький".

Поскольку nb может быть довольно большим, я бы хотел сделать что-то вроде этого

sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.connect((adr, prt)
for i in range(nb):
    reopen(sck) # ? ? ?
    sck.send('question %i'%i)
    sck.shutdown(SHUT_WR)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
sck.close()

Итак, вопрос:
Есть ли способ «повторно использовать» сокет, который был отключен?

Ответы [ 4 ]

17 голосов
/ 11 сентября 2009

Нет, это ограничение базовых сокетов C (и протокола TCP / IP в этом отношении). Мой вопрос к вам: почему вы закрываете их, когда вы можете создать свое приложение для их использования?

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

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

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

Что-то вроде:

sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.connect((adr, prt)
for i in range(nb):
    sck.send('question %i'%i)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
sck.close()

Обновление:

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

for i in range(nb):
    sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sck.connect((adr, prt)

    while sck.error() == NO_SOCKETS_AVAIL:
        sleep 250 milliseconds
        sck.connect((adr, prt)

    sck.send('question %i'%i)
    sck.shutdown(SHUT_WR)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
    sck.close()

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

3 голосов
/ 11 сентября 2009

Я не уверен, на что будут похожи дополнительные издержки, но вы можете полностью закрыть и снова открыть сокет. Вам нужно установить SO_REUSEADDR и привязать к определенному порту, который вы можете использовать повторно.

sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1 голос
/ 11 сентября 2009

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

Если у вас много краткосрочных сокетов, вы также можете рассмотреть датаграммы сокетов (UDP). Обратите внимание, что у вас нет гарантии для прибытия в этом случае, также порядок пакетов не гарантируется.

0 голосов
/ 11 сентября 2009

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

Номера портов - 16 бит, поэтому их всего 65536. Если вы работаете в Windows или Mac OS X, то по умолчанию эфемерные порты выбираются в диапазоне от 49152 до 65535. Это официальный диапазон , обозначенный IANA , но в Linux и Solaris (часто используется для большого трафика серверы) диапазон по умолчанию начинается с 32768, чтобы учесть больше портов. Возможно, вы захотите внести аналогичные изменения в вашу систему, если она еще не настроена таким образом, и вам нужны дополнительные эфемерные порты.

Также возможно уменьшить максимальное время жизни сегмента в вашей системе, сократив время, в течение которого каждый сокет находится в состоянии TIME_WAIT, или использовать SO_REUSEADDR или SO_LINGER в некоторых случаях для повторного использования портов до время истекло. Однако это может, по крайней мере, теоретически привести к тому, что старые соединения будут смешаны с более новыми соединениями, которые используют один и тот же номер порта, если некоторые пакеты от более старых соединений приходят медленно, поэтому, как правило, это не очень хорошая идея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...