我有这样的东西:
#define QUIT_TIME 5
int main(int argc, char **argv) {
//... SOCKETS STUFF ....
fdmax = parentfd;
while (notdone) {
//Set the timers
waitd.tv_sec = 1;
waitd.tv_usec = 0;
FD_ZERO(&tempreadfds);
FD_ZERO(&tempwritefds);
FD_ZERO(&readfds); /* initialize the read fd set */
FD_ZERO(&writefds); /* initialize the write fd set */
FD_SET(parentfd, &readfds); /* add listener socket fd */
FD_SET(0, &readfds); /* add stdin fd (0) */
tempreadfds = readfds; //make a copy
tempwritefds = writefds; //make a copy
if (select(fdmax+1, &tempreadfds, &tempwritefds, (fd_set*) 0, &waitd) < 0) {
error("ERROR in select");
}
for(i = 1; i <= fdmax; i++) {
if(FD_ISSET(i, &readfds)) {
if(i == parentfd) {
//This is a new connection
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
if (childfd < 0)
error("ERROR on accept");
InitializeDataStructures(childfd);
FD_SET(childfd, &readfds); //add to the master set
if(childfd > fdmax)
fdmax = childfd;
} else {
//Existing connection
if((nBytes = read(i, connections[i].buffer, MAXBUFFER)) <= 0) {
if(nBytes == 0) {
//Connection closed
printf("Socket %d hung up\n", read_write_loop);
} else {
error("\nReceive error\n");
}
FD_CLR(i, &readfds);
} else {
//We have some data from the connection
//... Manipulate the buffer
//Handle the message
}
}
}
if(FD_ISSET(i, &writefds)) {
.....
FD_CLR(i, &writefds);
}
//Timer checking
if(connections[i].active) {
gettimeofday(&TimeNow, NULL);
timeval_diff(&Interval, &TimeNow, &connections[i].TimeConnected);
printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
Interval.tv_sec,
Interval.tv_usec
);
if(Interval.tv_sec >= QUIT_TIME) {
printf("Timer elapsed!!\n");
}
}
}
}
/* clean up */
printf("Terminating server.\n");
close(parentfd);
return 0;
}
void InitializeDataStructures(int i) {
clients[i].active = YES;
clients[i].fd = i;
//Initialize other members of the structure
}
long long timeval_diff(struct timeval *difference, timeval *end_time, struct timeval *start_time) {
struct timeval temp_diff;
if(difference==NULL)
difference=&temp_diff;
difference->tv_sec =end_time->tv_sec -start_time->tv_sec ;
difference->tv_usec=end_time->tv_usec-start_time->tv_usec;
while(difference->tv_usec<0)
{
difference->tv_usec+=1000000;
difference->tv_sec -=1;
}
return 1000000LL*difference->tv_sec + difference->tv_usec;
}
我期望在执行过程中“Timer elapsed”行至少会打印一次(TimeConnected 被初始化为 if 条件之一),但由于某种原因,它永远不会打印出来。我认为我的 while 循环应该继续打印它...有人知道我是否在某个地方搞砸了吗?
编辑: 实际上,我正在使用计时器在超时后断开时间。我刚刚观察到,如果另一个客户端连接到服务器,它会打印“计时器已过”。我确实传递了最终参数来选择,但不确定为什么它没有任何效果。
感谢bdk!!如果您有兴趣了解我在这段代码中遇到的“愚蠢”错误,请详细阅读下面的讨论...这是一个明显的错误,我忽略了...这一切都是因为教程中的一句话:“选择修改你的原始描述符”。
变更列表:
工作代码:
#define QUIT_TIME 5
int main(int argc, char **argv) {
//... SOCKETS STUFF ....
fdmax = parentfd;
FD_ZERO(&readfds); /* initialize the read fd set */
FD_ZERO(&writefds); /* initialize the write fd set */
while (notdone) {
//Set the timers
waitd.tv_sec = 1;
waitd.tv_usec = 0;
FD_ZERO(&tempreadfds);
FD_ZERO(&tempwritefds);
FD_SET(parentfd, &readfds); /* add listener socket fd */
FD_SET(0, &readfds); /* add stdin fd (0) */
tempreadfds = readfds; //make a copy
tempwritefds = writefds; //make a copy
if (select(fdmax+1, &tempreadfds, &tempwritefds, (fd_set*) 0, &waitd) < 0) {
error("ERROR in select");
}
for(i = 1; i <= fdmax; i++) {
if(FD_ISSET(i, &tempreadfds)) {
if(i == parentfd) {
//This is a new connection
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
if (childfd < 0)
error("ERROR on accept");
InitializeDataStructures(childfd);
FD_SET(childfd, &readfds); //add to the master set
if(childfd > fdmax)
fdmax = childfd;
} else {
//Existing connection
if((nBytes = read(i, connections[i].buffer, MAXBUFFER)) <= 0) {
if(nBytes == 0) {
//Connection closed
printf("Socket %d hung up\n", read_write_loop);
} else {
error("\nReceive error\n");
}
FD_CLR(i, &readfds);
} else {
//We have some data from the connection
//... Manipulate the buffer
//Handle the message
}
}
}
if(FD_ISSET(i, &tempwritefds)) {
.....
FD_CLR(i, &writefds);
}
//Timer checking
if(connections[i].active) {
gettimeofday(&TimeNow, NULL);
timeval_diff(&Interval, &TimeNow, &connections[i].TimeConnected);
printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
Interval.tv_sec,
Interval.tv_usec
);
if(Interval.tv_sec >= QUIT_TIME) {
printf("Timer elapsed!!\n");
}
}
}
}
/* clean up */
printf("Terminating server.\n");
close(parentfd);
return 0;
}
void InitializeDataStructures(int i) {
clients[i].active = YES;
clients[i].fd = i;
//Initialize other members of the structure
}
long long timeval_diff(struct timeval *difference, timeval *end_time, struct timeval *start_time) {
struct timeval temp_diff;
if(difference==NULL)
difference=&temp_diff;
difference->tv_sec =end_time->tv_sec -start_time->tv_sec ;
difference->tv_usec=end_time->tv_usec-start_time->tv_usec;
while(difference->tv_usec<0)
{
difference->tv_usec+=1000000;
difference->tv_sec -=1;
}
return 1000000LL*difference->tv_sec + difference->tv_usec;
}
看看你选择的循环参数,它们对我来说看起来很可疑。主要是您在 tempreadfd 和 tempwritefd 上调用 select,但是当您调用 FD_ISSET 时,您将其传递给 readfd 和 writefd。在调用 select 之前,您使用 FD_SET 来设置您感兴趣的所有 fd。由于这些变量没有被发送到 select,因此未触发的 fd 不会被屏蔽。因此,您将在所有描述符上检测到“活动”。该接受描述符上确实没有任何活动,因此它会阻塞,直到有新客户端连接为止。
至少这是我的猜测。
如果我正确地阅读了您的代码,您将进入 while 循环,然后检查描述符是否在读取集中。如果是,则转到 if 语句的accept() 部分。如果不是,则输入 else 部分,立即阻止读取。如果套接字处于活动状态,则机器人没有可用数据,您将阻塞在那里,直到数据可用。它不会进入甚至检查计时器的部分,直到成功读取或输入错误。
如果 select 返回大于零的值,则应仅在检查套接字的位置输入代码,然后应在尝试读取套接字之前检查该套接字是否在读取集中。
通常,您会构建一个 fdset 来检查您正在接受连接的套接字,并构建另一个 fdset 来检查您已接受并实际读取数据的套接字。我想你可以按照你提出的方式来做,但我建议你从 read() 切换到 recv() 并使用 MSG_PEEK 标志。
这是一个非常特定于代码的问题,但谁知道......也许这对其他人会有一些帮助......我最终使用一个线程来解决问题(我知道......不是最好的,但我只是去疯狂地试图调试这个......)。感谢所有耐心帮助我的人...
在上面的代码中,我没有在 main() 的 while 循环内测试计时器,而是将其修改如下:
if(ThreadSpawned == 0) {
pthread_create(&thread, NULL, cleanup, (void *) &fdmax);
ThreadSpawned = 1;
}
然后清理函数如下:
void *cleanup(void *arg) {
//Timer checking
int i, *fdmax;
fdmax = (int *) arg;
while(1) {
for(i = 1; i <= *fdmax; i++) {
if(connections[i].active == 1) {
gettimeofday(&TimeNow, NULL);
timeval_diff(&Interval, &TimeNow, &clients[i].TimeConnected);
printf("*_*_*__*_*_*__*_*_*_*_* difference is %ld seconds, %ld microseconds\n",
Interval.tv_sec,
Interval.tv_usec
);
fflush(stdout);
if((int) Interval.tv_sec >= QUIT_TIME) {
printf("Timer elapsed!!\n");
fflush(stdout);
}
}
}
sleep(20);
}
}
任何更清洁的解决方案都是最受欢迎的。也许,bdk 在他的一个答案中提出的原因是程序在接受时阻塞的原因,但是如果它在那里阻塞,我将无法计算超时......所以我最终使用一个线程来做这个任务...