[英]Why is the select call not blocking on a unix domain socket?
I googled a lot and didn't get an answer, hence posting it here. 我在Google上搜索了很多,但没有得到答案,因此将其发布在这里。
In the following C program(the server code) I want a Unix domain socket server that's listening at /tmp/unix-test-socket
. 在下面的C程序(服务器代码)中,我想要一个监听
/tmp/unix-test-socket
的Unix域套接字服务器。 My problem is that the client code is sucessfully able to connect to the server. 我的问题是客户端代码成功能够连接到服务器。 However, once its connected and I have "accepted" the connection, the
select
call does NOT block. 但是,一旦连接成功并且我“接受”了连接,
select
调用就不会阻塞。
So let me explain. 所以让我解释一下。
Initially the unix_domain_socket = 3 最初,unix_domain_socket = 3
As soon as I get the first request, accept the connection and, store it in unix_domain_socket_connections[max_unix_domain_socket_connections]. 一旦收到第一个请求,就接受连接并将其存储在unix_domain_socket_connections [max_unix_domain_socket_connections]中。 The value of the socket
fd
is 4. 套接字
fd
值为4。
When I run the server code goes into a loop because the select call believes that always there is data in socket 4. 当我运行服务器代码时,由于select调用认为套接字4中始终有数据,因此该代码进入了循环。
./unix-client "/tmp/unix-test-socket" SEND_DATA
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
SERVER CODE: 服务器代码:
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);
}
}
}
CLIENT CODE: 客户代码:
/* 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;
}
You will find that select()
will return "ready for reading" if the far end has closed... The rule for "ready for reading" is that it is true iff a read()
would not block. 您将发现,如果远端已关闭,则
select()
将返回“ ready forread”。“ ready forread”的规则是,如果read()
不会阻塞,则它为true。 read()
does not block if it returns 0
. read()
返回0
则不会阻塞。
According to the select linux man pages there is a bug related to this behaviour: 根据选择的 Linux手册页,存在与该行为有关的错误:
Under Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks.
在Linux下,select()可能会将套接字文件描述符报告为“准备读取”,但是随后的读取会阻塞。 This could for example happen when data has arrived but upon examination has wrong checksum and is discarded.
例如,这可能在数据到达但检查时校验和错误并被丢弃时发生。 There may be other circumstances in which a file descriptor is spuriously reported as ready.
在其他情况下,文件描述符可能会虚假地报告为就绪。
On the other hand, I recommend you to consider the strategy for handle activity and process data into the loop(irrelevant parts removed): 另一方面,我建议您考虑处理活动和将数据处理到循环中的策略(删除了不相关的部分):
for (loop = 0; loop<4; loop++)
{
// ...
activity = select( maxfds + 1 , &readfds , NULL , NULL , NULL);
// ...
}
This will block for the first sockect while the 2nd, 3th, and fourth might be ready. 当第2个,第3个和第四个准备就绪时,这将阻止第一个sockect。 At least use a timeout and check
errno
for handle timeout event. 至少使用一个超时并检查
errno
来处理超时事件。 See select man pages for more info. 有关更多信息,请参见选择手册页。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.