Почему вызов select не блокируется на сокете домена unix?

Я много гуглил и не получил ответа, поэтому разместил его здесь.

В следующей программе на C (код сервера) я хочу сервер сокетов домена Unix, который прослушивает /tmp/unix-test-socket, Моя проблема в том, что клиентский код успешно может подключиться к серверу. Однако, как только он подключен, и я «принял» соединение, select звонок НЕ блокируется.

Итак, позвольте мне объяснить.

Изначально unix_domain_socket = 3

Как только я получу первый запрос, примите соединение и сохраните его в unix_domain_socket_connections [max_unix_domain_socket_connections]. Значение сокета fd это 4.

Когда я запускаю серверный код, он зацикливается, потому что вызов select считает, что в сокете 4 всегда есть данные.

Я запускаю сторону КЛИЕНТА как:

./unix-client "/tmp/unix-test-socket" SEND_DATA

Вывод со стороны SERVER:

Client sent us a message!

Successfully accepted the new ION connection with fd 4!

[program_select_to_look_at_right_sockets]: Storing fd 4

Data Arrived on UNIX domain socket 4

length 10 SEND_DATA  <-- I get the data sent by the client

[program_select_to_look_at_right_sockets]: Storing fd 4 *<-- Why isnt select blocking and why does it think there is still data on socket 4*

Data Arrived on UNIX domain socket 4

[program_select_to_look_at_right_sockets]: Storing fd 4

Data Arrived on UNIX domain socket 4

КОД СЕРВЕРА:

int unix_domain_socket = 0;

int max_unix_domain_socket_connections;
int unix_domain_socket_connections[2];

char *unix_domain_socket_name = "/tmp/unix-test-socket";

int open_unix_domain_server()
{
int socket_fd, result;
struct sockaddr_un name;
int client_sent_quit_message;
socklen_t socket_length;

max_unix_domain_socket_connections = 0;
memset((void *) &name, 0, sizeof(name));

socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
name.sun_family = AF_UNIX;
strcpy(name.sun_path, unix_domain_socket_name);
socket_length = strlen(name.sun_path) + sizeof(name.sun_family);

/* Remove this socket if it already exists */
unlink(name.sun_path);

result = bind(socket_fd, (struct sockaddr *) &name, socket_length);

if (result < 0)
goto Error;

result = listen(socket_fd, MAX_UNIX_DOMAIN_SOCKETS);

return socket_fd;

Error:

printf("[%s] Error in either listen or bind!\n", __FUNCTION__);

return -1;

}

int accept_new_unix_domain_connection()
{
int client_fd;
struct sockaddr_un new_connection;
socklen_t new_conn_length = sizeof(new_connection);

memset((void *) &new_connection, 0, sizeof(new_connection));

client_fd = accept(unix_domain_socket, (struct sockaddr *) &new_connection,
&new_conn_length);

if (client_fd < 0)
{
printf("The following error occurred accept failed %d %d\n", errno,
unix_domain_socket);
}

unix_domain_socket_connections[max_unix_domain_socket_connections] =
client_fd;

max_unix_domain_socket_connections++;

return client_fd;
}

int check_if_new_client_is_unix_domain(fd_set readfds)
{
int unix_fd = 0;

for (unix_fd = 0; unix_fd < 2; unix_fd++)
{
if (FD_ISSET(unix_domain_socket_connections[unix_fd], &readfds))
{
printf("Data Arrived on UNIX domain socket %d\n",
unix_domain_socket_connections[unix_fd]);
return 1;
}
}

return 0;
}

int process_data_on_unix_domain_socket(int unix_socket)
{
int length = 0;
char* data_from_gridFtp;

/* First, read the length of the text message from the socket. If
read returns zero, the client closed the connection. */

if (read(unix_socket, &length, sizeof(length)) == 0)
return 0;

/* Allocate a buffer to hold the text. */
data_from_gridFtp = (char*) malloc(length + 1);

/* Read the text itself, and print it. */
recv(unix_socket, data_from_gridFtp, length, 0);

printf("length %d %s\n", length, data_from_gridFtp);

return length;
}

void program_select_to_look_at_right_sockets(fd_set *readfds, int *maxfds)
{
int unix_fd = 0;

FD_ZERO(readfds);

FD_SET(unix_domain_socket, readfds);

for (unix_fd = 0; unix_fd < 2; unix_fd++)
{
if (unix_domain_socket_connections[unix_fd])
{
printf("[%s]: Storing fd %d\n", __FUNCTION__,
unix_domain_socket_connections[unix_fd]);

FD_SET(unix_domain_socket_connections[unix_fd], readfds);

if (*maxfds < unix_domain_socket_connections[unix_fd])
*maxfds = unix_domain_socket_connections[unix_fd];
}

}
}

int main(int argc, char**argv)
{
int result, maxfds, clientfd, loop;
fd_set readfds;
int activity;
socklen_t client_len;
struct sockaddr_in client_address;

FD_ZERO(&readfds);

unix_domain_socket = open_unix_domain_server();

if (unix_domain_socket < 0)
return -1;

maxfds = unix_domain_socket;

FD_SET(unix_domain_socket, &readfds);

for (loop = 0; loop < 4; loop++)
{
program_select_to_look_at_right_sockets(&readfds, &maxfds);

activity = select(maxfds + 1, &readfds, NULL, NULL, NULL);

if (FD_ISSET(unix_domain_socket, &readfds))
{
printf("client sent us a message!\n");
clientfd = accept_new_unix_domain_connection();

if (clientfd < 0)
break;
}
else if (check_if_new_client_is_unix_domain(readfds))
{
process_data_on_unix_domain_socket(clientfd);
}
}
}

КОД КЛИЕНТА:

/* Write TEXT to the socket given by file descriptor SOCKET_FD. */
void write_text(int socket_fd, const char* text)
{
/* Write the number of bytes in the string, including
NUL-termination. */
int length = strlen(text) + 1;
send(socket_fd, &length, sizeof(length), 0);
/* Write the string. */
send(socket_fd, text, length, 0);
}

int main(int argc, char* const argv[])
{
const char* const socket_name = argv[1];
const char* const message = argv[2];
int socket_fd;
struct sockaddr_un name;
/* Create the socket. */
socket_fd = socket(PF_LOCAL, SOCK_STREAM, 0);
/* Store the server’s name in the socket address. */
name.sun_family = AF_UNIX;
strcpy(name.sun_path, socket_name);
/* Connect the socket. */
connect(socket_fd, (struct sockaddr *) &name, SUN_LEN(&name));
/* Write the text on the command line to the socket. */
write_text(socket_fd, message);
close(socket_fd);
return 0;
}

0

Решение

Вы найдете это select() вернет «готово к чтению», если дальний конец закрыт … Правило «готово к чтению» таково, что оно истинно, если read() не будет блокировать. read() не блокируется, если возвращается 0,

3

Другие решения

Согласно Выбрать На страницах man linux есть ошибка, связанная с этим поведением:

В Linux select () может сообщить дескриптор файла сокета как «готовый к чтению», но, тем не менее, последующие блоки чтения. Это может, например, произойти, когда данные поступили, но при проверке они имеют неверную контрольную сумму и отбрасываются. Могут быть другие обстоятельства, при которых файловый дескриптор ложно сообщается как готовый.

С другой стороны, я рекомендую вам рассмотреть стратегию обработки действий и обработки данных в цикле (ненужные части удалены):

for (loop = 0; loop<4; loop++)
{
// ...
activity = select( maxfds + 1 , &readfds , NULL , NULL , NULL);
// ...
}

Это заблокирует первый сокет, в то время как второй, третий и четвертый могут быть готовы. По крайней мере, используйте тайм-аут и проверьте errno для события тайм-аута обработки. Для получения дополнительной информации см. Справочные страницы.

0

По вопросам рекламы [email protected]