linux--select

在linux中的I/O都是用文件描述符表示的,文件描述符就是一个int,比如0是标准输入,1是标准输出,2是标准错误输出。同理,socket也是返回一个int文件描述符。

select系统调用是用来让我们的程序监视多个文件描述符(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改变。

只要有一个描述符状态变为可用,select会停止堵塞,继续往下执行,这时我们就需要用轮询的方法查出是哪个文件描述符可用。

理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。
(2)若fd=5,执行FD_SET(fd,&set);后set变为00000100(第5位置为1)
(3)若再加入fd=2,fd=1,则set变为01100100
(4)执行select(6,&set,0,0,0)阻塞等待(第一个参数是最大的文件描述符+1)
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为01100000。注意:没有事件发生的fd=5被清空。

注意第五条中的fd=5被清空,说明只要是不活跃的文件描述符都会在select的阻塞被结束后从原有的文件描述集合fd_set中被去除。因此在socket使用select的场景下,是可以在while轮询中使用FD_ISSET宏来不断监听服务器端生成的socket文件描述符是否活跃。


FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。
FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否可读写,>0表示可读写。


可以采用select实现   并发服务器:多路复用I/O

下面是服务器select并发版本:

http://blog.csdn.net/vagrxie/article/details/3948204

select模型属于网络的I/O复用模型,比纯粹的阻塞I/O模型更具有实用性,因为可以同时等待多个描述字的就绪。

当年学习C/C++的时候,很少碰到底层以数字标示的描述字,只在写文件系统的去尝试各种情况,以获得最佳效率的时候实际尝试使用过一次,一直觉得那种open,write,read的文件操作方式,实在是比fopen一族函数还要低级的方式-_-!平时没有必要使用。但是等到网络编程的时候,才发现。。。。原来这么底层的东西,竟然也有一定的通用性,文件的描述字和网络的描述字竟然是一致的-_-!不管是谁设计的,还是挺佩服的。。。。。。

       这里仅仅是为了学习Select模型而写的学习例子,作用是在服务器端输出连接上的客户端的IP(仅以数字形式),然后将客户端的IP以字符串的形式返回,客户端连接服务器,并接受由服务器端返回的IP地址,然后输出转换为字符串形式的IP地址和数字形式的IP地址,为了区别select到正确的不同listen套接字,这里用了不同的端口,并且不同的两个套接字响应时以echo 1,echo 2区别。功能是很简单的,仅仅用于学习,所以其中很多地方本来可以抽出来称为函数的,都贪简单,直接复制了(-_-!这里本来习惯想说Ctrl-C Ctrl-V的。。。但是发现自己实在Ubuntu下用vim复制的,好像和实际情况不符。。。。)

       另外。。。。由于用的是《Unix Network Programming》一书,所以编程风格都变得有点像书中了。。。。服务器端全是自己写的,客户端代码由书中的daytime客户端改过来的,并且发现书中客户端代码都不关闭套接字,都交由退出进程的时候由系统关闭,不知道这种风格好不好。由于学习。。。写的是ANSI C程序,用gcc编译-_-!

unp.h是《Unix Network Programming》源代码中的公用头文件,makefile可能也得注意一下,为了图省事,我用了其源代码中的Make.defines,因为这样比自己写简单多了:),makefile就不贴了,没有什么学习意义。

 

运行效果如下:

客户端运行:

./TestSelectCli 127.0.0.1 1000

Conncet OK

127.0.0.1:16777343 Echo 1.

laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 127.0.0.1 1001

Conncet OK

127.0.0.1:16777343 Echo 2.

laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 192.168.0.138 1000

Conncet OK

192.168.0.138:2315299008 Echo 1.

laptop:~/unpv1/unpv13e/MyTest$ ./TestSelectCli 192.168.0.138 1001

Conncet OK

192.168.0.138:2315299008 Echo 2.

 

服务器端输出:

2315299008 Echo 1.

16777343 Echo 1.

16777343 Echo 2.

2315299008 Echo 1.

2315299008 Echo 2.

 

 

服务器端源代码:

 

  1 #include    "unp.h"
  2 
  3 
  4 void str_echo1(int connfd);
  5 void str_echo2(int connfd);
  6 
  7 int main(int argc, char **argv)
  8 {
  9     struct sockaddr_in cliaddr;
 10     pid_t childpid;
 11 
 12     /*  Bind 1000 port to listen socket 1 */
 13     int listenfd1 = Socket(AF_INET, SOCK_STREAM, 0);
 14 
 15     struct sockaddr_in servaddr;
 16     bzero(&servaddr, sizeof(servaddr));
 17     servaddr.sin_family = AF_INET;
 18     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 19     servaddr.sin_port = htons(1000);
 20 
 21     Bind(listenfd1, (SA *)&servaddr, sizeof(servaddr));
 22 
 23     Listen(listenfd1, LISTENQ);
 24 
 25     /*  Bind 1001 port to listen socket 2*/
 26     int listenfd2 = Socket(AF_INET, SOCK_STREAM, 0);
 27 
 28     struct sockaddr_in servaddr2;
 29     bzero(&servaddr2, sizeof(servaddr2));
 30     servaddr2.sin_family = AF_INET;
 31     servaddr2.sin_addr.s_addr = htonl(INADDR_ANY);
 32     servaddr2.sin_port = htons(1001);
 33 
 34     Bind(listenfd2, (SA *)&servaddr2, sizeof(servaddr2));
 35 
 36     Listen(listenfd2, LISTENQ);
 37 
 38     /* Initialize fd_set struct */
 39     int maxfdp1 = max(listenfd1, listenfd2) + 1;
 40     fd_set rset;
 41     FD_ZERO(&rset);
 42 
 43     /*  Select from this two listen socket */
 44     for( ; ; )
 45     {
 46         FD_SET(listenfd1, &rset);
 47         FD_SET(listenfd2, &rset);
 48 
 49         int nready = -1;
 50         if( (nready = select(maxfdp1, &rset, NULLNULL,NULL)) < 0)//只要有一个客户连接,select就停止堵塞
 51         {
 52             if(EINTR == errno)
 53             {
 54                 continue;
 55             }
 56             else
 57             {
 58                 err_sys("Select error.");
 59             }
 60         }
 61 
 62         /*  some one listening socket is readable.*/
 63         if(FD_ISSET(listenfd1, &rset))
 64         {
 65             socklen_t len = sizeof(cliaddr);
 66             int connfd = Accept(listenfd1, (SA *)&cliaddr, &len);
 67 
 68             if0 == (childpid = Fork()) )
 69             {
 70                 /* child process */
 71                 Close(listenfd1);
 72 
 73                 str_echo1(connfd);
 74                 exit(0);
 75             }
 76 
 77             /* parent process  */
 78             Close(connfd);
 79 
 80         }
 81 
 82 
 83         if(FD_ISSET(listenfd2, &rset))
 84         {
 85             socklen_t len = sizeof(cliaddr);
 86             int connfd = Accept(listenfd2, (SA *)&cliaddr, &len);
 87 
 88             if0 == (childpid = Fork()) )
 89             {
 90                 /* child process */
 91                 Close(listenfd2);
 92 
 93                 str_echo2(connfd);
 94                 exit(0);
 95             }
 96 
 97             /* parent process  */
 98             Close(connfd);
 99 
100         }
101 
102     }
103 
104     exit(0);
105 }
106 
107 void str_echo1(int connfd)
108 {
109     struct sockaddr_in clientAddr;
110     socklen_t len = sizeof(clientAddr);
111 
112     if(getpeername(connfd, (SA*) &clientAddr, &len) < 0)
113     {
114         return;
115     }
116 
117     char lcBuffer[MAXLINE] = {0};
118     sprintf(lcBuffer, "%u Echo 1.", clientAddr.sin_addr.s_addr);
119 
120     printf("%s/n", lcBuffer);
121 
122     Write(connfd, lcBuffer, MAXLINE);
123 }
124 
125 
126 void str_echo2(int connfd)
127 {
128     struct sockaddr_in clientAddr;
129     socklen_t len = sizeof(clientAddr);
130 
131     if(getpeername(connfd, (SA*) &clientAddr, &len) < 0)
132     {
133         return;
134     }
135 
136 
137     char lcBuffer[MAXLINE] = {0};
138     sprintf(lcBuffer, "%u Echo 2.", clientAddr.sin_addr.s_addr);
139 
140     printf("%s/n", lcBuffer);
141 
142     Write(connfd, lcBuffer, MAXLINE);
143 }
144 
145

 

客户端源代码:

 1 #include    "unp.h"
 2 
 3 int main(int argc, char **argv)
 4 {
 5     int                    sockfd, n;
 6     char               recvline[MAXLINE + 1];
 7     struct sockaddr_in servaddr;
 8 
 9     if (argc != 3)
10         err_quit("usage: a.out <IPaddress> <IPPort>");
11 
12     int port = atoi(argv[2]);
13 
14     if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
15         err_sys("socket error");
16 
17     bzero(&servaddr, sizeof(servaddr));
18     servaddr.sin_family = AF_INET;
19     servaddr.sin_port   = htons(port);
20     if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <=0)
21         err_quit("inet_pton error for %s", argv[1]);
22 
23     if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
24         err_sys("connect error");
25 
26     printf("Conncet OK/n");
27 
28     while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
29         recvline[n] = 0;  /* null terminate */
30 
31         /*  change number string to number and to ip string */
32         struct in_addr svraddr;
33         svraddr.s_addr = strtoul(recvline, NULL10);
34         char *pszsvraddr = inet_ntoa(svraddr);
35 
36         printf("%s:%s/n", pszsvraddr, recvline);
37     }
38     if (n < 0)
39         err_sys("read error");
40 
41     exit(0);
42 }


下面更简洁的代码会加深理解

我们要监控readfd文件描述符数组

int use_select(int *readfd,int n) 

   fd_set my_readfd; 
   int maxfd; 
   int i; 
    
   maxfd=readfd[0]; 
   for(i=1;i <n;++i)   if(readfd[i]>maxfd) maxfd=readfd[i]; 
   while(1) 
   { 
        /*   将所有的文件描述符加入   */ 
        FD_ZERO(&my_readfd); 
        for(i=0;i  <n;++i)           FD_SET(readfd[i],*my_readfd); 
        /*     进程阻塞                 */ 
        select(maxfd+1,& my_readfd,NULL,NULL,NULL); 
        /*        有东西可以读了       */ 
        for(i=0;i <n;++i)     if(FD_ISSET(readfd[i],&my_readfd)) 
              { 
                  /* 原来是我可以读了 */ 
                        we_read(readfd[i]); 
              } 
   } 
}


select监控的是一个文件描述符数组,一旦至少一个文件描述符可以用了,select停止堵塞,这时程序需要一个轮询确定具体哪个文件可以用了。


下面是一个 用select实现的异步聊天程序:

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>

#define MAXBUF 1024
/************关于本文档********************************************
*filename: async-server.c
*purpose: 演示网络异步通讯,这是服务器端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 21:22
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];
    fd_set rfds;
    struct timeval tv;
    int retval, maxfd = -1;

    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;

    if (argv[2])
        lisnum = atoi(argv[2]);
    else
        lisnum = 2;

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    if (argv[3])
        my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    else
        my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
        == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(sockfd, lisnum) == -1) {
        perror("listen");
        exit(1);
    }

    while (1) {
        printf
            ("\n----等待新的连接到来开始新一轮聊天……\n");
        len = sizeof(struct sockaddr);
        if ((new_fd =
             accept(sockfd, (struct sockaddr *) &their_addr,
                    &len)) == -1) {
            perror("accept");
            exit(errno);
        } else
            printf("server: got connection from %s, port %d, socket %d\n",
                   inet_ntoa(their_addr.sin_addr),
                   ntohs(their_addr.sin_port), new_fd);

        /* 开始处理每个新连接上的数据收发 */
        printf
            ("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n");
        while (1) {
            /* 把集合清空 */
            FD_ZERO(&rfds);
            /* 把标准输入句柄0加入到集合中 */
            FD_SET(0, &rfds);
            maxfd = 0;
            /* 把当前连接句柄new_fd加入到集合中 */
            FD_SET(new_fd, &rfds);
            if (new_fd > maxfd)
                maxfd = new_fd;
            /* 设置最大等待时间 */
            tv.tv_sec = 1;
            tv.tv_usec = 0;
            /* 开始等待 */
            retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
            if (retval == -1) {
                printf("将退出,select出错! %s", strerror(errno));
                break;
            } else if (retval == 0) {
                /* printf
                   ("没有任何消息到来,用户也没有按键,继续等待……\n"); */
                continue;
            } else {
                if (FD_ISSET(0, &rfds)) {
                    /* 用户按键了,则读取用户输入的内容发送出去 */
                    bzero(buf, MAXBUF + 1);
                    fgets(buf, MAXBUF, stdin);
                    if (!strncasecmp(buf, "quit", 4)) {
                        printf("自己请求终止聊天!\n");
                        break;
                    }
                    len = send(new_fd, buf, strlen(buf) - 1, 0);
                    if (len > 0)
                        printf
                            ("消息:%s\t发送成功,共发送了%d个字节!\n",
                             buf, len);
                    else {
                        printf
                            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
                             buf, errno, strerror(errno));
                        break;
                    }
                }
                if (FD_ISSET(new_fd, &rfds)) {
                    /* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */
                    bzero(buf, MAXBUF + 1);
                    /* 接收客户端的消息 */
                    len = recv(new_fd, buf, MAXBUF, 0);
                    if (len > 0)
                        printf
                            ("接收消息成功:'%s',共%d个字节的数据\n",
                             buf, len);
                    else {
                        if (len < 0)
                            printf
                                ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
                                 errno, strerror(errno));
                        else
                            printf("对方退出了,聊天终止\n");
                        break;
                    }
                }
            }
        }
        close(new_fd);
        /* 处理每个新连接上的数据收发结束 */
        printf("还要和其它连接聊天吗?(no->退出)");
        fflush(stdout);
        bzero(buf, MAXBUF + 1);
        fgets(buf, MAXBUF, stdin);
        if (!strncasecmp(buf, "no", 2)) {
            printf("终止聊天!\n");
            break;
        }
    }

    close(sockfd);
    return 0;
}

用select实现服务器的好处是,当客户与其建立连接之后,客户可能要过段时间才发送消息,如果之前普通的处理,服务器只能堵塞在recv,不能有其他操作;而用select监控输入文件描述符和socket描述符,只要有一个准备好,select就返回,譬如,select堵塞时,只要服务器输入消息,并回车发送时,select检测到输入文件描述符,就立马返回,将消息发送给客户端;



客户端程序:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#define MAXBUF 1024
/************关于本文档********************************************
// *filename: ssync-client.c
*purpose: 演示网络异步通讯,这是客户端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 21:32
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    int sockfd, len;
    struct sockaddr_in dest;
    char buffer[MAXBUF + 1];
    fd_set rfds;
    struct timeval tv;
    int retval, maxfd = -1;

    if (argc != 3) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }
    /* 创建一个 socket 用于 tcp 通信 */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
        perror(argv[1]);
        exit(errno);
    }

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }

    printf
        ("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n");
    while (1) {
        /* 把集合清空 */
        FD_ZERO(&rfds);
        /* 把标准输入句柄0加入到集合中 */
        FD_SET(0, &rfds);
        maxfd = 0;
        /* 把当前连接句柄sockfd加入到集合中 */
        FD_SET(sockfd, &rfds);
        if (sockfd > maxfd)
            maxfd = sockfd;
        /* 设置最大等待时间 */
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        /* 开始等待 */
        retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
        if (retval == -1) {
            printf("将退出,select出错! %s", strerror(errno));
            break;
        } else if (retval == 0) {
            /* printf
               ("没有任何消息到来,用户也没有按键,继续等待……\n"); */
            continue;
        } else {
            if (FD_ISSET(sockfd, &rfds)) {
                /* 连接的socket上有消息到来则接收对方发过来的消息并显示 */
                bzero(buffer, MAXBUF + 1);
                /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
                len = recv(sockfd, buffer, MAXBUF, 0);
                if (len > 0)
                    printf
                        ("接收消息成功:'%s',共%d个字节的数据\n",
                         buffer, len);
                else {
                    if (len < 0)
                        printf
                            ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
                             errno, strerror(errno));
                    else
                        printf("对方退出了,聊天终止!\n");
                    break;
                }
            }
            if (FD_ISSET(0, &rfds)) {
                /* 用户按键了,则读取用户输入的内容发送出去 */
                bzero(buffer, MAXBUF + 1);
                fgets(buffer, MAXBUF, stdin);
                if (!strncasecmp(buffer, "quit", 4)) {
                    printf("自己请求终止聊天!\n");
                    break;
                }
                /* 发消息给服务器 */
                len = send(sockfd, buffer, strlen(buffer) - 1, 0);
                if (len < 0) {
                    printf
                        ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
                         buffer, errno, strerror(errno));
                    break;
                } else
                    printf
                        ("消息:%s\t发送成功,共发送了%d个字节!\n",
                         buffer, len);
            }
        }
    }
    /* 关闭连接 */
    close(sockfd);
    return 0;
}
编译用如下命令:
gcc -Wall async-server.c -o server
gcc -Wall async-client.c -o client
运行用如下命令:
./server 7838 1
./client 127.0.0.1 7838



下面还是一个简单的并发处理的服务器,但是仅仅是能多路复用I/O,但是不能同时处理连接请求,因为这只有一个线程

http://www.cnblogs.com/faraway/archive/2009/03/06/1404449.html

使用select函数可以以非阻塞的方式和多个socket通信。程序只是演示select函数的使用,功能非常简单,即使某个连接关闭以后也不会修改当前连接数,连接数达到最大值后会终止程序。

1. 程序使用了一个数组fd_A,通信开始后把需要通信的多个socket描述符都放入此数组。

2. 首先生成一个叫sock_fd的socket描述符,用于监听端口。

3. 将sock_fd和数组fd_A中不为0的描述符放入select将检查的集合fdsr。

4. 处理fdsr中可以接收数据的连接。如果是sock_fd,表明有新连接加入,将新加入连接的socket描述符放置到fd_A。

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>



#define MYPORT 1234    // the port users will be connecting to



#define BACKLOG 5     // how many pending connections queue will hold



#define BUF_SIZE 200



int fd_A[BACKLOG];    // accepted connection fd

int conn_amount;    // current connection amount



void showclient()

{

    int i;

    printf("client amount: %d\n", conn_amount);

    for (i = 0; i < BACKLOG; i++) {

        printf("[%d]:%d  ", i, fd_A[i]);

    }

    printf("\n\n");

}



int main(void)

{

    int sock_fd, new_fd;  // listen on sock_fd, new connection on new_fd

    struct sockaddr_in server_addr;    // server address information

    struct sockaddr_in client_addr; // connector's address information

    socklen_t sin_size;

    int yes = 1;

    char buf[BUF_SIZE];

    int ret;

    int i;



    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {

        perror("socket");

        exit(1);

    }



    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {

        perror("setsockopt");

        exit(1);

    }

    

    server_addr.sin_family = AF_INET;         // host byte order

    server_addr.sin_port = htons(MYPORT);     // short, network byte order

    server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP

    memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));



    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {

        perror("bind");

        exit(1);

    }



    if (listen(sock_fd, BACKLOG) == -1) {

        perror("listen");

        exit(1);

    }



    printf("listen port %d\n", MYPORT);



    fd_set fdsr;

    int maxsock;

    struct timeval tv;



    conn_amount = 0;

    sin_size = sizeof(client_addr);

    maxsock = sock_fd;

    while (1) {

        // initialize file descriptor set

        FD_ZERO(&fdsr);

        FD_SET(sock_fd, &fdsr);



        // timeout setting

        tv.tv_sec = 30;

        tv.tv_usec = 0;



        // add active connection to fd set

        for (i = 0; i < BACKLOG; i++) {

            if (fd_A[i] != 0) {

                FD_SET(fd_A[i], &fdsr);

            }

        }



        ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);

        if (ret < 0) {

            perror("select");

            break;

        } else if (ret == 0) {

            printf("timeout\n");

            continue;

        }



        // check every fd in the set

        for (i = 0; i < conn_amount; i++) {

            if (FD_ISSET(fd_A[i], &fdsr)) {

                ret = recv(fd_A[i], buf, sizeof(buf), 0);

                if (ret <= 0) {        // client close

                    printf("client[%d] close\n", i);

                    close(fd_A[i]);

                    FD_CLR(fd_A[i], &fdsr);

                    fd_A[i] = 0;

                } else {        // receive data

                    if (ret < BUF_SIZE)

                        memset(&buf[ret], '\0', 1);

                    printf("client[%d] send:%s\n", i, buf);

                }

            }

        }



        // check whether a new connection comes

        if (FD_ISSET(sock_fd, &fdsr)) {

            new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);

            if (new_fd <= 0) {

                perror("accept");

                continue;

            }



            // add to fd queue

            if (conn_amount < BACKLOG) {

                fd_A[conn_amount++] = new_fd;

                printf("new connection client[%d] %s:%d\n", conn_amount,

                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

                if (new_fd > maxsock)

                    maxsock = new_fd;

            }

            else {

                printf("max connections arrive, exit\n");

                send(new_fd, "bye", 4, 0);

                close(new_fd);

                break;

            }

        }

        showclient();

    }



    // close other connections

    for (i = 0; i < BACKLOG; i++) {

        if (fd_A[i] != 0) {

            close(fd_A[i]);

        }

    }



    exit(0);

}

有网友提出了点改进:

accept函数里面fd_A[conn_amount++] = new_fd;可以稍加改进,按照楼主的意图,会出现当一个用户不断连接再断开的情况下,当连接次数超过maxconnection的时候,就会退出,因此fd_A[i]没有很好的利用,不能实现动态管理,我建议仅将conn_amount仅作为客户端连接数,而不是有连接就增加,当accept成功的时候,就加1,当recv=0的时候就减1;建议将fd_A[conn_amount++] = new_fd;这句程序改为
for(i = 0;i < MAXCLIENT;i++)
{
if(fd[i] == 0)
{
fd[i] = new_fd;
break;
}

}
conn_amount++;
这样就可以重复利用fd[i]的空间;
另外在recv返回值<=0的时候,加一句conn_amount++;
还有一点,超过最大连接数的时候break应该为continue,这样会更人性化一点,客户端太多关闭它的请求就行了,没必要自毁,这样整个系统就可以动态与客户端实现连接,





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值