• C语言实现聊天室软件


    /*
    common.h
    */
    
    /*服务器端口信息*/
    #define PORTLINK ".charport"
    
    /*缓存限制*/
    #define MAXNAMELEN 256
    #define MAXPKTLENE 2048
    
    /*信息类型钉钉*/
    #define LIST_GROUPS 0
    #define JOIN_GROUP    1
    #define LEAVE_GROUP    2
    #define USER_TXET    3
    #define JOIN_REJECTED    4
    #define JOIN_ACCEPTED    5
    
    /*数据包结构*/
    typedef struct _packet
    {
        /*
        数据包类型
        数据报类型长度
        数据报内容
        */
        char type;
        long lent;
        char *text;
    }Packet;
    
    extern int startserver();
    extern int locateserver();
    
    extern Packet *recvpkt(int sd);
    extern int sendpkt(int sd, char typ, long len, char *buf);
    extern void freepkt(Packet *msg);
    /*
    ChatLinker.c
    */
    #include<stdio.h>
    #include<stdlib.h>
    #include<fcntl.h>
    #include<string.h>
    #include<strings.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netdb.h>
    #include<time.h>
    #include<errno.h>
    #include"common.h"
    
    /*
      为服务器接收客户端请求做准备,
      正确返回 socket 文件描述符
      错误返回 -1
    */
    int startserver()
    {
        int     sd;         /* socket 描述符 */
        int     myport;        /* 服务器端口 */
        const char *myname;    /* 本地主机的全称 */
        char    linktrgt[MAXNAMELEN];
        char    linkname[MAXNAMELEN];
    
        /*    调用 socket 函数创建 TCP socket 描述符    */
        sd = socket(PF_INET, SOCK_STREAM, 0);
    
        /* 调用bind函数将一个本地地址指派给 socket  */
        struct sockaddr_in server_address;
        server_address.sin_family = AF_INET;
        
        /* 
        通配地址 INADDR_ANY 表示IP地址为 0.0.0.0,
        内核在套接字被连接后选择一个本地地址
        htonl函数 用于将 INADDR_ANY 转换为网络字节序 
        */
        server_address.sin_addr.s_addr = htonl(INADDR_ANY);
        
        /* 指派为通配端口 0,调用 bind 函数后内核将任意选择一个临时端口 */
        server_address.sin_port = htons(0);
    
        bind(sd, (struct sockaddr *)&server_address, sizeof(server_address));
        
        /* 
        调用listen 将服务器端 socket 描述符 sd 
        设置为被动地监听状态
        并设置接受队列的长度为20 
        */
        listen(sd,20);
    
      /*
        调用 getsockname、gethostname 和 gethostbyname 
        确定本地主机名和服务器端口号
      */
        char hostname[MAXNAMELEN];
    
        if (gethostname(hostname, sizeof(hostname)) != 0)
        {
            perror("gethostname");
        }
        
        struct hostent *h;
        h = gethostbyname(hostname);
    
        int len = sizeof(struct sockaddr);
    
        getsockname(sd, (struct sockaddr *)&server_address, &len);
    
        myname = h->h_name;
        myport = ntohs(server_address.sin_port);
    
        /* 在家目录下创建符号链接'.chatport'指向linktrgt */
        sprintf(linktrgt, "%s:%d", myname, myport);
        /* 在头文件 common.h 中:#define PORTLINK ".chatport" */
        sprintf(linkname ,"%s/%s", getenv("HOME"), PORTLINK);
    
        if (symlink(linktrgt,linkname) != 0)
        {
            fprintf(stderr, "error : server already exists
    ");
            return -1;
        }
    
        /* 准备接受客户端请求 */
        printf("admin: started server on '%s' at '%d'
    ", myname, myport);
        return sd;
    }
    
    /*
      和服务器建立连接,正确返回 socket 描述符,
      失败返回  -1
    */
    int hooktoserver()
    {
        int sd;
    
        char linkname[MAXNAMELEN];
        char linktrgt[MAXNAMELEN];
        char *servhost;
        char *servport;
        int bytecnt;
    
        /* 获取服务器地址 */
        sprintf(linkname, "%s/%s", getenv("HOME"), PORTLINK);
        bytecnt = readlink(linkname, linktrgt, MAXNAMELEN);
    
        if (bytecnt == -1)
        {
            fprintf(stderr,"error : no active char server
    ");
            return -1;
        }
        linktrgt[bytecnt] = '';
        
        /* 获得服务器 IP 地址和端口号 */
        servport = index(linktrgt, ':');
        *servport = '';
        servport++;
        servhost = linktrgt;
    
        /* 获得服务器 IP 地址的 unsigned short 形式 */
        unsigned short number = (unsigned short) strtoul(servport, NULL, 0);
    
        /*
        调用函数 socket 创建 TCP 套接字
        */
        sd = socket(AF_INET, SOCK_STREAM, 0);
    
        /*
        调用 gethostbyname() 和 connect()连接 'servhost' 的 'servport' 端口
        */
        struct hostent *hostinfo;
        struct sockaddr_in address;
    
        hostinfo = gethostbyname(servhost);
        address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;
        address.sin_family = AF_INET;
        address.sin_port = htons(number);
    
        if (connect(sd, (struct sockaddr *) &address, sizeof(address)) < 0)
        {
            perror("connecting");
            exit(1);
        }
    
        printf("admin : connected to server on '%s' at '%s'
    ",
                servhost,servport);
        return sd;
    }
    
    /* 从内核读取一个套接字的信息 */
    int readn(int sd, char *buf, int n)
    {
        int toberead;
        char *ptr;
    
        toberead = n;
        ptr = buf;
        while (toberead > 0)
        {
            int byteread;
    
            byteread = read(sd, ptr, toberead);
            if (byteread <= 0)
            {
                if(-1 == byteread)
                {
                    perror("read");
                }
                return 0;
            }
    
            toberead -= byteread;
            ptr += byteread;
        }
    
        return 1;
    }
    
    /* 接收数据包 */
    Packet *recvpkt(int sd)
    {
        Packet *pkt;
    
        pkt = (Packet *)calloc(1, sizeof(Packet));
        if (!pkt)
        {
            fprintf(stderr, "error : unable to calloc 
    ");
            return NULL;
        }
    
        /* 读取消息类型 */
        if (!readn(sd, (char *)&pkt->type, sizeof(pkt->type)))
        {
            free(pkt);
            return NULL;
        }
        
        /* 读取消息长度 */
        if (!readn(sd, (char *)&pkt->lent, sizeof(pkt->lent)))
        {
            free(pkt);
            return NULL;
        }
    
        pkt->lent = ntohl(pkt->lent);
        
        /* 读取消息文本 */
        if (pkt->lent > 0)
        {
            pkt->text = (char *)malloc(pkt->lent);
            if (!pkt)
            {
                fprintf(stderr,"error : unable to malloc
    ");
                return NULL;
            }
    
            if (!readn(sd, pkt->text, pkt->lent))
            {
                freepkt(pkt);
                return NULL;
            }
        }
    
        return pkt;
    }
    
    /* 发送数据包 */
    int sendpkt(int sd, char typ, long len, char *buf)
    {
        char tmp[8];
        long siz;
    
        /* 把包的类型和长度写入套接字 */
        bcopy(&typ, tmp, sizeof(typ));
        siz = htonl(len);
        bcopy((char *) &siz, tmp + sizeof(typ), sizeof(len));
        write(sd, tmp, sizeof(typ)+sizeof(len));
    
        /* 把消息文本写入套接字 */
        if (len > 0)
        {
            write(sd, buf, len);
        }
    
        return 1;
    }
    
    /* 释放数据包占用的内存空间 */
    void freepkt(Packet *pkt)
    {
        free(pkt->text);
        free(pkt);
    }
    /*
    ChatServer.c
    */
    #include<stdio.h>
    #include<signal.h>
    #include<stdlib.h>
    #include<fcntl.h>
    #include<string.h>
    #include<strings.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netdb.h>
    #include<time.h>
    #include<errno.h>
    #include"common.h"
    
    /* 聊天室成员信息 */
    typedef struct _member
    {
    
        char *name; /*成员姓名*/
        int sock;    /*成员socket描述符*/
        int grid;    /*成员所属聊天室*/
        struct _member *next;    /*下一个成员*/
        struct _member *prev;    /*前一个成员*/
    }Member;
    
    /* 聊天室信息 */
    typedef struct _group
    {
        char *name;    /* 聊天室名字 */
        int capa;    /* 聊天室最大容量(人数) */
        int occu;    /* 当前占有率(人数) */
        struct _member *mems;    /* 记录聊天室内所有成员信息的链表 */
    }Group;    
    
    /* 所有聊天室的信息表 */
    Group *group;
    int ngroups;
    
    /* 通过聊天室名字找到聊天室 ID */
    int findgroup(char *name)
    {
        int grid;
    
        for (grid = 0; grid < ngroups; grid++)
        {
            if (strcmp(group[grid].name, name) == 0)
            {
                return grid;
            }
        }
    
        return -1;
    }
    
    /* 通过室成员名字找到室成员的信息 */
    Member *findmemberbyname(char *name)
    {
        int grid;
    
        for (grid = 0; grid < ngroups; grid++)
        {
            Member *memb;
    
            for (memb = group[grid].mems; memb; memb = memb->next)
            {
                if (0 == strcmp(memb->name, name))
                {
                    return memb;
                }
            }
        }
    
        return NULL;
    }
    
    /* 通过 socket 描述符找到室成员的信息 */
    Member *findmemberbysock(int sock)
    {
        int grid;
    
        for (grid = 0 ; grid < ngroups; grid++)
        {
            Member *memb;
    
            for (memb = group[grid].mems; memb; memb = memb->next)
            {
                if (memb->sock == sock)
                {
                    return memb;
                }
            }
        }
    
        return NULL;
    }
    
    /* 退出前的清理工作 */
    void cleanup()
    {
        char linkname[MAXNAMELEN];
    
        sprintf(linkname,"%s/%s",getenv("HOME"),PORTLINK);
        unlink(linkname);
        exit(0);
    }    
    
    int initgroups(char *groupsfile)
    {
        FILE *fp;    
        char name[MAXNAMELEN];
        int capa;
        int grid;
    
        /* 打开存储聊天室信息的配置文件 */
        fp = fopen(groupsfile,"r");
        if (!fp)
        {
            fprintf(stderr,"error : unable to open file '%s'
    ",groupsfile);
            return 0;
        }
    
        /* 从配置文件中读取聊天室的数量 */
        fscanf(fp,"%d",&ngroups);
    
        group = (Group *)calloc(ngroups,sizeof(Group));
        if (!group)
        {
            fprintf(stderr,"error : unable to calloc
    ");
            return 0;
        }
    
        /* 从配置文件读取聊天室信息 */
        for (grid = 0; grid < ngroups; grid++)
        {
            /* 读取聊天室名和容量 */
            if (fscanf(fp,"%s %d", name, &capa) != 2)
            {
                fprintf(stderr, "error : no info on froup %d
    ",grid + 1);
                return 0;
            }
            /* 将信息存进 group 结构 */
            group[grid].name = strdup(name);
            group[grid].capa = capa;
            group[grid].occu = 0;
            group[grid].mems = NULL;
        }
        return 1;
    }
    
    int main(int argc, char *argv[])
    {
        int servsock;    /* 聊天室服务器端监听 socket 描述符 */
        int maxsd;         /* 连接的客户端 socket 描述符的最大值 */
        fd_set livesdset, tempset;    /* 客户端 sockets 描述符集 */
    
        if (argc != 2)
        {
            fprintf(stderr,"usage : %s <group-file>
    ",argv[0]);
            exit(1);
        }
    
        /* 调用 initgroups 函数,初始化聊天室信息 */
        if (!initgroups(argv[1]))
        {
            exit(1);
        }
    
        /* 设置信号处理函数 */
        signal(SIGTERM, cleanup);
        signal(SIGINT,cleanup);
    
        /* 准备接受请求 */
        
        /* 
        定义在 "chatlinker.c" 文件中
        主要完成创建服务器套接字,绑定端口号,
        并设置把套接字为监听状态 
        */
        servsock = startserver();
        if (-1 == servsock)
        {
            return -1;
        }
    
        maxsd = servsock;
    
        /* 初始化描述符集 */
        FD_ZERO(&livesdset);
        FD_ZERO(&tempset);
        /* 
        打开服务器监听套接字的套接字
        描述符 servsock 对应的fd_set 比特位 
        */
        FD_SET(servsock, &livesdset);
    
        while(1)
        {
            int sock;
            
            /* 特别注意 tempset 作为 select 参数时是一个 "值-结果" 参数,
            select 函数返回时,tempset 中打开的比特位只是读就绪的 socket
            描述符,所以我们每次循环都要将其更新为我们需要内核测试读就绪条件
            的 socket 描述符集合 livesdset */
            tempset = livesdset;
    
            /* 调用 select 函数等待已连接套接字上的包和来自
            新的套接字的链接请求 */
            select(maxsd+1, &tempset, NULL, NULL, NULL);
    
            /* 循环查找来自客户机的请求 */
            for (sock = 3; sock <= maxsd; sock++)
            {
                /* 如果是服务器监听 socket,则跳出接收数据包环节,执行接受连接 */
                if (sock == servsock)
                {
                    continue;
                }
                
                /* 有来自客户 socket 的消息 */
                if (FD_ISSET(sock, &tempset))
                {
                    Packet *pkt;
                    pkt = recvpkt(sock);
                    if (!pkt)
                    {
                        /* 客户机断开了连接 */
                        char *clientname;
                        
                        /* 使用 gethostbyaddr,getpeername 函数得到 client 的主机名 */
                        socklen_t len;
                        struct sockaddr_in addr;
                        len = sizeof(addr);
                        if (getpeername(sock, (struct sockaddr *)&addr, &len) == 0)
                        {
                            struct sockaddr_in *s = (struct sockaddr_in *)&addr;
                            struct hostent *he;
                            he = gethostbyaddr(&s->sin_addr, sizeof(struct in_addr),
                                    AF_INET);
                            clientname = he->h_name;
                        }
                        else
                        {
                            printf("Cannot get peer name");
                        }
    
                        printf("admin : disconnect from '%s' at '%d'
    ",
                                clientname, sock);
    
                        leavegroup(sock);
                        close(sock);
                        FD_CLR(sock, &livesdset);
                    }
                    else
                    {
                        char *gname, *mname;
                        
                        /* 基于消息类型采取行动 */
                        switch (pkt->type)
                        {
                            case LIST_GROUPS:
                                listgroups(sock);
                                break;
                            case JOIN_GROUP:
                                gname = pkt->text;
                                mname = gname + strlen(gname) + 1;
                                joingroup(sock, gname, mname);
                                break;
                            case LEAVE_GROUP:
                                leavegroup(sock);
                                break;
                            case USER_TXET:
                                relaymsg(sock, pkt->text);
                                break;
                        }
    
                        freepkt(pkt);
                    }
                }
            }    
    
            struct sockaddr_in remoteaddr;    /* 客户机地址结构 */
            socklen_t addrlen;
    
            /* 有来自新的客户机的连接请求请求 */
            if (FD_ISSET(servsock, &tempset))
            {
                /* 已连接的 socket 描述符 */
                int csd;
    
                /* 接受一个新的连接请求 */
                addrlen = sizeof(remoteaddr);
                csd = accept(servsock, (struct sockaddr *)&remoteaddr, &addrlen);
    
                if (-1 != csd)
                {
                    char *clientname;
                    struct hostent *h;
                    /* 使用 gethostbyaddr 函数得到 client 的主机名 */
                    h = gethostbyaddr((char *)&remoteaddr.sin_addr.s_addr,
                            sizeof(struct in_addr), AF_INET);
    
                    if (h != (struct hostent *)0)
                    {
                        clientname = h->h_name;
                    }
                    else
                    {
                        printf("gethostbyaddr failed
    ");
                    }
    
                    /* 显示客户机的主机名和对应的 socket 描述符 */
                    printf("admin : connect from '%s' at '%d'
    ",
                            clientname, csd);
    
                    /* 将该连接的套接字描述符 csd 加入livesdset */
                    FD_SET(csd, &livesdset);
        
                    /* 保持 maxsd 记录的是最大的套接字描述符 */
                    if (csd > maxsd)
                    {
                        maxsd = csd;
                    }
                }
                else
                {
                    perror("accept");
                    exit(0);
                }
            }
        }
    }
    
    /* 把所有聊天室的信息发给客户端 */
    int listgroups(int sock)
    {
        int grid;
        char pktbufr[MAXPKTLENE];
        char *bufrptr;
        long bufrlen;
    
        /* 每一块信息在字符串中用 NULL 分割 */
        bufrptr = pktbufr;
        for (grid = 0; grid < ngroups; grid++)
        {
            /* 获取聊天室名字 */
            sprintf(bufrptr,"%s", group[grid].name);
            bufrptr += strlen(bufrptr) + 1;
    
            /* 获取聊天室容量 */
            sprintf(bufrptr, "%d", group[grid].capa);
            bufrptr += strlen(bufrptr) + 1;
    
            /* 获取聊天室占有率 */
            sprintf(bufrptr, "%d",group[grid].occu);
            bufrptr += strlen(bufrptr) + 1;
        }
    
        bufrlen = bufrptr - pktbufr;
    
        /* 发送消息给回复客户机的请求 */    
        sendpkt(sock, LIST_GROUPS, bufrlen, pktbufr);
        return 1;
    }
    
    /* 加入聊天室 */
    int joingroup(int sock, char *gname, char *mname)
    {
        int grid;
        Member *memb;
    
        /* 根据聊天室名获得聊天室 ID */
        grid = findgroup(gname);
        if (grid == -1)
        {
            char *errmsg = "no such group";
            sendpkt(sock, JOIN_REJECTED, strlen(errmsg), errmsg);
            return 0;
        }
    
        /* 检查是否聊天室成员名字已被占用 */
        memb = findmemberbyname(mname);
    
        /* 如果聊天室成员名已存在,则返回错误消息 */
        if (memb)
        {
            char *errmsg = "member name already exists";
            sendpkt(sock, JOIN_REJECTED, strlen(errmsg), errmsg);
            return 0;
        }
        
        /* 检查聊天室是否已满 */
        if (group[grid].capa == group[grid].occu)
        {
            char *errmsg = "room is full";
            sendpkt(sock, JOIN_REJECTED, strlen(errmsg), errmsg);
            return 0;
        }
    
        memb = (Member *)calloc(1, sizeof(Member));
        if (!memb)
        {
            fprintf(stderr, "error : unable to calloc
    ");
            cleanup();
        }
    
        memb->name = strdup(mname);
        memb->sock = sock;
        memb->grid = grid;
        memb->prev = NULL;
        memb->next = group[grid].mems;
        if (group[grid].mems)
        {
            group[grid].mems->prev = memb;
        }
    
        group[grid].mems = memb;
        printf("admin: '%s' join '%s'
    ",mname, gname);
        
        /* 更新聊天室的在线人数 */
        group[grid].occu++;
    
        sendpkt(sock, JOIN_ACCEPTED, 0, NULL);
    
        return 1;
    }
    
    /* 离开聊天室 */
    int leavegroup(int sock)
    {
        Member *memb;
    
        /* 得到聊天室成员信息 */
        memb = findmemberbysock(sock);
        if (!memb)
        {
            return 0;
        }
    
        /* 从聊天室信息结构中删除 memb 成员 */
        if (memb->next)
        {
            memb->next->prev = memb->prev;    /* 在聊天室成员链表的尾部 */
        }
    
        if (group[memb->grid].mems == memb)
        {
            group[memb->grid].mems = memb->next;    /* 在聊天室成员链表的头部 */
        }
        else
        {
            memb->prev->next = memb->next;    /* 在聊天室成员链表的中部 */
        }
    
        printf("admin: '%s' left '%s'
    ",
                memb->name, group[memb->grid].name);
    
        /* 更新聊天室的占有率 */
        group[memb->grid].occu--;
    
        free(memb->name);
        free(memb);
        return 1;
    }
    
    /* 把成员的消息发送给其他聊天室成员 */
    int relaymsg(int sock, char *text)
    {
        Member *memb;
        Member *sender;
        char pktbufr[MAXPKTLENE];
        char *bufrptr;
        long bufrlen;
    
        /* 根据 socket 描述符获得该聊天室成员的信息 */
        sender = findmemberbysock(sock);
        if (!sender)
        {
            fprintf(stderr,"strange : no member at %d
    ",sock);
            return 0;
        }
        
        /* 把发送者的姓名添加到消息文本前边 */
        bufrptr = pktbufr;
        strcpy(bufrptr, sender->name);
        bufrptr += strlen(bufrptr) + 1;
        strcpy(bufrptr, text);
        bufrptr += strlen(bufrptr) + 1;
        bufrlen = bufrptr - pktbufr;
    
        for (memb = group[sender->grid].mems; memb; memb = memb->next)
        {
            if (memb->sock == sock)
            {
                continue;
            }
    /* 给聊天室其他成员发送消息(TCP是全双工的) */
            sendpkt(memb->sock, USER_TXET, bufrlen, pktbufr);
        }
    
        printf("%s : %s", sender->name, text);
        return 1;
    
    
    }
    /*
    ChatClient.c
    */
    #include<stdio.h>
    #include<stdlib.h>
    #include<fcntl.h>
    #include<string.h>
    #include<strings.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<netdb.h>
    #include<time.h>
    #include<errno.h>
    #include"common.h"
    
    #define QUIT_STRING "/end"
    
    /*打印聊天室名单*/
    void showgroups(long lent, char *text)
    {
        char *tptr;
    
        tptr = text;
        printf("%15s %15s %15s
    ","group","capacity","occupancy");
        while (tptr < text + lent)
        {
            char *name, *capa, *occu;
    
            name = tptr;
            tptr = name + strlen(name) + 1;
            capa = tptr;
            tptr = capa + strlen(capa) + 1;
            occu = tptr;
            tptr = occu + strlen(occu) + 1;
    
            printf("%15s %15s %15s
    ",name, capa, occu);
        }        
    }
    
    /*加入聊天室*/
    int joingroup(int sock)
    {
        Packet *pkt;
        char bufr[MAXPKTLENE];
        char *bufrptr;
        int bufrlen;
        char *gname;
        char *mname;
    
        /*请求聊天信息*/
        sendpkt(sock, LIST_GROUPS, 0, NULL);
    
        /*接收聊天室信息回复*/
        pkt = recvpkt(sock);
        if (!pkt)
        {
            fprintf(stderr,"error: server died
    ");
            exit(1);
        }
    
        if (pkt->type != LIST_GROUPS)
        {
            fprintf(stderr,"error: unexpected reply from server
    ");
            exit(1);
        }
    
        /*显示聊天室*/
        showgroups(pkt->lent, pkt->text);
    
        /*从标准输入读入聊天室名*/
        printf("which group? ");
        fgets(bufr, MAXPKTLENE, stdin);
        bufr[strlen(bufr) - 1] = '';
    
        /*此时可能用户想退出*/
        if (strcmp(bufr, "") == 0 || 
                strncmp(bufr, QUIT_STRING, strlen(QUIT_STRING)) == 0)
        {
            close(sock);
            exit(0);
        }
        gname = strdup(bufr);
        
        /*读入成员名字*/
        printf("what nickname? ");
        fgets(bufr, MAXPKTLENE, stdin);
        bufr[strlen(bufr) - 1] = '';
    
        /*此时可能用户想退出*/
        if (strcmp(bufr, "") == 0 || 
                strncmp(bufr, QUIT_STRING, strlen(QUIT_STRING)) == 0)
        {
            close(sock);
            exit(0);
        }
        mname = strdup(bufr);
    
        /*发送加入聊天室信息*/
        bufrptr = bufr;
        strcpy(bufrptr, gname);
        bufrptr += strlen(bufrptr) + 1;
        strcpy(bufrptr, mname);
        bufrptr += strlen(bufrptr) + 1;
        bufrlen = bufrptr - bufr;
        sendpkt(sock, JOIN_GROUP, bufrlen, bufr);
    
        /* 读取来自服务器的回复 */
        pkt = recvpkt(sock);
        if (!pkt)
        {
            fprintf(stderr, "error : server died
    ");
            exit(1);
        }
    
        if (pkt->type != JOIN_ACCEPTED && pkt->type != JOIN_REJECTED)
        {
            fprintf(stderr, "error : unexpected reply from server
    ");
            exit(1);
        }
    
        /* 如果拒绝显示其原因 */
        if (pkt->type == JOIN_REJECTED)
        {
            printf("admin : %s
    ", pkt->text);
            free(gname);
            free(mname);
            return 0;
        }    
        else
        {
            printf("admin: join '%s' as '%s'
    ",gname, mname);
            free(gname);
            free(mname);
            return 1;
        }    
    }
    
    int main(int argc, char **argv)
    {
        int sock;
    
        /* 用户输入合法性检测 */    
        if (argc != 1)
        {
            fprintf(stderr,"usage : %s
    ", argv[0]);
            exit(1);
        }    
    
        /* 与服务器连接 */
        sock = hooktoserver();
        if (sock == -1)
        {
            exit(1);
        }
    
        /* 清除标准输出缓冲区 */
        fflush(stdout);
    
        /* 初始化描述符集 */
        fd_set clientfds, tempfds;
        FD_ZERO(&clientfds);
        FD_ZERO(&tempfds);
        FD_SET(sock, &clientfds);  /* 设置服务器套接字在clientfds中的比特位 */
        FD_SET(0, &clientfds);        /* 设置标准输入在 clientfds 中的比特位 */
    
        while(1)
        {
            /* 加入聊天室 */
            if (!joingroup(sock))
            {
                continue;
            }
    
            while(1)
            {
                /* 调用 select 函数同时监测键盘和服务器信息 */
                tempfds = clientfds;
    
                if (select(FD_SETSIZE, &tempfds, NULL, NULL, NULL) == -1)
                {
                    perror("select");
                    exit(4);
                }
    
                /* 对于所有在 tempfds 中被置位的文件描述符,检测它是否是套接字描述符,
                如果是,意味服务器传来消息。如果它文件描述符是 0,则意味有来自用户
                键盘的输入要发送给服务器 */
                
                /* 处理服务器传来信息 */
                if(FD_ISSET(sock, &tempfds))
                {
                    Packet *pkt;
                    pkt = recvpkt(sock);
                    if (!pkt)
                    {
                        fprintf(stderr, "error: server died
    ");
                        exit(1);
                    }
    
                    /* 显示消息文本 */
                    if (pkt->type != USER_TXET)
                    {
                        fprintf(stderr,"error: unexpected reply from server
    ");
                        exit(1);
                    }
    
                    printf("%s: %s", pkt->text, pkt->text + strlen(pkt->text) + 1);
                    freepkt(pkt);
                }
    
                if (FD_ISSET(0, &tempfds))
                {
                    char bufr[MAXPKTLENE];
                    fgets(bufr, MAXPKTLENE, stdin);
                    
                    /* 处理键盘输入 */
                    if (strncmp(bufr, QUIT_STRING, strlen(QUIT_STRING)) == 0)
                    {
                        sendpkt(sock, LEAVE_GROUP, 0, NULL);
                        break;
                    }
    
                    sendpkt(sock, USER_TXET, strlen(bufr) + 1, bufr);
                }
            }
        }    
    }
    /*
    groups
    */
    4
    ind       2
    uk        2
    us        2
    aus       3
    /*
    makefile
    */
    .c.o:
        gcc -g -c $?
    
    # compile client and server
    all: chatclient chatserver
    
    # compile client only
    chatclient: chatclient.o chatlinker.o
        gcc -g -o chatclient chatclient.o  chatlinker.o 
    
    # compile server program
    chatserver: chatserver.o chatlinker.o
        gcc -g -o chatserver chatserver.o  chatlinker.o 
    
    .PHONY:clean
    
    clean:
        rm chatlinker.o chatclient.o chatserver.o chatclient chatserver 

    运行结果:

  • 相关阅读:
    求js数组中最小值
    分析apply,call方法
    前端模块化详解
    js中形参的小练习
    js中return返回值小练习
    mysql 视图
    mysql 数据库语句
    mysql 事务管理
    vue-前端工程化
    Vue-router
  • 原文地址:https://www.cnblogs.com/wanghao-boke/p/11864713.html
Copyright © 2020-2023  润新知