为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用I/O模型.
首先介绍一个函数select
int select(int nfds,fd_set *readfds,fd_set *writefds,
fd_set *except fds,struct timeval *timeout)
void FD_SET(int fd,fd_set *fdset)
void FD_CLR(int fd,fd_set *fdset)
void FD_ZERO(fd_set *fdset)
int FD_ISSET(int fd,fd_set *fdset)
一般的来说当我们在向文件读写时,进程有可能在读写出阻塞,直到一定的条件满足. 比如我们从一个套接字读数据时,可能缓冲区里面没有数据可读 (通信的对方还没有发送数据过来),这个时候我们的读调用就会等待(阻塞)直到有数据可读.如果我们不 希望阻塞,我们的一个选择是用select系统调用. 只要我们设置好select的各个参数,那么当文件可以读写的时候select回"通知"我们 说可以读写了.
readfds所有要读的文件文件描述符的集合
writefds所有要的写文件文件描述符的集合
exceptfds其他的服要向我们通知的文件描述符
timeout超时设置.
nfds所有我们监控的文件描述符中最大的那一个加1
在我们调用select时进程会一直阻塞直到以下的一种情况发生.
1)有文件可以读.
2)有文件可以写.
3)超时所设置的时间到.
为了设置文件描述符我们要使用几个宏.
FD_SET将fd加入到fdset
FD_CLR将fd从fdset里面清除
FD_ZERO从fdset中清除所有的文件描述符
FD_ISSET判断fd是否在fdset集合中
*****************************************
使用select的一个例子
int use_select(int *readfd,int n)
{
fd_set my_readfd;
int maxfd;
int i;
maxfd=readfd[0];
for(i=1;i
if(readfd[i]>maxfd) maxfd=readfd[i];
while(1)
{
FD_ZERO(&my_readfd);
for(i=0;i
FD_SET(readfd[i],*my_readfd);
select(maxfd+1,& my_readfd,NULL,NULL,NULL);
for(i=0;i
if(FD_ISSET(readfd[i],&my_readfd))
{
we_read(readfd[i]);
}
}
}
*****************************************
使用select后我们的服务器程序就变成了.
初始话(socket,bind,listen);
while(1)
{
设置监听读写文件描述符(FD_*);
调用select;
如果是倾听套接字就绪,说明一个新的连接请求建立
{
建立连接(accept);
加入到监听文件描述符中去;
}
否则说明是一个已经连接过的描述符
{
进行操作(read或者write);
}
}
多路复用I/O可以解决资源限制的问题.着模型实际上是将UDP循环模型用在了TCP上面. 这也就带来了一些问题.如由于服务器依次处理客户的请求,所以可能会导致有的客户 会等待很久.
——————————————————————————————————————————————————————
/*
filename: server.c
function: client input a message and server echo it !
version: ipv4 -> ipv6
conpile: gcc -Wall -lpthread server.c -o server
run: ./server port listennum ipv6
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#define PORT 15636
#define BACKLOG 5
#define MAXLINE 100
typedef struct _CLIENT
{
int fd;
}CLIENT;
int iListenfd;
void *thread(void *v_pArg);
void f(int signo)
{
close(iListenfd);
printf(" --*Server over ! Thank you for use ! *--\n");
exit(0);
}
int main(int argc, char *argv[])
{
if (argc < 4)
printf("usage: server port num ipv6\n"),exit(-1);
signal(SIGINT, f);
fd_set rset, allset;
int maxfd, maxi, i;
CLIENT client[FD_SETSIZE];
unsigned int myport,lisnum;
if (argv[1])
myport = atoi(argv[1]);
else
myport = PORT;
if (argv[2])
lisnum = atoi(argv[2]);
else
lisnum = BACKLOG;
/*create socket*/
//iListenfd = socket(AF_INET, SOCK_STREAM, 0);
iListenfd = socket(AF_INET6, SOCK_STREAM, 0); //ipv6
if (iListenfd < 0) {
perror("socket error"), exit(1);
}
printf("1. creating socket success!\n");
int ireuse = 1;
/*set socket option : reuse addr*/
if (setsockopt(iListenfd, SOL_SOCKET, SO_REUSEADDR,
&ireuse, sizeof(int)) < 0) {
perror("setsockopt error"), exit(1);
}
//struct sockaddr_in serveraddr;
struct sockaddr_in6 serveraddr; //ipv6
bzero((char*)&serveraddr, sizeof(serveraddr));
//serveraddr.sin_family = AF_INET;
serveraddr.sin6_family = AF_INET6; //ipv6
//serveraddr.sin_port = htons(myport);
serveraddr.sin6_port = htons(myport); //ipv6
if (argv[3])
//serveraddr.sin_addr.s_addr = inet_addr(argv[3]);
inet_pton(AF_INET6, argv[3], &serveraddr.sin6_addr);
else
//serveraddr.sin_addr.s_addr = inet_addr(INADDR_ANY);
serveraddr.sin6_addr = in6addr_any; //in6addr_any must little
/*bind socket*/
if (bind(iListenfd, (struct sockaddr *)&serveraddr,
sizeof(serveraddr)) < 0) {
perror("bind error"), exit(1);
}
/* start listening */
if (listen(iListenfd, lisnum) < 0) {
perror("listen error"), exit(1);
} else {
printf("2. Waitting client connect ...\n");
}
maxfd = iListenfd;
maxi = -1;
for (i=0; i < FD_SETSIZE; i++)
{
client[i].fd = -1;
}
FD_ZERO(&allset);
FD_SET(iListenfd, &allset);
pthread_t tid;
//void *ret;
int nready;
int iClientLen, iConnfd, sockfd;
while (1)
{
//struct sockaddr_in clientaddr;
struct sockaddr_in6 clientaddr; //ipv6
rset = allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
printf("3. There are %d fd(s) active.\n", nready);
if (FD_ISSET(iListenfd, &rset)) {
printf("4. Accept ok.\n");
iClientLen = sizeof(clientaddr);
iConnfd = accept(iListenfd, (struct sockaddr *)&clientaddr,
(socklen_t*)&iClientLen);
if (iConnfd < 0) {
perror("accept failed"), exit(1);
} else {
printf("5. Server accepted...\n");
}
for (i=0; i < FD_SETSIZE; i++) {
if (client[i].fd < 0) {
client[i].fd = iConnfd;
break;
}
}
if (i == FD_SETSIZE) {
printf("Too many clients\n");
}
FD_SET(iConnfd, &allset); //add new client fd
if (iConnfd > maxfd)
maxfd = iConnfd; //to sure maxfd is max
if (i > maxi)
maxi = i;
if (--nready <= 0)
continue; //no more new action
}
for (i=0; i <= maxi; i++) {
if ((sockfd = client[i].fd) < 0)
continue;
if (FD_ISSET(sockfd, &rset)) {
pthread_create(&tid, NULL, thread, &sockfd);
FD_CLR(sockfd, &allset);
client[i].fd = -1;
if (--nready <= 0)
break;
}
}
}
close(iListenfd);
return EXIT_SUCCESS;
}
void *thread(void *v_pArg)
{
int iConnfd = *((int *)v_pArg);
int idata;
char temp[100];
printf("\n-----------------------start----------------------------\n");
while (1) {
idata = recv(iConnfd, temp, 100, 0);
if (idata > 0)
{
printf("client say: %s\n", temp);
}
send(iConnfd, temp, 100, 0);
if (strcmp(temp,"bye\n") == 0)
{
printf("-----------------------end----------------------------\n");
close(iConnfd);
break;
}
}
return NULL;
}
/*
filename: clent.c
function: client input a message and server echo it !
version: ipv4 -> ipv6
conpile: gcc -Wall -lpthread client.c -o client
run: ./client ipv6 port
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#define MAXLINE 100
#define PORT 15636
void *threadsend(void *v_parg);
void *threadrecv(void *v_parg);
int main(int argc, char *argv[])
{
if (argc < 3)
printf("usage: client ipv6 port\n"),exit(-1);
int iclientfd;
unsigned int myport;
if (argv[2])
myport = atoi(argv[2]);
else
myport = PORT;
/* create socket */
//iclientfd = socket(AF_INET, SOCK_STREAM, 0);
iclientfd = socket(AF_INET6, SOCK_STREAM, 0); //ipv6
if (iclientfd < 0) {
perror("socket error"), exit(1);
}
//struct sockaddr_in serveraddr;
struct sockaddr_in6 serveraddr; //ipv6
bzero((char*)&serveraddr, sizeof(serveraddr));
//serveraddr.sin_family = AF_INET;
serveraddr.sin6_family = AF_INET6; //ipv6
//serveraddr.sin_port = htons(myport);
serveraddr.sin6_port = htons(myport); //ipv6
if (argv[1])
//serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
inet_pton(AF_INET6, argv[1], &serveraddr.sin6_addr); //ipv6
else
//serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
inet_pton(AF_INET6, "::1/128", &serveraddr.sin6_addr);
/* try connect */
if (connect(iclientfd, (struct sockaddr *)&serveraddr,
sizeof(serveraddr)) < 0) {
perror("connect error\n"), exit(1);
} else {
printf("Client connected...\n");
}
pthread_t tid;
pthread_create(&tid, NULL, threadsend, &iclientfd);
pthread_join(tid, 0);
return EXIT_SUCCESS;
}
void *threadsend(void *v_parg)
{
int iconnfd = *((int*)v_parg);
char sendbuf[100], recvbuf[100];
int idata;
printf("\n-----------------------start----------------------------\n");
while (1)
{
fgets(sendbuf, 100, stdin);
send(iconnfd, sendbuf, 100, 0);
printf(" ~client send OK! ^-^\n");
idata = recv(iconnfd, recvbuf, 100, 0);
if (idata > 0) {
printf("server echo: %s\n", recvbuf);
}
if (strcmp(sendbuf, "bye\n") == 0) {
printf("-----------------------end----------------------------\n");
printf(" --* Client over ! Thank you for use! *--\n");
close(iconnfd);
break;
}
}
return NULL;
}
——————————————————————————————————————————————————————