zoukankan      html  css  js  c++  java
  • ser and client.c 王晓峰

                                 班级:09计应一班        姓名:王晓峰  

    /*前言:在局域网中启动一台服务器,通过服务器让每一个客户端都能知道其他的客户端从服务器端接受到的信息;
    这里是在本机上模拟的,打开一个终端窗口作为服务器,打开其他的终端窗口作为客户端。

    ———————————————————————————————————— service.c ——————————————————————————————————————

    ------------------------------------------------------------------------------------
     注释:服务器端,负责接受客户端的数据请求,把对一个客户端的数据处理结果返回给每个与服务器建立链接的客户端。
    -------------------------------------------------------------------------------------*/

    /* server.c */
    #include                                  
    #include                                 
    #include                                  
    #include                             
    #include "wrap.h"                             
    // 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);     

        bzero(&servaddr, sizeof(servaddr));             /* 清空servaddr描述符表 */
        servaddr.sin_family      = AF_INET;             /* 设置地址类型为AF_INE*/
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   /* 将主机IP转化为网络字节序赋值给servaddr.sin_addr.s_addr */
        servaddr.sin_port        = htons(SERV_PORT);    /* 将主机端口号转化为网络字节序赋值给servaddr.sin_port */

        Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));/* 将listenfd与服务器的ip地址和端口号绑定;*/
        Listen(listenfd, 20);                            /* listen()声明listenfd处于监听状态,并且最多允许有20个客户端处于连接待状态 */

        maxfd = listenfd;                                   
        maxi = -1;                                          
        for (i = 0; i < FD_SETSIZE; i++)                 
            client[i] = -1;                                /*将队列的内容清空,即每个位置都可用*/
        FD_ZERO(&allset);                                /* 将描述符表清空*/
        FD_SET(listenfd, &allset);                       /*将listenfd放到allset描述符表里*/

        for ( ; ; ) {                                  
            rset = allset;                                /* 把 描述符集allset里的描述符赋值给rset*/
            nready = select(maxfd+1, &rset, NULL, NULL, NULL); /* 调用select还回rset有效的文件描述符的个数给nready*/
            if (nready < 0)                               
                perr_exit("select error");               

            if (FD_ISSET(listenfd, &rset)) {              /*确认listenfd是否在在 rset描述表里面*/
                cliaddr_len = sizeof(cliaddr);             
                connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);/* 调用accept()接受连接,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[i]为空,就将 connfd保存在client[i]中*/
                        break;                               
                    }
                if (i == FD_SETSIZE) {                     /* 如果i == FD_SETSIZE表示client[i]已没有可用的位置*/
                    fputs("too many clients ", stderr);    
                    exit(1);                               /* 输出错误信息并退出程序*/
                }

                FD_SET(connfd, &allset);                   
                if (connfd > maxfd)                       
                    maxfd = connfd;                         /* 把connfd赋值给 maxfd 保证已有套节子描述符中 maxfd 是最大的,在select 中要用到maxfd*/
                if (i > maxi)                             
                    maxi = i;                                /*maxi表示已用的 client[i]的最大索引*/

                if (--nready == 0)                        /*nready自减1后如果等于零 表示没有接受到有效数据请求;
                    continue;                                /* no more readable descriptors */
            }

            for (i = 0; i <= maxi; i++) {                    /* 遍历client[i]里的保存的所有套节子描述符*/
                if ( (sockfd = client[i]) < 0)            
                    continue;                               
                if (FD_ISSET(sockfd, &rset)) {             /* 判断sockfd是否在rset描述表里 在就执行下面的语句 */
                    if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {    
                    
                        Close(sockfd);                       /*如果客户端关闭了连接,就关闭sockfd*/
                        FD_CLR(sockfd, &allset);             /*将sockfd从allset描述表里清除*/
                        client[i] = -1;                      /*清空client[i]里存放的描述符*/
                    } else {
                        int j;
                        for (j = 0; j < n; j++)
                            buf[j] = toupper(buf[j]);            /*处理客户端的数据请求,将其转换为大写;*/                  
                        for (i = 0; i <= maxi; i++)          /* check all clients for data */
                        Write(client[i], buf, n);            /* 将处理结果遍历送给每一个与服务器建立链接的客户端*/
                    }

                    if (--nready == 0)                      
                        break;                                /* no more readable descriptors */
                }
            }
        }
    }

    /*------------------------------------------------------------------------------------
       执行结果如下:

    [root@localhost wxf]# gcc server.c -o server
    [root@localhost wxf]# ./server
    received from 127.0.0.1 at PORT 54791
    received from 127.0.0.1 at PORT 54792


    -------------------------------------------------------------------------------------


    ------------------------------------ client.c ------------------------------------

    -------------------------------------------------------------------------------------------
    客户端程序,功能:向服务器端发送数据请求,并fork一个子进程负责接受网络套接字里面的内容,并显示到标准输出;
    --------------------------------------------------------------------------------------------*/


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

    #define MAXLINE 80                                 
    #define SERV_PORT 8000                             

    int main(int argc, char *argv[])                   
    {
        struct sockaddr_in servaddr;                    
        char buf[MAXLINE];                             
        int sockfd, n;                                
        
        sockfd = Socket(AF_INET, SOCK_STREAM, 0);       /* 调用Socket函数还回套节子描述符sockfd*/

        bzero(&servaddr, sizeof(servaddr));             /* 将servaddr结构体清空*/
        servaddr.sin_family = AF_INET;                  
        inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);   
        servaddr.sin_port = htons(SERV_PORT);           
        
        Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));/*调用connect()连接服务器*/
    while(1)                                           
    {    pid_t pid;                                      
                                     
        int n;                                          
        pid = fork();                                   /*调用fork()生成子进程*/
        if (pid < 0) {                                  
            perror("fork failed");                       //fork出错打印错误信息,退出程序;
            exit(1);                                     
        }
        if (pid == 0) {                                 /*如果pid == 0,则执行子进程;*/
                n = Read(sockfd, buf, MAXLINE);           /*从sockfd套接字描述符里读取内容到本地buf中;*/
            if (n == 0)                                  
                printf("the other side has been closed. "); /*如果n==0,表示套接字里内容为空,服务器已闭连接;*/
            else                                         
                Write(STDOUT_FILENO, buf, n);             /*否则就把从网络套接字里读到的内容写到终端显示频!*/
        } else                                          
         {fgets(buf, MAXLINE, stdin);                  //pid>0,执行父进程,调用fgets从键盘输入保存到本地buf中
            Write(sockfd, buf, strlen(buf));             //输入的内容写到网络套接字中,向服务器端发送数据请求;
        
           }
        
    }

        Close(sockfd);                                  
        return 0;                                       
    }

    /*------------------------------------------------------------------------------------
    执行结果如下
    客户端.1
    [root@localhost wxf]# ./client
    xiao
    XIAO
    hao
    HAO
    hao
    HAO
    客户端2
    对应另一个客户端的输出结果:是由fork出的子进程负责完成的
    [root@localhost Desktop]# ./wxf/client
    XIAO
    HAO
    HAO


    另一种设置非阻塞的方式未能实现:
    -------------------------设置非阻塞的方式的错误情况分析---------------------------------------------
     /*client.c*/
    #include                                  
    #include                                 
    #include                                 
    #include                             
    #include "wrap.h"                                  

    #define MAXLINE 80                                 
    #define SERV_PORT 8000                             

    int main(int argc, char *argv[])                   
    {
        struct sockaddr_in servaddr;                    
        char buf[MAXLINE];                             
        int sockfd, n;                                
        
        sockfd = Socket(AF_INET, SOCK_STREAM, 0);       //调用Socket函数还回套节子描述符sockfd

        bzero(&servaddr, sizeof(servaddr));             // 将servaddr结构体清空
        servaddr.sin_family = AF_INET;                  
        inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);   
        servaddr.sin_port = htons(SERV_PORT);           
        
        Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//调用connect()连接服务器
    //--------通过fcntl对sockfd的非阻塞设置;能够实现非阻塞read;但read总是返回负值,即调用出错!错误原因未知?????------
            int flags = fcntl(sockfd, F_GETFL, 0);
            fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);//将sockfd设置成非阻塞;

            i = read(sockfd, buf, 10);//read系统调用确实不再阻塞,但是调用总是出错,退出程序,不知为何?;

            if (i > 0)
                printf("接收消息成功:'%s',共%d个字节的数据 ", buf, MAXLINE);
            else {
                if (i == 0)
                    while (fgets(buf, MAXLINE, stdin) != NULL) {
                        Write(sockfd, buf, strlen(buf));
                        n = Read(sockfd, buf, MAXLINE);
                        if (n == 0)
                            printf("the other side has been closed. ");
                        else

                            Write(STDOUT_FILENO, buf, n);

                    }

                else
                    printf("对方退出了,聊天终止! ");//总是执行这条语句,即read返回值总是小于0;
                break;
            }
        }
        Close(sockfd);
        return 0;

    }

    ------------------------------------wrap.h ------------------------------------

    -------------------------------------------------------------------------------------------
    头文件wrap.h是对read,write等系统调用的错误包装函数;和上述两个文件应放于同一个文件夹下;

    --------------------------------------------------------------------------------------------*/
    #include
    #include
    #include

    void perr_exit(const char *s)
    {
        perror(s);
        exit(1);
    }

    int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
    {
        int n;

    again:
        if ( (n = accept(fd, sa, salenptr)) < 0) {
            if ((errno == ECONNABORTED) || (errno == EINTR))
                goto again;
            else
                perr_exit("accept error");
        }
        return n;
    }

    void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
    {
        if (bind(fd, sa, salen) < 0)
            perr_exit("bind error");
    }

    void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
    {
        if (connect(fd, sa, salen) < 0)
            perr_exit("connect error");
    }

    void Listen(int fd, int backlog)
    {
        if (listen(fd, backlog) < 0)
            perr_exit("listen error");
    }

    int Socket(int family, int type, int protocol)
    {
        int n;

        if ( (n = socket(family, type, protocol)) < 0)
            perr_exit("socket error");
        return n;
    }

    ssize_t Read(int fd, void *ptr, size_t nbytes)
    {
        ssize_t n;

    again:
        if ( (n = read(fd, ptr, nbytes)) == -1) {
            if (errno == EINTR)
                goto again;
            else
                return -1;
        }
        return n;
    }

    ssize_t Write(int fd, const void *ptr, size_t nbytes)
    {
        ssize_t n;

    again:
        if ( (n = write(fd, ptr, nbytes)) == -1) {
            if (errno == EINTR)
                goto again;
            else
                return -1;
        }
        return n;
    }

    void Close(int fd)
    {
        if (close(fd) == -1)
            perr_exit("close error");
    }
    ssize_t Readn(int fd, void *vptr, size_t n)
    {
        size_t  nleft;
        ssize_t nread;
        char   *ptr;

        ptr = vptr;
        nleft = n;
        while (nleft > 0) {
            if ( (nread = read(fd, ptr, nleft)) < 0) {
                if (errno == EINTR)
                    nread = 0;
                else
                    return -1;
            } else if (nread == 0)
                break;

            nleft -= nread;
            ptr += nread;
        }
        return n - nleft;
    }

    ssize_t Writen(int fd, const void *vptr, size_t n)
    {
        size_t nleft;
        ssize_t nwritten;
        const char *ptr;

        ptr = vptr;
        nleft = n;
        while (nleft > 0) {
            if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
                if (nwritten < 0 && errno == EINTR)
                    nwritten = 0;
                else
                    return -1;
            }

            nleft -= nwritten;
            ptr += nwritten;
        }
        return n;
    }
    static ssize_t my_read(int fd, char *ptr)
    {
        static int read_cnt;
        static char *read_ptr;
        static char read_buf[100];

        if (read_cnt <= 0) {
        again:
            if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
                if (errno == EINTR)
                    goto again;
                return -1;
            } else if (read_cnt == 0)
                return 0;
            read_ptr = read_buf;
        }
        read_cnt--;
        *ptr = *read_ptr++;
        return 1;
    }

    ssize_t Readline(int fd, void *vptr, size_t maxlen)
    {
        ssize_t n, rc;
        char    c, *ptr;

        ptr = vptr;
        for (n = 1; n < maxlen; n++) {
            if ( (rc = my_read(fd, &c)) == 1) {
                *ptr++ = c;
                if (c  == ' ')
                    break;
            } else if (rc == 0) {
                *ptr = 0;
                return n - 1;
            } else
                return -1;
        }
        *ptr  = 0;
        return n;
    }

    /*-------------------------------------------------------------------------------------------


    --------------------------------------------------------------------------------------------*/

    <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>
    阅读(194) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    Mysql主从同步延迟问题及解决方案
    elasticsearch 查询过程
    RPC(Remote Procedure Call):远程过程调用
    windows
    设计模式
    Linux Safe
    AS
    开机启动
    springboot打包部署
    【Linux】Linux 常用命令汇总
  • 原文地址:https://www.cnblogs.com/ztguang/p/12647600.html
Copyright © 2011-2022 走看看