IO多路转接模型与范例

并发服务器:多路复用I/O
    为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用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;
}

——————————————————————————————————————————————————————


你可能感兴趣的:(thread,tcp,socket,select,setsockopt)