【发布时间】:2019-11-22 22:16:06
【问题描述】:
我是套接字编程的新手。我正在尝试制作一个可以处理多个连接的 TCP 侦听器。
我找到了this 示例,这似乎非常有用。
此代码的问题在于它仅在收到数据时才向连接的客户端发送数据。我想异步向连接的客户端发送数据。
我看到select() 函数永远阻塞代码,直到事件到达套接字。
我想在select() 函数中设置一个延迟(而不是NULL),这样它每隔几微秒就会超时,如果有数据,程序就可以发送数据。在线if (buffer[0] > 0)
问题是:有没有更好的方法来做我想做的事?我可以通过其他方式强制select() 超时吗?
char buffer[1025]; 是一个从另一个线程填充的全局数组。下面的while(TRUE) 正在另一个线程上运行。
while(TRUE)
{
//clear the socket set
FD_ZERO(&readfds);
//add master socket to set
FD_SET(master_socket, &readfds);
max_sd = master_socket;
//add child sockets to set
for ( i = 0 ; i < max_clients ; i++)
{
//socket descriptor
sd = client_socket[i];
//if valid socket descriptor then add to read list
if(sd > 0)
FD_SET( sd , &readfds);
//highest file descriptor number, need it for the select function
if(sd > max_sd)
max_sd = sd;
}
//wait for an activity on one of the sockets , timeout is NULL ,
//so wait indefinitely
struct timeval timeout;
timeout.tv_usec = 10000;
activity = select( max_sd + 1 , &readfds , NULL , NULL , &timeout);
if ((activity < 0) && (errno!=EINTR))
{
cout << "select error" << endl;
}
//If something happened on the master socket ,
//then its an incoming connection
if (FD_ISSET(master_socket, &readfds))
{
if ((new_socket = accept(master_socket,
(struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
//inform user of socket number - used in send and receive commands
printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//send new connection greeting message
if( send(new_socket, message, strlen(message), 0) != strlen(message) )
{
perror("send");
}
puts("Welcome message sent successfully");
//add new socket to array of sockets
for (i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("Adding to list of sockets as %d\n" , i);
break;
}
}
}
if (buffer[0] > 0)
{
sd = client_socket[0];
send(sd , buffer , strlen(buffer) , 0 );
}
//else its some IO operation on some other socket
for (i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and also read the
//incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&address , \
(socklen_t*)&addrlen);
printf("Host disconnected , ip %s , port %d \n" ,
inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
//Close the socket and mark as 0 in list for reuse
close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else
{
//set the string terminating NULL byte on the end
//of the data read
buffer[valread] = '\0';
send(sd , buffer , strlen(buffer) , 0 );
}
}
}
}
【问题讨论】:
-
小心
timeout的初始化,你只初始化tv_usec字段,而不是tv_sec字段,它会有一个indeterminate(并且看似随机/垃圾)价值。 -
许多需要处理其他事件的单线程服务器的工作方式与此非常相似。曾经有一种古老的游戏类型叫做MUD,它的服务器大多是单线程的,并且有许多看似异步发生的事件,他们使用超时来实现
select,就像你一样。今天可能会使用线程进行并行和异步操作。 -
@Someprogrammerdude 你的意思是为每个客户端使用一个线程?
-
这是一种解决方案,或者一个线程用于所有通信(使用
select和空超时),另一个用于其他事件。 -
我不确定我是否完全理解。我将我在 geeksforgeeks 上找到的示例中的所有
main()函数放入一个线程中。我在上面发布的代码是我在该示例中所做的更改。我怎么能打破另一个线程的选择?我只想能够在我必须发送一些数据时向客户端发送数据。但是如果没有从套接字接收到数据,我不能导致select永远阻塞(设置了NULL超时)