Из POSIX.1-2008/2013 документация shutdown():
int shutdown(int socket, int how);
...
Функция
shutdown()
должна вызывать все или часть полнодуплексного соединение сокета, связанное с сокетом дескриптора файла, к быть закрытым.Функция
shutdown()
принимает следующие аргументы:
socket
Указывает файловый дескриптор сокета.
how
Указывает тип отключения. Значения следующие:
SHUT_RD
Отключает дальнейшие операции приема.SHUT_WR
Отключает дальнейшие операции отправки.SHUT_RDWR
Отключает дальнейшие операции отправки и получения....
Страница для
shutdown(2)
говорит почти то же самое.
Вызов
shutdown()
вызывает все или часть полнодуплексного соединения на сокете, связанной с отключением sockfd. Еслиhow
SHUT_RD
, дальнейшие приемы будут запрещены. Еслиhow
-SHUT_WR
, дальнейшие передачи будут запрещены. Еслиhow
-SHUT_RDWR
, дальнейшие приемы и передачи будут запрещены.
Но я думаю, что могу получать данные даже после
shutdown(sockfd, SHUT_RD)
вызов. Вот тест, который я организовал и
результаты, которые я наблюдал.
------------------------------------------------------
Time netcat (nc) C (a.out) Result Observed
------------------------------------------------------
0 s listen - -
2 s connect() -
4 s send "aa" - -
6 s - recv() #1 recv() #1 receives "aa"
8 s - shutdown() -
10 s send "bb" - -
12 s - recv() #2 recv() #2 receives "bb"
14 s - recv() #3 recv() #3 returns 0
16 s - recv() #4 recv() #4 returns 0
18 s send "cc" - -
20 s - recv() #5 recv() #5 receives "cc"
22 s - recv() #6 recv() #6 returns 0
------------------------------------------------------
Ниже приведено краткое описание приведенной выше таблицы.
- Время: Истекшее время (в секундах) с начала теста.
- netcat (nc): Шаги, выполняемые через netcat (nc). Netcat был сделан, чтобы слушать на порт 8888 и принять TCP-соединения из моей программы на C, скомпилированной в . /a.out. Здесь Netcat играет роль сервера. Он отправляет три сообщения "aa", "bb" и "cc" в программу C после 4s, 10s и 18s, соответственно, истекли.
- C (a.out): Шаги, выполненные моей программой на языке C, скомпилированной в. /a.out. Это выполняет 6 вызовов recv() после 6s, 12s, 14s, 16s, 20s и 22s прошло.
- Результат: Результат, наблюдаемый на выходе программы C.
Он показывает, что он способен recv() сообщение "bb", которое было отправлено
после
shutdown()
успешно завершена. См. Строки для "12 с" и "20 с".
Вот программа C (клиентская программа).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int main()
{
struct addrinfo hints, *ai;
int sockfd;
int ret;
ssize_t bytes;
char buffer[1024];
/* Select TCP/IPv4 address only. */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((ret = getaddrinfo("localhost", "8888", &hints, &ai)) == -1) {
printf("getaddrinfo() error: %s\n", gai_strerror(ret));
return EXIT_FAILURE;
}
if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
printf("socket() error: %s\n", strerror(errno));
return EXIT_FAILURE;
}
/* Connect to localhost:8888. */
sleep(2);
if ((connect(sockfd, ai->ai_addr, ai->ai_addrlen)) == -1) {
printf("connect() error: %s\n", strerror(errno));
return EXIT_FAILURE;
}
freeaddrinfo(ai);
/* Test 1: Receive before shutdown. */
sleep(4);
bytes = recv(sockfd, buffer, 1024, 0);
printf("recv() #1 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer);
sleep(2);
if (shutdown(sockfd, SHUT_RD) == -1) {
printf("shutdown() error: %s\n", strerror(errno));
return EXIT_FAILURE;
}
printf("shutdown() complete\n");
/* Test 2: Receive after shutdown. */
sleep (4);
bytes = recv(sockfd, buffer, 1024, 0);
printf("recv() #2 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer);
/* Test 3. */
sleep (2);
bytes = recv(sockfd, buffer, 1024, 0);
printf("recv() #3 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer);
/* Test 4. */
sleep (2);
bytes = recv(sockfd, buffer, 1024, 0);
printf("recv() #4 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer);
/* Test 5. */
sleep (4);
bytes = recv(sockfd, buffer, 1024, 0);
printf("recv() #5 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer);
/* Test 6. */
sleep (2);
bytes = recv(sockfd, buffer, 1024, 0);
printf("recv() #6 returned %d bytes: %.*s\n", (int) bytes, (int) bytes, buffer);
}
Вышеприведенный код был сохранен в файле с именем foo.c
.
Вот небольшая оболочка script, которая компилирует и запускает вышеуказанную программу и
вызывает netcat (nc
) для прослушивания на порту 8888 и отвечает клиенту
с сообщениями aa
, bb
и cc
с определенными интервалами в соответствии с таблицей
показано выше. Следующая оболочка script сохраняется в файле с именем
run.sh
.
set -ex
gcc -std=c99 -pedantic -Wall -Wextra -D_POSIX_C_SOURCE=200112L foo.c
./a.out &
(sleep 4; printf aa; sleep 6; printf bb; sleep 8; printf cc) | nc -vvlp 8888
При запуске вышеуказанной оболочки script наблюдается следующий вывод.
$ sh run.sh
+ gcc -std=c99 -pedantic -Wall -Wextra -D_POSIX_C_SOURCE=200112L foo.c
+ nc -vvlp 8888
+ sleep 4
listening on [any] 8888 ...
+ ./a.out
connect to [127.0.0.1] from localhost [127.0.0.1] 54208
+ printf aa
+ sleep 6
recv() #1 returned 2 bytes: aa
shutdown() complete
+ printf bb
+ sleep 8
recv() #2 returned 2 bytes: bb
recv() #3 returned 0 bytes:
recv() #4 returned 0 bytes:
+ printf cc
recv() #5 returned 2 bytes: cc
recv() #6 returned 0 bytes:
sent 6, rcvd 0
Выход показывает, что программа C может принимать сообщения с
recv()
даже после того, как он вызвал shutdown()
. Единственное поведение, которое
вызов shutdown()
, похоже, повлиял на то, является ли recv()
вызов немедленно возвращается или блокируется в ожидании следующего сообщения.
Обычно, перед shutdown()
, вызов recv()
будет ждать
сообщение для прибытия. Но после вызова shutdown()
recv()
возвращает 0
немедленно, когда нет нового сообщения.
Я ожидал, что все вызовы recv()
после shutdown()
не сработают (например,
return -1
) из-за документации, приведенной выше.
Два вопроса:
- Является ли поведение, наблюдаемое в моем эксперименте, т.е.
recv()
вызовом возможность получать новые сообщения, отправленные послеshutdown()
, в соответствии с стандарте POSIX и странице руководства дляshutdown(2)
, что у меня есть цитируется выше? - Почему это происходит после вызова
shutdown()
,recv()
возвращает0
немедленно, а не ждет появления нового сообщения?