Как я могу отказаться от соединения сокета в C?
Если я хочу принять соединение, я вызываю accept
, но как я могу отказаться от соединения?
В рабочем эхо-клиенте для сокета у меня есть оператор if. На эхо-сервере, как я могу заставить эхо-клиент достичь этой инструкции printf
?
...
if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
printf("Connecting failed\n");
return 1;
}
...
Ответы
Ответ 1
Насколько мне известно, это не так, как работает TCP. Вызов accept(..)
всегда будет возвращаться с данными клиента. Существует невозможно заглянуть в соединение и выборочно отказаться.
То, как вы делаете это сейчас, на самом деле является правильным: принять и затем закрыть. Если у вас есть другая структура сообщений над этим слоем, вы можете создать пользовательское сообщение "Отклонить". Эта опция полностью зависит от вашего варианта использования.
Если вы ищете отклонение на основе IP-адреса, оно не входит в ваш домен приложений. Это работа вашего брандмауэра (как говорит @Bart Friederichs). Таким образом, запрос даже не коснется стека TCP.
На самом деле я хочу строго одно соединение только на этом конкретном порту. Любое другое соединение в идеале потерпит неудачу очень очевидным образом.
Не позволяйте принимать вызов в потоке управления. Только когда вы ждете accept
, ваша программа будет ждать соединения сокета, никогда иначе.
Ответ 2
Чтобы получить нужное поведение (принимайте только одно соединение за раз, другие попытки клиентов должны получить сбой), есть два варианта.
-
Вы можете закрыть ваш слуховой сокет после того, как вы приняли соединение. Повторно создайте ваш слуховой сокет после закрытия закрытого соединения.
-
Вы можете закрыть недавно установленные соединения, если соединение уже выполняется. Если вы хотите, чтобы клиент увидел TCP reset, большинство стеков TCP вызывают одно, если вы включите опцию linger с таймаутом 0.
struct linger lo = { 1, 0 };
setsockopt(s, SOL_SOCKET, SO_LINGER, &lo, sizeof(lo));
close(s);
Ответ 3
В стандартных API сокетов на большинстве платформ нет возможности отклонить соединение. Вы должны accept()
соединение, а затем немедленно закрыть его, если оно вам не нужно.
Исключением из этого правила является специфичная для WSAAccept()
функция WSAAccept()
. Он обеспечивает функцию обратного вызова, которая позволяет приложению самостоятельно решать, следует ли принимать, отклонять или сохранять соединение в очереди невыполненных работ.
Ответ 4
Может быть полезно
- закрыть сокет сервера прослушивания после успешного
accept()
подключения клиента и
- чтобы восстановить его после того, как клиентское соединение по какой-либо причине исчезло.
Ответ 5
Немного опоздал на вечеринку, но вы можете использовать WSAAccept() вместо accept(). WSAAccept() имеет дополнительную функцию обратного вызова, которая позволяет вам взглянуть на того, кто стучит в дверь, и решить, хотите ли вы ответить. Ваша функция обратного вызова может возвращать константы CF_ACCEPT, CF_REJECT и CF_DEFER. Первые два очевидны. Третий позволяет отложить ответ на случай, если вам нужно принять решение позже. Как только вы решили наверняка, вы снова вызовите WSAAccept() и либо примете, либо отклоните.
К сожалению, вы должны либо принять, либо отклонить, чтобы удалить запись с начала очереди прослушивания. Причина, по которой я говорю это неудачно, заключается в том, что подключаемая машина имеет очевидную разницу между отказом и отказом от ответа (тайм-аут).
Если хакер использует сканер портов, он узнает, что вы прослушиваете порт, если отклоните его. В этот момент они могут начать взламывать свой путь. Было бы неплохо иметь возможность удалить запись из передней части очереди, ничего не делая с ней так, чтобы тот, кто на другом конце не знал, что вы слушаете этот порт.