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 

 

運行結果:緩存

相關文章
相關標籤/搜索