Socket.Close действительно не закрывает сокет tcp? (c #)

Кажется, что использование socket.Close () для сокета tcp не полностью закрывает сокет. В следующем примере я пытаюсь подключиться к example.com через порт 9999, который не открыт, и после короткого времени ожидания я пытаюсь закрыть сокет.

  for (int i = 0; i < 200; i++)
  {
    Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    sock.LingerState = new LingerOption(false, 0);
    sock.BeginConnect("www.example.com", 9999, OnSocketConnected, sock);
    System.Threading.Thread.Sleep(50);
    sock.Close();
  }

Но когда я смотрю на netstat после завершения цикла, я обнаруживаю, что есть много полуоткрытых сокетов:

  TCP    israel-xp:6506         www.example.com:9999   SYN_SENT
  TCP    israel-xp:6507         www.example.com:9999   SYN_SENT
  TCP    israel-xp:6508         www.example.com:9999   SYN_SENT
  TCP    israel-xp:6509         www.example.com:9999   SYN_SENT

ИЗМЕНИТЬ. Хорошо, отсутствует какой-то контекст. Я использую beginconnect, потому что ожидаю, что соединение сокета не удастся (9999 не открывается), и в моем реальном коде я вызываю socket.Close () после установки таймера. В OnSocketConnected я вызываю EndConnect, который вызывает исключение (пытается вызвать метод удаленного объекта). Моя цель - иметь короткий тайм-аут для этапа подключения сокета.

Есть какие-нибудь подсказки, что я делаю не так? Спасибо!


person r0u1i    schedule 06.01.2010    source источник


Ответы (4)


Это закроет .NET-часть сокета. Однако в соответствии со спецификацией TCP ОС должна держать лакомые кусочки нижнего уровня сокета открытыми в течение определенного времени, чтобы обнаруживать повторную передачу и тому подобное. В этом конкретном случае, вероятно, сокет немного задержится, чтобы обнаружить ответ на отправленный SYN-пакет, чтобы он мог отвечать более разумно и не путать ответ с другими отправленными пакетами.

person nos    schedule 06.01.2010
comment
это имеет смысл, но разве установка linger на false не должна решить эту проблему? - person r0u1i; 06.01.2010
comment
о, задержка этого не делает. Итак, на самом деле нет способа заставить Windows завершить работу сокета. - person r0u1i; 07.01.2010
comment
по умолчанию он будет находиться в состоянии FIN_WAIT в течение 4 минут, прежде чем Windows действительно закроет его. Еще более интересно, если вы повторно откроете другое соединение с тем же местом назначения / портом, окна будут повторно использовать соединения в FIN_WAIT, которые совпадают. - person Jay; 25.08.2010
comment
@Jay: Последний пункт может вызвать значительное раздражение при работе с оборудованием TCP, которое имеет тенденцию выбирать одни и те же номера портов назначения каждый раз, когда оно включается и запускает новый сокет. Если соединение установлено, когда это произойдет, устройство определит, что номер порта не подходит, и попробует новый номер порта. Однако, если соединение находится в режиме FIN_WAIT, устройству требуется много времени, чтобы понять, что что-то не так. - person supercat; 12.01.2011

По задумке вы всегда должны вызывать Shutdown перед закрытием сокета.

mySocket.Shutdown(SocketShutdown.Both);
mySocket.Close();

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

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

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

person Ed Altorfer    schedule 06.01.2010
comment
ShutDown выдает исключение, если сокет еще не подключен - это как раз мой сценарий. - person r0u1i; 06.01.2010
comment
Затем вы должны использовать OnSocketConnected, чтобы вызвать на нем ShutDown и закрыть его, или что-то в этом роде. - person Ed Altorfer; 06.01.2010
comment
@Dean, это второй вопрос сегодня, на который мы с Джоном ответили одновременно. У него уже слишком много репутации, может, он мне ее даст. :) - person Ed Altorfer; 06.01.2010
comment
Но при вызове ShutDown из OnSocketConnected упускается из виду попытка тайм-аута процесса подключения. Ожидание вызова OnSocketConnected может занять много времени. - person r0u1i; 06.01.2010
comment
Но это нормально, потому что вы больше не заботитесь о сокете. Невозможно полностью просто убить сокет, если вы попытаетесь подключить его, как вы. Вам нужно либо установить флаг, а затем выключить / закрыть его, либо не использовать подобные асинхронные соединения. В качестве альтернативы вы можете просто закрыть его и знать, что НЕКОТОРЫЕ сообщения могут быть отправлены до того, как ОС очистит сокет. - person Ed Altorfer; 07.01.2010

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

Попробуйте подключиться синхронно - или закрыть его в OnSocketConnected, чтобы увидеть эффект закрытия действительно подключенного сокета.

person Jon Skeet    schedule 06.01.2010
comment
Смотрите мою правку. Я специально использую BeginConnect, и мой код ожидает тайм-аута перед вызовом Close. Просто вызов EndConnect для такого сокета (где порт назначения не открыт) просто займет много времени. Или, другими словами, я хочу приостановить этап подключения сокета через X миллисекунд (а не абсурдно большое количество миллисекунд, которое окна имеют по умолчанию). - person r0u1i; 06.01.2010
comment
Я думаю, что вызов Socket.Dispose() - это единственный способ отменить асинхронное соединение с текущим API. Похоже, это сделано специально. - person binki; 21.06.2018

Вы инициализируете новый сокет в каждом цикле ... с помощью * .close () вы закрываете старый и в начале вы создаете новый с теми же параметрами, что и раньше.

person hase    schedule 06.01.2010