server.c客户端服务器select 赵超越

班级:09计算机用技术1班     姓名:赵超越      学号:0906041011

 

/* server.c */
#include
#include
#include
#include
#include "wrap.h"

#define MAXLINE 80
                 /*宏定义 MAXLINE 为 80*/
#define SERV_PORT 8000
           /*宏定义 端口号为 8000*/

int main(int argc, char **argv)
{
    int i, maxi, maxfd, listenfd, connfd, sockfd;
    int nready, client[FD_SETSIZE];
    ssize_t n;
    fd_set rset, allset;
                                  /*定义fd_set(文件描述符的集合)型变量 rset allset*/
    char buf[MAXLINE];
                               /*定义缓冲区数组buf[ ]*/
    char str[INET_ADDRSTRLEN];
                /*定义字符数组str[ ]*/
    socklen_t cliaddr_len;
    struct sockaddr_in    cliaddr, servaddr;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);
      /*打开一个网络通讯端口,地址类型IPV4,TCP协议*/


    bzero(&servaddr, sizeof(servaddr));
                        /*servaddr 初始化*/
    servaddr.sin_family      = AF_INET;
                        /*设置地址类型为 IPV4*/
    servaddr.sin_a
ddr.s_addr = htonl(INADDR_ANY);         /*设置网络地址为本地任意IP地址*/

servaddr.sin_port = htons(SERV_PORT); /*定义端口号 SERV_PORT(8000)*/
   

 Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));                                /*调用bind将参数listenfd与servaddr绑定,使listenfd监听servaddr所描述的地址和端口*/

    Listen(listenfd, 20);
     /*listen()声明listenfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,0—成功,-1—失败*/

    maxfd = listenfd;        /
*将maxfd初始化*/
    maxi = -1;           
   /*将maxi赋初值为-1*/
    for (i = 0; i < FD_SETSIZE; i+
+)
        client[i] = -1;   
                          /*将client[i]中的每一个元素赋初值为-1*/
    FD_ZERO(&allset);
            /* 将文件描述符集合allset*/
    FD_SET(listenfd, &allset);
     /*将监听文件描述符listenfd放入集合allset中*/

    for ( ; ; )
{            /*死循环*/
        rset = allset;   
        nready = select(maxf
d+1, &rset, NULL, NULL, NULL);/* 调用select函数它可以同时监听多个阻塞的文件描述符,并且处理有数据到达的文件描述符,并返回更新的个数给nready,没有数据到达就阻塞*/
        if (nready < 0)
            perr_exit("select error");
     /*当nready小于0时出错* /

        if (FD_ISSET(listenfd, &rset)) {
/*检测文件描述符listenfd是否在rset集合(检测rset中是否有新的连接出现/*
           
cliaddr_len = sizeof(cliaddr); /*cliaddr_len赋初值,并将本地客户端地址的长度放入变量cliaddr_len中*/
         
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);  /*服务器调用accept()接受连接,若没有客户端的连接请求,就阻塞等待直到有客户端连接上来;若有客户端连接则解除阻塞状态,并返回一个服务器与客户端互相通信的文件描述符connfd*/

            printf("received from %s at PORT %d\n",
                   inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
           
       ntohs(cliaddr.sin_port)); /*打印连接的客户端地址和端口号*/

            for (i = 0; i < FD_SETSIZE; i++)
                if (client[i] < 0) {
                    client[i] = connfd;
/*将已连接到服务器的客户端的文件描述符放入数组client[i]中*/
                    break;
             
  }
            if (i == FD_SETSIZE) {
                fputs("too many clients\n", stderr);/
*如果i == FD_SETSIZE表示连接已满,输出错误信息" too many clients "*/
                exit(1);
     /*异常退出*/
            }

            FD_SET(connfd, &allset);
/*将新的文件描述符connfdt添加到allset中 */
            if (connfd > maxfd)
                maxfd = connfd;
/*如果connfd > maxfd,则将connfd的值赋给maxfd使maxfd存放的是最大的文件描述符*/
            if (i > maxi)
                maxi = i;   
/*如果i > maxi,将i的值赋给maxi使得maxi存放的是值最大的i*/

            if (--nready == 0)

                continue;                                    /*当nready为0,没有可读的文件描述符,结束本次循环。并开始执行下一次循环*/

        }

        for (i = 0; i <= maxi; i++) {   
                /* check all clients for data */
            if ( (sockfd = client[i]) < 0)
                continue;
                                        /*如果没有连接,结束本次循环,并开始执行下一次循环*/
            if (FD_ISSET(sockfd, &rset))
        /*检测文件描述符listenfd是否在rset集中*/

{
             
if ( (n = Read(sockfd, buf, MAXLINE)) == 0)                 /*判断客户端连接是否关闭*/

{                           
                    Close(sockfd);
                                        /*如果客户端连接关闭,服务器端连接关闭*/
                    FD_CLR(sockfd, &allset);
                /*sockfd将文件描述符从allset中清除*/
                    client[i] = -1;
                } else {
                                /*若客户端连接没有关闭*/
                    int j;
                    for (j = 0; j < n; j++)
                        buf[j] = toupper(buf[j]);
                /*将buf[j]中的小写字母全部转换为大写字母,并存入buf[j]中*/
                    Write(sockfd, buf, n);
                  /*调用write将处理结果发给客户端*/
                }

                if (--nready == 0)
                    break;    /
*当nready为0,没有可读的文件描述符,结束本次循环*/
            }
        }
    }
}
执行过程:

首先系统调用socekt返回一个文件描述符listenfd监听SERV_PORT端口。然后调用bind将listenfd 和服务器地址servaddr 绑定在一起,使listenfd这个用于网络通讯的文件描述符监听servaddr 所描述的地址(本地任意IP地址)和端口号(8000)服务器调用listen()声明sockfd处于监听状态,并且最多允许有20个客户端处于连接待状态,如果接收到更多的连接请求就忽略。

然后把listenfd加入allset集合中调 用select()函数判断rset 中存放的listenfd 是否有数据到达,现在有一个文件描述符listenfd 在rset 中,假设有数据到达返回1赋给nready , 判断 nready 不小于0,所以不出错。接着服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接 上来。当集合里有可读的socket时,select返回。select返回后,判断是否是listenfd可读,如果为真,则说明有新的客户端连接进来,然后调用accept函数进行接收accept()返回时返回一个服务器与客户端互相通信的文件描述符connfd,存放传出客户端的地址和端口号并打印Accept到的connfd保存到客户端列表里(client[i] 数组中的第一个位置), 通过if 语句判断i 不等于FD_SETSIZE退出。把最大的描述符添加到allset集中,把最大的i 值赋给maxi , nready = 1继续返回执行for循环,重新等待监听来自客户端的连接请求。 把client[i] 中第一个元素(即第一个文件描述符listenfd )赋给sockfd 不小于0执行下面的if 语句,判断sockfd(listenfd )在rset 集中,接着判断客户端是否关闭连接若已经关闭连接,将sockfd(listenfd )从allset 集中清除,将client[i]的第一个元素赋值为-1若没有关闭连接,将接收到的来自客户端的数据由小写转换为大写,重新返回给客户端果客户端断开,那么从client列表和集合里去除掉。连接关闭后,此时nready = 1执行“——”操作后后等于0没有更多的文件描述符,结束本次循环,进入下一次for循环,重新监听来自客户端的连接请求。

 

执行结果:

终端1:

[root@localhost ~]#cd /opt/zcy/

[root@localhost zcy]#gcc server.c -o server

[root@localhost zcy]#./server

received from 127.0.0.1 at PORT 50275

received from 127.0.0.1 at PORT 50276

终端2:

[root@localhost zcy]# ./client

zcy

ZCY

nypd

NYPD

www

WWW

终端3:

[root@localhost ~]# cd /opt/zcy/

[root@localhost zcy]# ./client

zhongduan3

ZHONGDUAN3

 

 

 

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(336) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值