Winsock2: как разрешить ТОЛЬКО одно клиентское соединение за раз, используя отставание прослушивания в VC++

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

Я использую код (приведенный ниже), но когда я запускаю 2 клиента один за другим, оба подключаются. Я использую VC++ с winsock2.

слушать(m_socket,-1);

прохождение нуля, так как отставание также не работает.

Ждем вашего ответа.

С уважением,
имми


person immi    schedule 15.10.2009    source источник


Ответы (4)


Если вы действительно можете ограничить свое приложение использованием только Winsock 2, вы можете использовать его механизм условного принятия:

SOCKET sd = socket(...);
listen(sd, ...);
DWORD nTrue = 1;
setsockopt(sd, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (char*)&nTrue, sizeof(nTrue));

Это изменяет поведение стека, чтобы он автоматически не отправлял ответы SYN-ACK на входящие пакеты SYN, пока установлено соединение. резервное место доступно. Вместо этого ваша программа получает сигнал о том, что она должна принимать соединение как обычно -- select(), WSAEventSelect(), WSAAsyncSelect()... -- тогда вы вызываете WSAAccept() вместо accept():

sockaddr_in sin;
WSAAccept(sd, (sockaddr*)&sin, sizeof(sin), ConditionalAcceptChecker, 0);

Вы пишете функцию ConditionalAcceptChecker(), чтобы просмотреть информацию о входящем соединении и решить, принимать ли соединение. В вашем случае вы можете просто вернуть CF_REJECT, если вы уже обрабатываете соединение.

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

person Warren Young    schedule 15.10.2009
comment
Хороший ответ, Уоррен Янг. Спасибо за ваше время и ответ. - person immi; 15.10.2009
comment
Мило, я и забыл об этом :) Что видит клиент, если сервер отклоняет соединение? - person Len Holgate; 15.10.2009
comment
RST на уровне TCP, WSAECONNREFUSED на уровне Winsock. - person Warren Young; 15.10.2009
comment
Уоррен Янг, поскольку твой ответ предполагает, что я хочу сделать, я принимаю его. Еще раз спасибо вам и всем, кто ответил, особенно Лену Холгейту, который дал несколько действительно хороших предложений. Мои наилучшие пожелания для всех вас. - person immi; 19.10.2009

Вы можете установить отставание равным 1, так как это желаемое количество подключений.

Но, насколько я знаю, жестких гарантий по размеру очереди нет (этот документ говорит об этом будет 1,5 * отставание в BSD, например).

ИМХО, вам лучше контролировать количество соединений вручную, не принимая () соединения после некоторого ограничения.

person elder_george    schedule 15.10.2009
comment
Elder_george, Большое спасибо за ответ на вопрос. Но есть ли способ, которым клиентский запрос на подключение терпит неудачу всякий раз, когда он пытается подключиться к tcp-серверу. - person immi; 15.10.2009

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

person jdehaan    schedule 15.10.2009

Журнал прослушивания не для этого.

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

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

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

person Len Holgate    schedule 15.10.2009
comment
Спасибо за предложения. Я понял твою идею. Это хорошо и практично. Я не думаю, что смогу пойти по пути закрытия прослушивающего сокета, но намерен серьезно подумать о втором подходе. Просто для сведения, хочу спросить, есть ли способ привести сокет в состояние, в котором он был до прослушивания? Например, если я связал сокет и в настоящее время слушаю его, как я могу отменить режим прослушивания сокета, не закрывая его. Ждем вашего ответа. - person immi; 15.10.2009
comment
Насколько я знаю, нет никакого способа сделать это. Впрочем, зачем заморачиваться? - person Len Holgate; 15.10.2009