zoukankan      html  css  js  c++  java
  • server-client 服务器-客户端 闫俊霖

    09级计算机应用1班 闫俊霖

    前言:在局域网中启动一台服务器,使每一个客户端通过服务器端获得其他客户端输入的数据。
    ------------------------------------ sever.c ------------------------------------
    /*------------------------------------------------------------------------------------
    注释: 服务器端负责接受客户端的数据请求,把对一个客户端的数据转发给每个与服务器建立链接的客户端。
    下面的指令是生成可执行文件:
    [root@localhost zy]# gcc server.c -o ser
    -------------------------------------------------------------------------------------*/
    /* server.c */
    #include
    #include
    #include
    #include
    #include "wrap.h"

    #define MAXLINE 80
    #define SERV_PORT 8000

    int main(int argc, char **argv)
    {
    int i,j, 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);/*打开一个网络通讯端口返回一个套接字描述符赋值给listenfd*/

    bzero(&servaddr, sizeof(servaddr));/*将结构体清零*/
    servaddr.sin_family = AF_INET;/*设置地址类型为AF_INET*/
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);/*设置网络地址为INADDR_ANY,这个宏表示本地的任意ip地址*/
    servaddr.sin_port = htons(SERV_PORT);/*设置端口号为SERV_PORT,我们定义为8000*/

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));/*将所监听的端口套接字描述符与服务器的地址绑定*/

    Listen(listenfd, 20);/*声明服务器处于监听状态,并且最多允许有20个客户端处于连接待状态*/

    maxfd = listenfd; /* initialize */
    maxi = -1; /* index into client[] array */
    for (i = 0; i < FD_SETSIZE; i++)
    client[i] = -1; /* -1 indicates available entry */
    FD_ZERO(&allset);/*将allset套接字描述符集清空*/
    FD_SET(listenfd, &allset);/*将所监听的套接字描述符添加到allset*/

    for ( ; ; ) { /*用于循环接受数据请求,要与服务器交互的client的端口*/
    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);/*把cliaddr结构体的长度赋给cliaddr_len,作为缓冲区的长度*/
    connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);/*接受客户端的连接请求,与客户端建立连接*/

    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++)/*将文件描述符connfd的值存入client[FD_SETSIZE]中最小未被使用的位置*/
    if (client[i] < 0) {
    client[i] = connfd; /* save descriptor */
    break;
    }
    if (i == FD_SETSIZE) { /*若i等于FD_SETSIZE,则表示有数据请求的客户端已达到FD_SETSIZE最大值*/
    fputs("too many clients ", stderr);
    exit(1);
    }

    FD_SET(connfd, &allset); /* 向allset中添加与服务器建立连接并有数据请求的客户端端口 */
    if (connfd > maxfd)/*若此时建立并有数据请求的客户端已大于原来的套接字描述符最大值,则将connfd赋值给maxfd*/
    maxfd = connfd; /* for select */
    if (i > maxi)/*把maxi赋值为当前最大的放置建立连接并有数据请求客户端描述符的索引,以在下面的for循环语句中作为处理客户端请求个数的上限;*/
    maxi = i; /* max index in client[] array */

    if (--nready == 0)/*若--nready为0,则表示当前的套接字描述符集中只有listenfd这个监听的描述符,没有客户端的数据请求端口,进行下一轮的select循环;*/
    continue; /* no more readable descriptors */
    }

    for (i = 0; i <= maxi; i++) { /* check all clients for data */
    if ( (sockfd = client[i]) < 0)/*将client[i]中存放的客户端的套接字描述符赋给sockfd,若小于0,则表示client[i]中没有套接字描述符。*/
    continue;
    if (FD_ISSET(sockfd, &rset)) { /*判断sockfd是否在rset这个描述符集中*/
    if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {/*读入客户端的数据,若n等于0表示客户端已经关闭了连接*/
    /* connection closed by client */
    Close(sockfd);/*服务器关闭与客户端相应的连接*/
    FD_CLR(sockfd, &allset);/*从fd_set结构:allset中删掉清空与客户端连接的套接字描述符*/
    client[i] = -1;/*将存放该客户端套接字的数组位置设为-1,存放新的客户端连接描述符*/
    } else {

    for ( j=0; j <= maxi; j++) { /*依次读取客户端输入的数据*/
    if (j != i) { /*将数据发给请求服务以外客户端*/
    write( client[j], buf, n);
    }
    }
    }

    if (--nready == 0)/*再次判断nready,是否进行下一轮的select循环;*/
    break; /* no more readable descriptors */
    }
    }
    }
    }

    /*------------------------------------------------------------------------------------
    执行过程如下:
    [root@localhost zy]# ./ser
    received from 127.0.0.1 at PORT 52042
    received from 127.0.0.1 at PORT 52043

    -------------------------------------------------------------------------------------*/
    ------------------------------------ client.c ------------------------------------
    /*-------------------------------------------------------------------------------------------
    注释: 客户端连接服务器的程序,向服务器发信息。
    下面的指令是生成可执行文件:
    [root@localhost zy]# gcc client.c -o cli
    --------------------------------------------------------------------------------------------*/
    /* client.c */
    #include
    #include
    #include
    #include
    #include "wrap.h"
    #include

    #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全部清0 */
    servaddr.sin_family = AF_INET; /* 设置地址类型为AF_INE*/
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);/* 由服务器端IP 转化为网络字节序保存在servaddr.sin_addr*/
    servaddr.sin_port = htons(SERV_PORT);/* 由主机端口号 转化为网络字节序赋值给servaddr.sin_port */
    Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));/*户端需要调用connect()连接服务器*/

    pid_t pid; /*定义pid_t类型的pid*/
    pid=fork();/*调用fork()生成一个子进程,返回值可能是父进程, 也可能子进程*/
    if (pid < 0) { /*返回发生错误*/
    perror("fork"); /*输出错误信息*/
    exit(1);
    }else if(pid==0){ /*pid == 0是子进程*/
    while(1){
    n=Read(sockfd, buf, MAXLINE);/*从服务器sockfd读信息到buf*/

    if (n > 0)
    Write(STDOUT_FILENO, buf, n); /*n>0,正确读取了信息到buf里*/

    }
    }else{ /*pid >0是父进程*/
    while (fgets(buf, MAXLINE, stdin) != NULL)/*调用fgets从标准输入获取信息到buf里*/
    Write(sockfd, buf, strlen(buf));/*把buf里的信息写到sockfd对应的网络上*/

    }
    Close(sockfd);/*关闭连接*/

    return 0;
    }

    /*------------------------------------------------------------------------------------
    执行过程如下:
    客户端1
    [root@localhost zy]# ./cli
    asdffgfg
    qweert
    [root@localhost zy]#
    客户端2
    [root@localhost zy]# ./cli
    asdffgfg
    qweert

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

    <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>
    阅读(232) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    数组删除元素注意事项
    点击下拉菜单以外的区域,关闭弹窗
    webpack学习笔记(六)优化
    webpack学习笔记(五)
    webpack学习笔记(四)
    webpack学习笔记(三)
    webpack学习笔记(二)
    es6 笔记
    vue学习笔记——组件的优化
    vue学习笔记——路由
  • 原文地址:https://www.cnblogs.com/ztguang/p/12647569.html
Copyright © 2011-2022 走看看