非阻塞之多路复用
fcntl()
NAME
fcntl - manipulate file descriptor
SYNOPSIS
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl可以修改文件状态标志:
File status flags
Each open file description has certain associated status flags, initialized by open(2) and possibly modified by fcntl().
The file status flags and their semantics are described in open(2).
F_GETFL (void)
Get the file access mode and the file status flags; arg is ignored.
F_SETFL (int)
Set the file status flags to the value specified by arg. On Linux this command can change only the O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
实例:
用fcntl给socket设置 O_NONBLOCK,所以在accept时资源不可用时,就以及返回。
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <fcntl.h>
#define SERVPORT 3333
#define BACKLOG 10
#define MAXDATASIZE 100
int main()
{
struct sockaddr_in server_sockaddr,client_sockaddr;
int sin_size = 0,recvbytes = 0,flags = 0;
int sockfd,client_fd;
char buf[MAXDATASIZE];
if((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1){
perror("socket");
exit(1);
}
printf("socket success!,sockfd=%d
",sockfd);
server_sockaddr.sin_family=AF_INET;
server_sockaddr.sin_port=htons(SERVPORT);
server_sockaddr.sin_addr.s_addr=INADDR_ANY;
bzero(&(server_sockaddr.sin_zero),8);
if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))== -1){
perror("bind");
exit(1);
}
printf("bind success!
");
if(listen(sockfd,BACKLOG)== -1){
perror("listen");
exit(1);
}
printf("listening....
");
/* 调用 fcntl 函数设置非阻塞参数 */
if((flags=fcntl( sockfd, F_GETFL, 0)) < 0)
perror("fcntl F_SETFL");
flags |= O_NONBLOCK;
if(fcntl(sockfd,F_SETFL,flags)<0)
perror("fcntl");
while(1){
sin_size = sizeof(struct sockaddr_in);
if((client_fd=accept(sockfd,(struct sockaddr*)&client_sockaddr,&sin_size))== -1){
perror("accept");
exit(1);
}
if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))== -1){
perror("recv");
exit(1);
}
if(read(client_fd,buf,MAXDATASIZE)<0){
perror("read");
exit(1);
}
printf("received a connection :%s",buf);
close(client_fd);
exit(1);
}/*while*/
}
结果如下:
xxx@xxx-pc:~/Documents$ ./a.out
socket success!,sockfd=3
bind success!
listening....
accept: Resource temporarily unavailable
select()
SYNOPSIS
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
Four macros are provided to manipulate the sets. FD_ZERO() clears a set. FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set. FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.
readfds: 读文件描述符的集合,select检测该集合读变化,若该集合有文件可读,select返回大于0的数。如果没有可读,则等待timeout返回0,若发生错误返回负数。
writefds:写文件描述符的集合。
exceptfds:要检测是否异常的文件描述符集合。
timeout的值大于0,若文件描述符的集合没有变化,则等待timeout时间返回。
特例:timeout的结构体时间值都为0,则立马timeout,立即就返回了。
如果timeout的参数设为0(NULL),则会一直阻塞直到文件描述符出现了变化。
实例:
server端:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#define SERVPORT 3333
#define BACKLOG 10
#define MAXDATASIZE 100
int main()
{
struct sockaddr_in server_sockaddr,client_sockaddr;
int sin_size,recvbytes;
fd_set readfd;
fd_set writefd;
int sockfd,client_fd;
char buf[MAXDATASIZE] = {0};
struct timeval time = {0,0};
if((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1){
perror("socket");
exit(1);
}
printf("socket success!,sockfd=%d
",sockfd);
server_sockaddr.sin_family=AF_INET;
server_sockaddr.sin_port=htons(SERVPORT);
server_sockaddr.sin_addr.s_addr=INADDR_ANY;
bzero(&(server_sockaddr.sin_zero),8);
if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))== -1){
perror("bind");
exit(1);
}
printf("bind success!
");
if(listen(sockfd,BACKLOG)== -1){
perror("listen");
exit(1);
}
printf("listening....
");
/* 将调用 socket 函数的描述符作为文件描述符 */
FD_ZERO(&readfd);
FD_SET(sockfd,&readfd);
sin_size=sizeof(struct sockaddr_in);
while(1){
/* 调用 select 函数 */
if(select(sockfd+1,&readfd,NULL,NULL,0) > 0){
if(FD_ISSET(sockfd,&readfd) > 0){
if((client_fd = accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))== -1){
perror("accept");
exit(1);
}
if((recvbytes = recv(client_fd,buf,MAXDATASIZE,0))== -1){
perror("recv");
exit(1);
}
printf("received a connection - %s
", buf);
}/*if*/
close(client_fd);
}/*select*/
}/*while*/
}
结果如下:
xxx@xxx-pc:~/Documents$ ./server
socket success!,sockfd=3
bind success!
listening....
received a connection - hello
received a connection - hello
received a connection - hello