Как я могу отказаться от соединения сокета в 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() и либо примете, либо отклоните.

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

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