Сокет сервера Linux - неправильный дескриптор файла
У меня проблема с сокетом сервера под Linux. По неизвестной мне причине пропадает сокет сервера и я получаю Bad file descriptor
ошибка в вызове выбора, ожидающего входящего соединения. Эта проблема всегда возникает, когда я закрываю несвязанное соединение сокета в другом потоке. Это происходит на встроенном Linux с ядром 2.6.36.
Кто-нибудь знает, почему это произойдет? Это нормально, что сокет сервера может просто исчезнуть, что приводит к Bad file descriptor
?
редактировать: другой код сокета реализует VNC-сервер и работает в совершенно другом потоке. Единственное, что особенного в этом другом коде - это использование setjmp/longjmp
но это не должно быть проблемой.
Код, который создает сокет сервера, следующий:
int server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(1234);
const int optionval = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optionval, sizeof(optionval));
if (bind(server_socket, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
perror("bind");
return 0;
}
if (listen(server_socket, 1) < 0) {
perror("listen");
return 0;
}
Я жду входящего соединения, используя код ниже:
static int WaitForConnection(int server_socket, struct timeval *timeout)
{
fd_set read_fds;
FD_ZERO(&read_fds);
int max_sd = server_socket;
FD_SET(server_socket, &read_fds);
// This select will result in 'EBADFD' in the error case.
// Even though the server socket was not closed with 'close'.
int res = select(max_sd + 1, &read_fds, NULL, NULL, timeout);
if (res > 0) {
struct sockaddr_in caddr;
socklen_t clen = sizeof(caddr);
return accept(server_socket, (struct sockaddr *) &caddr, &clen);
}
return -1;
}
редактировать: когда возникает проблема, я просто перезагружаю сервер, но я не понимаю, почему идентификатор сокета сервера должен внезапно стать неверным дескриптором файла:
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (server_socket, SOL_SOCKET, SO_ERROR, &error, &len );
if (retval < 0) {
close(server_socket);
goto server_start;
}
4 ответа
Сокеты (файловые дескрипторы) обычно страдают от тех же проблем управления, что и необработанные указатели в C
, Всякий раз, когда вы закрываете сокет, не забудьте назначить -1
к переменной, которая содержит значение дескриптора:
close(socket);
socket = -1;
Как бы вы сделали, чтобы C
указатель
free(buffer);
buffer = NULL;
Если вы забудете это сделать, вы сможете позже дважды закрыть сокет, как free()
памяти в два раза, если это был указатель.
Другая проблема может быть связана с тем, что люди обычно забывают: файловые дескрипторы в среде UNIX начинаются с0
, Если где-то в коде у вас есть
struct FooData {
int foo;
int socket;
...
}
// Either
FooData my_data_1 = {0};
// Or
FooData my_data_2;
memset(&my_data_2, 0, sizeof(my_data_2));
В обоих случаях my_data_1
а также my_data_2
иметь действительный дескриптор (socket
) значение. А потом какой-то кусок кода, отвечающий за освобождение FooData
структура может слепо close()
этот дескриптор, который оказывается слушающим сокетом вашего сервера (0
).
1- закройте ваше гнездо:
close(sockfd);
2 - очистить дескриптор файла сокета от выбранного набора:
FD_CLR(sockfd,&master); //opposite of FD_SET
Вы не различаете два случая ошибок в вашем коде, оба могут потерпеть неудачу select
или же accept
, Я думаю, что у вас просто есть тайм-аут, и этот выбор возвращает 0
,
- Распечатать
retval
а такжеerrno
вelse
ветка - исследовать возвращаемое значение
accept
отдельно - убедиться, что
errno
сбрасывается на0
перед каждым из системных вызовов
В Linux, когда вы создаете соединение и оно закрывается, вам придется подождать некоторое время, прежде чем устанавливать новое соединение. Как и в Linux, сокет не освобождает порт no. как только вы закроете розетку.
ИЛИ ЖЕ
Вы повторно используете сокет, тогда плохой дескриптор файла хочет прийти.