server-client 服务器-客户端 段彦青

09级计算机应用1班  段彦青 0906041001

/* server.c */
#include
#include                     /*库函数*/
#include                     /*字符串处理*/
#include                 /*INTERNET地址族*/
#include "wrap.h"                    /*包含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];                /*定义client数组*/
    ssize_t n;                            /*定义signed size_t类型变量*/
    fd_set rset, allset;                    /*定义fd_set(文件描述符的集合)型变量*/
    char buf[MAXLINE];                  /*定义缓冲区数组*/
    char str[INET_ADDRSTRLEN];         /*定义字符数组*/
    socklen_t cliaddr_len;                /*定义socklen_t类型变量*/
    struct sockaddr_in    cliaddr, servaddr;    /*定义struct sockaddr_in 类型变量*/

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);  /*分配一个文件描述符*/

    /*初始化servaddr*/
    bzero(&servaddr, sizeof(servaddr));        /*取servaddr的地址,并将结构体清零*/
    servaddr.sin_family = AF_INET;         /*设置地址类型为AF_INET (IPV4)*/
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   /*设置网络地址为INADDR_ANY(本地任意IP地址)*/
    servaddr.sin_port  = htons(SERV_PORT);  /*设置端口号为SERV_PORT(8000)*/

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));   /*绑定要结合的网络地址与端口号,bind()成功返回0,失败返回-1 */

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

    maxfd = listenfd;        /* maxfd初始化 */
    maxi = -1;            /* maxi赋初值-1 */
    for (i = 0; i < FD_SETSIZE; i++)        /* FD_SETSIZE*/
        client[i] = -1;          /*将client[]数组的元素全部赋值为-1,-1代表可进入的 */
    FD_ZERO(&allset);          /*清空allset集合,用于存放soket文件描述符*/
    FD_SET(listenfd, &allset);         /*把监听到的lisenfd放入allset集合中*/

    for ( ; ; ) {
        rset = allset;    /* structure assignment */
    nready = select(maxfd+1, &rset, NULL, NULL, NULL);    /*调用select函数它可以同时监听多个阻塞的文件描述符,哪个有数据到达就处理哪个,返回值大于0,表示有文件可读,就可以用FD_ISSET来遍历所有可能的描述符,以检查是哪个上面有活动发生。如果是监听套接字可读,说明正有一个客户试图建立连接,此时,即可调用accept。没有数据到达时就阻塞。反之,如果是某个客户描述符准备好,则说明该描述符上有一个客户请求需要我们读取和处理。maxfd是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1。*/
        if (nready < 0)
            perr_exit("select error");    /*nready小于0时出错*/

        if (FD_ISSET(listenfd, &rset)) {   /* 检测文件描述符是否在reset中,检测是否有新连接出现 */
            cliaddr_len = sizeof(cliaddr);   /*取客户端的地址长度放入cliaddr_len中*/
            connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);  /*服务器调用accept()接受连接,如果没有客户端的连接请求,就阻塞等待直到有客户端连接上来,返回客户端的地址和端口号,赋值给connfd*/

            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;    /* 保存里连接的客户端的文件描述符到client数组中 */
                    break;
                }
            if (i == FD_SETSIZE) {
                fputs("too many clients ", stderr);   /*如果i== FD_SETSIZE表示连接已满,输出错误信息too many clients */
                exit(1);
            }

            FD_SET(connfd, &allset);    /*添加新的文件描述符到allset */
            if (connfd > maxfd)
                maxfd = connfd;     /* 如果connfd > maxfd ,交换connfd 和 maxfd 的值,使maxfd中存放的是最大的文件描述符*/
            if (i > maxi)
                maxi = i;      /*如果i > maxi,交换两个变量里的值,maxi存放的是值最大的数组下标*/

            if (--nready == 0)
                continue;     /* 当nready == 0时,表示没有可读的文件描述符,结束本次循环,继续下一次循环*/
        }

        for (i = 0; i <= maxi; i++) {    /* check all clients for data */
            if ( (sockfd = client[i]) < 0)
                continue;   /*如果(sockfd = client[i]) < 0,表示没有连接,结束本次循环,继续下次循环*/
            if (FD_ISSET(sockfd, &rset)) {  /*检测文件描述符是否在rset集合中*/
                if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {   /*判断客户端连接是否关闭*/
                    /* connection closed by client */
                    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轮询,当集合里有可读的socket时,select返回。select返回后,判断是否是listenfd可读,如果是,那么就有新的客户端连进来,调用accept函数进行接收,accept()返回时传出客户端的地址和端口号。Accept得到的connfd保存到客户端列表里(client[n]),然后同样加到allset集合里,以便进行下一次轮询。如果不是listenfd可读,那么遍历所有客户端socket,检查是否可读,如果可读,就读取数据,转换成大写然后发送回客户端。如果客户端断开,那么从client列表和集合里去除掉。


运行结果:

服务器端:
[root@localhost dyq]# gcc server.c -o server

[root@localhost dyq]# ./server

received from 127.0.0.1 at PORT 60058

received from 127.0.0.1 at PORT 60059

received from 127.0.0.1 at PORT 47114

客户端1:
[root@localhost dyq]# gcc client.c -o client

[root@localhost dyq]# ./client

wo   

WO

men

MEN

shi

SHI

yi

YI

jia

JIA

客户端2:
[root@localhost dyq]# ./client

cat

CAT

dog

DOG

bird

BIRD

客户端3:
[root@localhost dyq]# ./client

hello world

HELLO WORLD


<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>
阅读(388) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
原文地址:https://www.cnblogs.com/ztguang/p/12647589.html