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\n");
        return -1;
    }

    /* 准备接受客户端请求 */
    printf("admin: started server on '%s' at '%d'\n", 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\n");
        return -1;
    }
    linktrgt[bytecnt] = '\0';
    
    /* 获得服务器 IP 地址和端口号 */
    servport = index(linktrgt, ':');
    *servport = '\0';
    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'\n",
            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 \n");
        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\n");
            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'\n",groupsfile);
        return 0;
    }

    /* 从配置文件中读取聊天室的数量 */
    fscanf(fp,"%d",&ngroups);

    group = (Group *)calloc(ngroups,sizeof(Group));
    if (!group)
    {
        fprintf(stderr,"error : unable to calloc\n");
        return 0;
    }

    /* 从配置文件读取聊天室信息 */
    for (grid = 0; grid < ngroups; grid++)
    {
        /* 读取聊天室名和容量 */
        if (fscanf(fp,"%s %d", name, &capa) != 2)
        {
            fprintf(stderr, "error : no info on froup %d\n",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>\n",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'\n",
                            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\n");
                }

                /* 显示客户机的主机名和对应的 socket 描述符 */
                printf("admin : connect from '%s' at '%d'\n",
                        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\n");
        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'\n",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'\n",
            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\n",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\n","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\n",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\n");
        exit(1);
    }

    if (pkt->type != LIST_GROUPS)
    {
        fprintf(stderr,"error: unexpected reply from server\n");
        exit(1);
    }

    /*显示聊天室*/
    showgroups(pkt->lent, pkt->text);

    /*从标准输入读入聊天室名*/
    printf("which group? ");
    fgets(bufr, MAXPKTLENE, stdin);
    bufr[strlen(bufr) - 1] = '\0';

    /*此时可能用户想退出*/
    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] = '\0';

    /*此时可能用户想退出*/
    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\n");
        exit(1);
    }

    if (pkt->type != JOIN_ACCEPTED && pkt->type != JOIN_REJECTED)
    {
        fprintf(stderr, "error : unexpected reply from server\n");
        exit(1);
    }

    /* 如果拒绝显示其原因 */
    if (pkt->type == JOIN_REJECTED)
    {
        printf("admin : %s\n", pkt->text);
        free(gname);
        free(mname);
        return 0;
    }    
    else
    {
        printf("admin: join '%s' as '%s'\n",gname, mname);
        free(gname);
        free(mname);
        return 1;
    }    
}

int main(int argc, char **argv)
{
    int sock;

    /* 用户输入合法性检测 */    
    if (argc != 1)
    {
        fprintf(stderr,"usage : %s\n", 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\n");
                    exit(1);
                }

                /* 显示消息文本 */
                if (pkt->type != USER_TXET)
                {
                    fprintf(stderr,"error: unexpected reply from server\n");
                    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 

 

运行结果:

posted @ 2019-11-15 10:19  王清河  阅读(1926)  评论(0编辑  收藏  举报