• select--服务器与客户端 郑明莉


    班级:09计应用二班  姓名:郑明莉  学号:0906042007

     

    /* server.c */

    #include

    #include

    #include

    #include

    #include "wrap.h"

     

    #define MAXLINE 80 //通道大小

    #define SERV_PORT 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;

          char buf[MAXLINE];

          char str[INET_ADDRSTRLEN];

          socklen_t cliaddr_len;

          struct sockaddr_in      cliaddr, servaddr;

     

          listenfd = Socket(AF_INET, SOCK_STREAM, 0); //打开一个IPV4的TCP连接端口

    bzero(&servaddr, sizeof(servaddr));//清空服务器的地址

    servaddr.sin_family = AF_INET;//服务器的地址类型为IPV4

          servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//服务器允许任何客户端访问

    servaddr.sin_port= htons(SERV_PORT);//服务器端口设为8000

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//把监听端口和服务器通信端口绑定

    Listen(listenfd, 20);开始监听服务器通信端口

    maxfd = listenfd;            /* initialize */把最新的连接描述符赋值给maxfd,三次握手第二步;

    maxi = -1;                  /* index into client[] array */maxi初始化为-1;

          for (i = 0; i < FD_SETSIZE; i++)

                client[i] = -1;      /* -1 indicates available entry */初始化连接空间

          FD_ZERO(&allset);把allset清空

          FD_SET(listenfd, &allset);把listenfd描述符添加进allset文件描述符集

     

          for ( ; ; ) {

                rset = allset;      /* structure assignment */把连接赋值给rset

                nready = select(maxfd+1, &rset, NULL, NULL, NULL);把已连接的描述符保存在nready里;

                if (nready < 0)

                      perr_exit("select error");

     

                if (FD_ISSET(listenfd, &rset)) { /* new client connection */如果listenfd已经在rset集里,则执行此命令下面的语句;

                      cliaddr_len = sizeof(cliaddr);客户端地址长度

                      connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);把新的描述符给connfd,接受listenfd连接,三次握手完成;

     

                      printf("received from %s at PORT %d ",

                             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; /* save descriptor */把已连接的客户端文件描述符保存在client 数组里

                                  break;

                            }

                      if (i == FD_SETSIZE) {

                            fputs("too many clients ", stderr);如果连接已满则输出错误信息

                            exit(1);错误输出

                      }

     

                      FD_SET(connfd, &allset);      /* add new descriptor to set */将新的文件描述符添加进allset 里!

                      if (connfd > maxfd)

                            maxfd = connfd; /* for select */把最新的文件描述符保存在maxfd里

                      if (i > maxi)

                            maxi = i;      /* max index in client[] array */把已有的连接总数保存在maxi里

     

                      if (--nready == 0)

                            continue;      /* no more readable descriptors */如果没有文件描述符可读则结束此次循环

                }

     

                for (i = 0; i <= maxi; i++) {      /* check all clients for data */

                      if ( (sockfd = client[i]) < 0)如果没连接则结束此次循环

                            continue;

                      if (FD_ISSET(sockfd, &rset)) {如果sockfd在rset里则执行下面的语句

                            if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {如果没有读到内容则执行下面语句

                                  /* connection closed by client */

                                  Close(sockfd);关闭通信端口

                                  FD_CLR(sockfd, &allset);从allset中清除sockfd这个端口文件描述符

                                  client[i] = -1;将这个空间设置为没有连接

                            } else {如果读到内容则将服务器接受来自客户端的数据,返回给客户端

                                  int j;

                                  for (j = 0; j < n; j++)

                                        buf[j] = toupper(buf[j]);将读到的数据转换为大写

                                  Write(sockfd, buf, n);把转换为大写的内容写进buf通道里,写回客户端

                            }

     

                            if (--nready == 0)如果没有客户写入数据则结束这个循环体

                                  break;      /* no more readable descriptors */

                      }

                }

          }

    }

     

    执行过程:

    Listenfd=socket(AF_INET,SOCK_STREAM,0)从此句开始,

       调用函数Socket(),打开一个IPV4的TCP连接端口,返回一套接字文件描述符赋值给监听套接字listenfd.再把servaddr清空,IPV4的地址类型设置为AF­_INET,服务器允许任何客户端访问,NADDR-ANY为本地的任意IP地址,服务器端口设为8000;

       调 用Bind函数,将本地监听端口与服务器通信端口绑定在一起,在服务器端调用Listen函数来监听客户端状态。将监听到的文件描述符Listenfd赋 值给maxfd,maxi赋值为-1,表示客户端没有连接,client[]进行初始化,把allset清空,再把监听到的文件描述符 listenfd 添加到文件描述符集allset中;用for循环把allset里的文件描述符给rset,这时allset里有一个listenfd文件描述符.    调 用select函数,如果客户端有连接请求时,将客户端请求个数返回给nready,若有一个请求时,则nready=1,用if判断nready不小于 0,则不出错,这时rset文件描述符集合中有listenfd文件描述符,执行Accept()函数,相当于调用一个socket,生成新的套接字把新 套接字描述符给connfd,再打印出客户端的IP地址,端口号,用子循环for,从低位开始查找空位,再赋值放connfd文件描述符,就是把新得到的文件描述符connfd赋给数组client[],再把新得到的connfd写入 allset文件描述符集合中,此时allset文件描述符集中有listenfd和connfd两个,用if 把allset中的文件描述符和I 最大值找出来分别赋给mxfd ,maxi.nready==0时,没有更多的文件描述符,结束本次循环,这是就只有一个文件描述符listenfd,因此nready的值为1,--nready时不等0,就不执行continue,进入for 语句把connfd赋给sockfd,这时等于0,结束本次循环.再次执行for循环,监听来自客户端的连接请求. Rset和allset中都有两个文件描述符listenfd,connfd,如果connfd有数据到达,listenfd没有数据到达时,调用select,得到nready 的值 是1,在for 循环中把 connfd给sockfd,如 果客户端和服务器关闭连接,从allset清除sockfd文件描述符,把nready的值自减1,当nready的值为0时,结束本次循环.在此 时,rset ,allset中有 listenfd,connfd,,如果都有连接请求,select返回2,nready为2,if判断出nread的值不小于0, 正确,listendfd在rset 中,执行Accept函数.打印客户端IP地址和端口号,新得到的connfd1保存到client[]

    再写入allset 中,这时有listend,connfd,connfd1三个文件描述符,找出最大值给maxfd,maxi,--nready为1,直接进入for把connfd给sockfd,接收来自客户端的数据,把小写转换成大写返回给客户端.

     

     

     

    执行结果:

    服务器端

    [root@localhost zml]# gcc server.c -o server
    [root@localhost zml]# ./server
    received from 127.0.0.1 at PORT 50121
    received from 127.0.0.1 at PORT 50124


    终端一

    [root@localhost zml]# gcc client.c -o client
    [root@localhost zml]# ./client

    abc

    ABC

    zhengmingli

    ZHENGMINGLI

    qianrushibanji

     





    终端二

    [root@localhost zml]# ./client
    abc
    ABC
    zhengmingli
    ZHENGMINGLI

    qianrushibanji

     

     

    终端三

    [root@localhost zml]# ./client

    qianrushibanji

    QIANRUSHIBANJI

     

     

     

     

     

     

     

     

     

     

     

     

     

     

      

    <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>
    阅读(1205) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    分页公共方法
    关于锁表查询的部分SQL
    将Excel中的数据绑定到DataGrid控件中
    CMMI相关信息
    图片在DataGrid中的用法
    关于standard Button和html Button之disabled属性的比较
    VS2003中链接数据库方法
    .NET多语言版本系统功能实现
    Read and Write 单一文件
    为继承masterpage的页面设置defaultbutton和defaultfocus
  • 原文地址:https://www.cnblogs.com/ztguang/p/12647538.html
Copyright © 2020-2023  润新知