我的客户端程序首先连接到服务器,然后服务器发送文件,然后客户端输入命令并将其发送到服务器并接收result.i希望通过非阻塞io实现此输入命令和非阻塞套接字。 但是我的程序无法从stdin读取输入。当我在read_set中设置stdin时它可以工作,但在这种情况下它不起作用。 这是我的客户代码:
main (int argc, char *argv[])
{
int len, rc,max_sd;
int sockfd;
int on=1;
char recv_buff[100]="";
char command[100]="";
char result[100]="";
char instr_buff[100]="";
struct sockaddr_in addr;
struct timeval timeout;
fd_set read_set,write_set;
/*************************************************/
/* Create an AF_INET stream socket */
/*************************************************/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket() failed");
exit(-1);
}
rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,(char *)&on, sizeof(on)); // Allow socket descriptor to be reuseable
if(rc<0)
{
perror("setsockopt() failed");
close(sockfd);
exit(-1);
}
//Set socket to be non-blocking.
rc = ioctl(sockfd, FIONBIO, (char *)&on);
if(rc<0)
{
perror("ioctl() faild");
close(sockfd);
exit(-1);
}
//making stdin to be non-blocking
rc = ioctl(0, FIONBIO, (char *)&on);
if(rc<0)
{
perror("ioctl() faild");
close(sockfd);
exit(-1);
}
/*************************************************/
/* Initialize the socket address structure */
/*************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
/*************************************************/
/* Connect to the server */
/*************************************************/
int count=0;
do
{
rc = connect(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in));
count++;
}while(rc<0&&count<20);
if(rc<0)
{
printf("can not connect to server\n");
exit(-1);
}
else
printf("Connect completed.\n");
timeout.tv_sec = 60;
timeout.tv_usec = 0;
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(sockfd,&read_set);
FD_SET(0,&read_set);
while(1)
{
rc=select(sockfd+1,&read_set,&write_set,NULL,&timeout);
if(rc<0)
{
perror("select failed()\n");
break;
}
if(rc==0)
{
printf("select timed out.End Program\n");
break;
}
if(FD_ISSET(sockfd,&write_set))
{
//sending data
rc=send(sockfd,command,strlen(command)+1,0);
if(rc<0)
{
perror("sending command failed");
break;
}
if(rc==0)
{
printf("connection closed by server\n");
break;
}
printf("command send\n");
FD_CLR(sockfd,&write_set);
}
if(FD_ISSET(sockfd,&read_set))
{
//receiving data
rc=recv(sockfd,recv_buff,sizeof(recv_buff),0);
if (rc < 0)
{
if(errno == EAGAIN||errno == EWOULDBLOCK)
{
printf("no message\n");
continue;
}
perror(" recv() failed");
break;
}
if(rc==0)
{
printf("connection closed by server\n");
break;
}
//data received
printf("data received\n");
//check to see if is instr.conf or result
if(recv_buff[strlen(recv_buff)-1]=='@') //is file
{
//save to instr_buff
strcpy(instr_buff,recv_buff);
}
else
{
//result received
printf("Result:\n");
printf("%s",recv_buff);
}
printf("Enter Command:\n");
}
if(FD_ISSET(0,&read_set))
{
scanf("%s",command);
FD_SET(sockfd,&write_set);
}
}//end of while
close(sockfd);
}
答案 0 :(得分:1)
请记住select
更新您传入的读取,写入和异常fd_set
项目。这意味着如果您选择两个可能的输入,其中一个是就绪,读取集只有一位设置:
/* read_set has two bits set here ... */
rc1 = select(maxfd, &read_set, ...);
/* but now read_set has only one bit set here */
rc2 = select(maxfd, &read_set, ...);
第二个select
调用会检查多少个文件描述符?只有一个“准备读取”,第一个调用清除了另一个描述符的位。
请注意,您的循环永远不会重新初始化read_set
。因此,即使您没有直接连续进行两次select
调用,实际上调用select
时会在第二次和之后的循环中清除一些位。
(使用select
时的一个典型习惯是拥有用于输入的“主集”,并复制它们以便在调用中进行修改:
copy_reads = master_reads;
copy_writes = master_writes;
rc = select(maxfd, ©_reads, ©_writes, ...);
然后根据需要在&amp; copy_reads和&amp; copy_writes上使用FD_ISSET
。或者,您可以使用FD_ZERO
并通过循环在每次旅行中重建整个集合。完全绕过此问题的另一种方法是使用poll
,甚至epoll
或kqueues或类似问题。)