select()和pselect()允许程序监控多个文件描述符,等待一个或多个文件描述符变为I/O操作的“就绪”状态。一个“就绪”状态的文件描述符被认为可以接受内核提供的IO操作,比如不被阻塞地“read()”。
有趣的是,Unix的标准输入/输出也是一类文件描述符,可以和socket描述符同时被监听。所以,可以用select监听socket和stdin,保证实时接收网络数据的同时又不阻塞住键盘输入。
这相当于学习笔记(一)中单线程半双工实现的实时性扩展,(一)中的程序需要手动从接收缓冲区刷新接收到的数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define BUFSIZE 1024
int main()
{
char buf[BUFSIZE];
int sockfd;
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) <0)
{
perror("Create socket failed");
exit(-1);
}
struct sockaddr_in addr;
memset(&addr,0,sizeof(struct sockaddr_in));
char serverIP[15];
int serverPort;
printf("Server IP:");
scanf("%s",serverIP);
printf("Server port:");
scanf("%d",&serverPort);
addr.sin_family = AF_INET;
addr.sin_port = htons(serverPort);
addr.sin_addr.s_addr = inet_addr(serverIP);
if(connect(sockfd,(struct sockaddr *)(&addr),sizeof(struct sockaddr)) < 0)
{
perror("Connection error!");
exit(-1);
}
else
{
printf("Connected:%s:%d\n",serverIP,serverPort);
}
//fcntl(sockfd,F_SETFL,O_NONBLOCK);//设置socket非阻塞模式
fd_set rset;
int n;
FD_ZERO(&rset);
for(;;)
{
FD_SET(STDIN_FILENO,&rset);
FD_SET(sockfd,&rset);
select(sockfd +1,&rset,NULL,NULL,NULL);
if(FD_ISSET(sockfd,&rset))
{
memset(buf,0,sizeof(buf));
n = recv(sockfd,buf,BUFSIZE,0);
if(n == 0)
{
printf("Sever terminated prematurely\n");
exit(0);
}
else
{
printf("[recv]%s\n",buf);
}
}
if(FD_ISSET(STDIN_FILENO,&rset))
{
n = read(STDIN_FILENO,buf,BUFSIZE);
if( n <=1)
continue;
else
send(sockfd,buf,n,0);
}
}
return 0;
}