基本思想
先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。
函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。
#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);
功能:
实现IO的多路复用
参数:
nfds:关注的最大的文件描述符+1
readfds:关注的读表
writefds:关注的写表
exceptfds:关注的异常表
timeout:超时的设置
NULL:一直阻塞,直到有文件描述符就绪或出错
时间值
为0:仅仅检测文件描述符集的状态,然后立即返回
时间值
不为0:在指定时间内,如果没有事件发生,则超时返回。
返回值:
准备好的文件描述符的个数
-1 失败:
0:超时检测时间到
注意:
select返回后 ,关注列表中只存在准备好的文件描述符
void FD_CLR(int fd, fd_set *set);
功能:
将fd从关注列表中清除
参数:
fd:文件描述符
set:关注列表的指针
返回值:无
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
net.h
#ifndef __NET_H__
#define __NET_H__
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#define errlog(errmsg) do{\
perror(errmsg);\
printf("%s -- %s -- %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
#define N 128
#endif
server.c
#include "net.h"
int main(int argc, const char *argv[])
{
int sockfd, acceptfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t addrlen = sizeof(serveraddr);
char buf[N] = { 0 };
if(argc < 3)
{
fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit(1);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
errlog("fail to socket");
}
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
errlog("fail to bind");
}
if(listen(sockfd, 5) < 0)
{
errlog("fail to listen");
}
fd_set readfds;
int maxfd;
FD_ZERO(&readfds);
maxfd = sockfd;
while(1)
{
FD_SET(0, &readfds);
FD_SET(sockfd, &readfds);
if(select(maxfd + 1, &readfds, NULL, NULL, NULL) < 0)
{
errlog("fail to select");
}
if(FD_ISSET(sockfd, &readfds))
{
if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0)
{
errlog("fail to accept");
}
printf("ip: %s, port: %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
close(acceptfd);
}
if(FD_ISSET(0, &readfds))
{
if(NULL != fgets(buf, N, stdin))
{
buf[strlen(buf) - 1] = '\0';
printf("buf : %s\n", buf);
}
}
if(0 == strncasecmp(buf, "quit", 4))
{
printf("server is quit!\n");
break;
}
}
close(sockfd);
return 0;
}
client.c
#include "net.h"
int main(int argc, const char *argv[])
{
int sockfd;
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
if(argc < 3)
{
fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
exit(1);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
errlog("fail to socket");
}
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
{
errlog("fail to connect");
}
close(sockfd);
return 0;
}
测试结果