本文章介绍一种基于Linux使用C语言实现简单的局域网聊天室程序的方法,支持消息群发,历史数据查询,好友列表查看,好友上线下线提醒等功能。聊天界面如下图所示:

下面将按步骤介绍该系统的设计实现,首先在linux下创建一个目录,在目录下按顺序创建几个文件,clientlist.c common.h sock.c main.c Makefile,如下图所示,创建完各文件之后,依次在文件里面新增相关的代码内容。

1.在Makefile文件里面添加如下内容:

CC=gcc
CPROG= chat
BIN = $(CPROG) 
OBJS = main.o sock.o clientlist.o
LDFLAGS += -lpthread
CFLAGS    = -I. 
all: $(BIN)
install:$(BIN)
    @true
uninstall:
    rm -f $(OBJS) $(BIN)
clean:
    rm -f $(OBJS) $(BIN)
$(BIN): $(OBJS)
    $(CC) $(OBJS) $(CFLAGS) $(LDFLAGS) $(CFLAGS_EXTRA) -o $(BIN)

2.在main.c文件里面添加如下内容:

#include <stdio.h>
#include <string.h>
#include "common.h"
void main(int argc, char *argv[])
{
    if(argc<2) {
        LOG("input fmt err!\n");
        return ;
    }
    if(!strcmp(argv[1], "s")) {
        svr_start(SERVER_PORT);
    }
    else if(!strcmp(argv[1], "c")) {
        cli_start(argv);
    }
}

3.在common.h文件添加如下内容:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <semaphore.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <fcntl.h>

#define MAXCLI 10
#define SERVER_PORT 8080
#define USRNAME_LENGTH 32
#define  RGST_INIT 0
#define RGST_OK 1
#define PKG_HEAD 'm'
#define MSG_MODE_LOGIN '0'
#define MSG_MODE_LOGIN_ACK '1'
#define MSG_MODE_SEND '2'
#define MSG_MODE_GET_LIST '3'
#define MSG_MODE_GET_LIST_ACK '4'
#define MSG_MODE_ONLINE '5'
#define MSG_MODE_OFFLINE '6'
#define HISTROY_FILE "h.txt"

struct client_chn_s {
    int skfd;
    char usrname[USRNAME_LENGTH];
    CIRCLEQ_ENTRY(client_chn_s) link;
};
typedef struct client_chn_s client_chn_t;
typedef struct svr {
    int listener;
    unsigned short port;
}svr_t;

typedef struct cli {
    int sockfd;
    unsigned char stat;
    unsigned char history_enable;
    time_t login_timeout;
    time_t reconn_timeout;
    char usrname[USRNAME_LENGTH];
    char ip[48];
    unsigned short port;
}cli_t;

typedef struct msg_s {
    unsigned char msgtyp;
    unsigned char *payload;
    int payloadlen;
    unsigned char *username;
    int username_len;
}msg_t;

4.在sock.c添加如下内容:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdarg.h>
#include "common.h"
cli_t cli;
void LOG(const char *format,...)
{
    va_list argptr;
    char buffer[2048];
    va_start(argptr,format);
    vsprintf(buffer,format,argptr);
    va_end(argptr);
    //printf("%s", buffer);
}
/*
Hex打印
*/
void dump_buf(char *label, unsigned char *in, int len)
{
    int i=0;
    LOG("%s:\n", label);
    for(i=0;i<len;i++) {
        LOG("%02x ", in[i]&0xff);
        if(0==(i+1)%16) {
            LOG("\n");
        }
    }
    LOG("\n");
}
void get_cur_time(char *timestamp)
{
    time_t timep;
    struct tm *p, pp;
    time(&timep);
    localtime_r(&timep, &pp);
    p = &pp;    
    sprintf(timestamp,"<%04d-%02d-%02d %02d:%02d:%02d>",p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
}
void sk_history_record(char *msg)
{
    FILE *fp = fopen(HISTROY_FILE, "ab+");
    if(!fp) {
        LOG("[%s]open %s fail", __func__, HISTROY_FILE);
        return 0;
    }
    fprintf(fp, "%s\r\n", msg);
    fclose(fp);
}
int sk_tcp_svr_init(int port)
{
    int yes = 1;
    struct sockaddr_in svr_addr;
    int listenfd = -1;
    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {        
        LOG("create socket() fail\n");            
        return -1;
    }    
    if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
    {        
        LOG("setsockopt() fail!\n");    
        exit(0);
        return -1;    
    }
    svr_addr.sin_family = AF_INET;    
    svr_addr.sin_addr.s_addr = INADDR_ANY;    
    svr_addr.sin_port = htons(port);
    memset(&(svr_addr.sin_zero), '\0', 8);
    if(bind(listenfd, (struct sockaddr *)&svr_addr, sizeof(svr_addr)) == -1)
    {
        LOG("bind fail!\n");
        return -1;
    }
    if(listen(listenfd, MAXCLI) == -1)        
    {        
        LOG("listen fail\n");
        return -1;
    }
    LOG("listen success!!!\n");
    return listenfd;
}
int sk_start_connect(int *sockfd,char *ip,unsigned short port)
{
    int i; 
    int iRet;
    int status;
    int times=3;
    struct sockaddr_in host;
    int    err; 
       int errlen; 
    struct timeval timeout={3,0};
    fd_set mask;
    iRet=0;
    status=-1;
    *sockfd=-1;
    if ((0==port) || 0==strlen(ip))
    {
        LOG("invalid param");
        return 0;
    }
    host.sin_family=AF_INET; 
    host.sin_port=htons(port); 
    LOG("connect %s:%u\n",ip,port);
    inet_aton(ip,&host.sin_addr);
    if ( (*sockfd=socket(AF_INET,SOCK_STREAM,0))<0 ) 
    { 
        LOG("create sock err"); 
        iRet=0;
        goto err_out; 
    }
    iRet=connect(*sockfd,(struct sockaddr *)&host,sizeof(host));
    if(iRet==0) {
        LOG("connect succ\n");
        if(fcntl(*sockfd,F_SETFL,O_NDELAY) < 0 )
        { 
            LOG("set sock non_block error"); 
            iRet=0;
            LOG("fcntl err");
            goto err_out; 
            
        } 
        return 1;
    }
    LOG("iRet:%d\n", iRet);
err_out:
    return (iRet);
}
int sk_tcp_connect_init(char *svr, int port)
{
    int iRet, sockfd = -1;
    iRet=sk_start_connect(&sockfd,svr, port);
    if (iRet == 0)
        return -1;
    return sockfd;
}
int sk_msg_pack(unsigned char *buf, msg_t *m)
{
    int len = 0;
    unsigned char msgtype = m->msgtyp;
    buf[len++] = PKG_HEAD;
    buf[len++] = msgtype;
    switch(msgtype)
    {
        case MSG_MODE_LOGIN:
        {
            LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
            if(m->username_len<=USRNAME_LENGTH) {
                buf[len++]=m->username_len/256;
                buf[len++]=m->username_len;
                memcpy(buf+len, m->username, m->username_len);
                len+=m->username_len;
            }
            LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
            break;
        }
        case MSG_MODE_SEND:
        {
            char timestamp[48]="";
            get_cur_time(timestamp);
            LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
            unsigned char msgbuf[1024]="";
            int msglen = sprintf(msgbuf,"%s%s:%s", m->username, timestamp,m->payload);
            buf[len++]=msglen/256;
            buf[len++]=msglen;
            memcpy(buf+len, msgbuf, msglen);
            len+=msglen;
            break;
        }
        case MSG_MODE_GET_LIST_ACK:
        case MSG_MODE_OFFLINE:
        case MSG_MODE_ONLINE:
        {
            buf[len++]=m->payloadlen/256;
            buf[len++]=m->payloadlen;
            memcpy(buf+len, m->payload, m->payloadlen);
            len+=m->payloadlen;
            break;
        }
    }
    return len;
}
void svr_msg_hander(int fd, unsigned char *in, int len)
{
    unsigned char msgtyp = in[1];
    switch(msgtyp)
    {
        case MSG_MODE_LOGIN:
        {
            char usr[USRNAME_LENGTH]="";
            strcpy(usr, &in[4]);
            client_chn_add_usr(fd,  usr);
            msg_t m;
            m.msgtyp = MSG_MODE_LOGIN_ACK;
            unsigned char ackbuf[64]="";
            int acklen = sk_msg_pack(ackbuf, &m);
            LOG("Send RGST Ack!\n");
            send(fd, ackbuf, acklen, 0);
            svr_online_offline_notify(fd,1);
            break;
        }
        case MSG_MODE_SEND:
        {
            client_chn_msg_proc(0,in, len);
            break;
        }
        case MSG_MODE_GET_LIST:
        {
            char usrlist[1024]="";
            int ret=client_chn_get_usr_list(usrlist);
            msg_t m;
            m.msgtyp = MSG_MODE_GET_LIST_ACK;
            m.payload = usrlist;
            m.payloadlen = ret;
            unsigned char ackbuf[1450]="";
            int acklen = sk_msg_pack(ackbuf, &m);
            dump_buf("Send friend list resuest Ack", ackbuf,acklen);
            send(fd, ackbuf, acklen, 0);
            break;
        }
    }
}
void svr_online_offline_notify(int fd, int isonline)
{
    client_chn_t *chn = client_chn_get_elm(fd);
    msg_t m;
    if(isonline==1) {
        m.msgtyp = MSG_MODE_ONLINE;
    }
    else {
        m.msgtyp = MSG_MODE_OFFLINE;
    }
    char payload[128]="";
    char timestamp[48]="";
    get_cur_time(timestamp);
    if(isonline==1) {
        sprintf(payload, "%s%s Online!", timestamp, chn->usrname);
    }
    else {
        sprintf(payload, "%s%s Quit!", timestamp, chn->usrname);
    }
    m.payload = payload;
    m.payloadlen = strlen(payload);
    unsigned char buf[1450]="";
    int len = sk_msg_pack(buf, &m);
    dump_buf("notify offline", buf,len);
    client_chn_msg_proc(fd,buf, len);
}
int svr_recv_proc(int fd)
{
    unsigned char recvbuf[1450]="";
    int ret =recv(fd, recvbuf, sizeof(recvbuf), 0);
    if(ret<=0) {
        int close_flag = 0;
        if(ret == 0)
        {
            /* connection closed */
            LOG("socket %d close\n", fd);
            close_flag = 1;
        }
        else {
            if(errno!=EAGAIN) {
                LOG("recv error!\n");
                close_flag =1;
            }
        }
        if(close_flag) {
            svr_online_offline_notify(fd, 0);
            return -1;
        }
    }
    else {
        LOG("[clisk:%d]recv %d bytes\n", fd, ret);
        dump_buf("recv", recvbuf, ret);
        //client_chn_msg_proc(fd, recvbuf, ret);
        svr_msg_hander(fd, recvbuf, ret);
        return ret;
    }
    return 0;
}
void svr_accept(svr_t *svr)
{
    int newfd;
    struct sockaddr_in clientaddr;
    int addrlen = sizeof(clientaddr);
    
    if((newfd = accept(svr->listener, (struct sockaddr *)&clientaddr, (socklen_t *)&addrlen)) == -1)
    {
        LOG("accept() error !\n");
    }
    else
    {
        LOG(" New connection from %s on socket %d\n", inet_ntoa(clientaddr.sin_addr), newfd);
        client_chn_t *elm = (client_chn_t *)malloc(sizeof(client_chn_t));
        elm->skfd = newfd;
        client_chn_insert(elm);
    }
}
int svr_add_fds(svr_t *svr, fd_set *master)
{
    int i;
    int fdmax = 0;
    if(svr->listener>0) {
        FD_SET(svr->listener, master);
        if (svr->listener > fdmax) {
            fdmax = svr->listener;
        }
    }
    int max = client_chn_add_fds(master);
    if(fdmax<max) fdmax = max;
    return fdmax;
}
void svr_select_fd_proc(svr_t *svr, int fd)
{
    if(svr->listener==fd) {
        svr_accept(svr);
        return;
    }
    else {
        if(svr_recv_proc(fd)<0) {
            client_chn_del(fd);
            close(fd);
        }
    }
}
void svr_start(int port)
{
    svr_t svr;
    svr.port=port;
    svr.listener=sk_tcp_svr_init(port);
    struct timeval tv, timeo;
    fd_set master;
    client_chn_init();
    while(1) {
        tv.tv_sec=0;
        tv.tv_usec=20000;
        FD_ZERO(&master);
        int fdmax  = svr_add_fds(&svr, &master);
        if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
        {
            LOG("select err\n");
        }
        int i;
        for(i = 0; i <= fdmax; i++){
            if(FD_ISSET(i, &master)) {
                LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
                svr_select_fd_proc(&svr,i);
            }
        }
    }
}
void *cli_input_hander(void *arg)
{
     while(1) {
        char msgbuf[1024]="";
        //scanf("%s", msgbuf);
        gets(msgbuf);
        if((cli.sockfd>0)&&(cli.stat==RGST_OK)) {
            if((strlen(msgbuf)==2)&&(msgbuf[0]=='-')) {
                switch(msgbuf[1]) {
                    case 'h':
                    {
                        show_help();
                        break;
                    }
                    case 'H':
                    {
                        printf("<---------History----------\n");
                        char cmd[64]="";
                        sprintf(cmd, "cat %s", HISTROY_FILE);
                        system(cmd);
                        printf("--------------------------->\n");
                        break;
                    }
                    case 'q':
                    {
                        printf("Bye!\n");
                        exit(0);
                        break;
                    }
                    case 'g':
                    {
                        char sendbuf[1450]="";
                        char username[32]="";
                        msg_t m;
                        m.msgtyp = MSG_MODE_GET_LIST;
                        int sendlen = sk_msg_pack(sendbuf, &m);
                        dump_buf("send", sendbuf, sendlen);
                        send(cli.sockfd, sendbuf, sendlen, 0);
                        break;
                    }
                }
            }
            else {
                char sendbuf[1450]="";
                char username[32]="";
                msg_t m;
                m.payload = msgbuf;
                m.payloadlen=strlen(msgbuf);
                
                strcpy(username, cli.usrname);
                m.username = username;
                m.username_len = strlen(cli.usrname);
                m.msgtyp = MSG_MODE_SEND;
                
                int sendlen = sk_msg_pack(sendbuf, &m);
                dump_buf("send", sendbuf, sendlen);
                send(cli.sockfd, sendbuf, sendlen, 0);
            }
        }
     }
}
void cli_input_start()
{
     pthread_t thread_id;
     int ret = pthread_create(&thread_id, NULL, cli_input_hander, NULL);
     if (ret != 0) {
            LOG("[%s] create phread fail,%s.\n", __func__, strerror(ret));
            exit(1);
    }
}
void cli_login_send(cli_t *c)
{
    unsigned char logibuf[512]="";
    unsigned char usrname[32]="";
    msg_t  m;
    m.msgtyp = MSG_MODE_LOGIN;
    sprintf(usrname,"%s",c->usrname);
    m.username = usrname;
    m.username_len = strlen(c->usrname);
    LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
    int len = sk_msg_pack(logibuf, &m);
    if(c->sockfd>0) {
        send(c->sockfd, logibuf, len, 0);
    }
}
void cli_login_check(cli_t *c)
{
    if(c->stat==RGST_INIT) {
        LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
        cli_login_send(c);
    }
}
void cli_reconnet_check(cli_t *c)
{
    if(c->sockfd<0) {
        LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
        cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
        if(cli.sockfd>0)
            cli_login_send(c);
    }
}
void cli_recv_msg_hander(cli_t *c, unsigned char *msg, int len)
{
    unsigned char msgtype=msg[1];
    if(msgtype==MSG_MODE_LOGIN_ACK) {
        c->stat= RGST_OK;
        LOG("RGST_OK!\n");
    }
    else if(msgtype==MSG_MODE_GET_LIST_ACK) {
        unsigned char data[1024]="";
        int datalen = msg[2];
        datalen=datalen*256+msg[3];
        memcpy(data, msg+4, datalen);
        printf("<---------Friends list----------\n");
        printf("%s", data);
        printf("-------------------------------->\n");
        
    }
    else if(msgtype==MSG_MODE_SEND||msgtype==MSG_MODE_ONLINE
        ||msgtype==MSG_MODE_OFFLINE||msgtype==MSG_MODE_ONLINE) {
        unsigned char data[1024]="";
        int datalen = msg[2];
        datalen=datalen*256+msg[3];
        memcpy(data, msg+4, datalen);
        LOG("recv<==%s\n", data);
        printf("%s\n", data);
        if(c->history_enable) {
            sk_history_record(data);
        }
    }
}
int cli_recv_proc(cli_t *c)
{
    int fd = c->sockfd;
    unsigned char recvbuf[1450]="";
    int ret =recv(fd, recvbuf, sizeof(recvbuf), 0);
    if(ret<=0) {
        int close_flag = 0;
        if(ret == 0)
        {
            /* connection closed */
            LOG("socket %d close\n", fd);
            close_flag = 1;
        }
        else {
            if(errno!=EAGAIN) {
                LOG("recv error!\n");
                close_flag =1;
            }
        }
        if(close_flag) {
            return -1;
        }
    }
    else {
        LOG("[clisk:%d]recv %d bytes, %s\n", fd, ret, recvbuf);
        cli_recv_msg_hander(c, recvbuf, ret);
        return ret;
    }
    return 0;
}
void cli_start(char *argv[])
{
    strcpy(cli.ip,"127.0.0.1");
    cli.port=SERVER_PORT;
    cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
    cli.stat = RGST_INIT;
    cli.history_enable = 1;
    strcpy(cli.usrname, argv[2]);
    LOG("[%s][%d]cli.usrname:%s\n",__FUNCTION__,__LINE__, cli.usrname);
    struct timeval tv, timeo;
    fd_set master;
    time_t timenow;
    client_chn_init();
    cli_input_start();
    while(1) {
        tv.tv_sec=0;
        tv.tv_usec=20000;
        FD_ZERO(&master);
        time(&timenow);
        int fdmax=0;
        if(cli.sockfd>0) {
            FD_SET(cli.sockfd, &master);
            if (cli.sockfd> fdmax) {
                fdmax = cli.sockfd;
            }
        }
        if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
        {
            LOG("select err\n");
        }
        if(FD_ISSET(cli.sockfd, &master)) {
            LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
            if(cli_recv_proc(&cli)<0) {
                close(cli.sockfd);
                cli.sockfd = -1;
                cli.stat=RGST_INIT;
            }
        }
        if(abs(timenow-cli.login_timeout)>=10) {
            LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
            cli.login_timeout = timenow;
            cli_login_check(&cli);
        }
        if(abs(timenow-cli.reconn_timeout)>=30) {
            LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
            cli.reconn_timeout = timenow;
            cli_reconnet_check(&cli);
        }
    }
}
/*
服务端程序入口
*/
void svr_start(int port)
{    
    svr_t svr;
    svr.port=port;
    svr.listener=sk_tcp_svr_init(port);
    
    struct timeval tv, timeo;
    fd_set master;
    client_chn_init();
    while(1) {
        tv.tv_sec=0;
        tv.tv_usec=20000;
        FD_ZERO(&master);
        int fdmax  = svr_add_fds(&svr, &master);
        if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
        {
            LOG("select err\n");
        }
        int i;
        for(i = 0; i <= fdmax; i++){
            if(FD_ISSET(i, &master)) {
                LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
                svr_select_fd_proc(&svr,i);
            }
        }
    }
}
void *cli_input_hander(void *arg)
{
     while(1) {
        char msgbuf[1024]="";
        scanf("%s", msgbuf);
        if((cli.sockfd>0)&&(cli.stat==RGST_OK)) {
            char sendbuf[1450]="";
            char username[32]="";
            msg_t m;
            m.payload = msgbuf;
            m.payloadlen=strlen(msgbuf);
            strcpy(username, cli.usrname);
            m.username = username;
            m.username_len = strlen(cli.usrname);
            m.msgtyp = MSG_MODE_SEND;
            int sendlen = sk_msg_pack(sendbuf, &m);
            dump_buf("send", sendbuf, sendlen);
            send(cli.sockfd, sendbuf, sendlen, 0);
        }
     }
}
void cli_input_start()
{
     pthread_t thread_id;
     int ret = pthread_create(&thread_id, NULL, cli_input_hander, NULL);
     if (ret != 0) {
            LOG("[%s] create phread fail,%s.\n", __func__, strerror(ret));
            exit(1);
    }
}
void cli_login_send(cli_t *c)
{
    unsigned char logibuf[512]="";
    unsigned char usrname[32]="";
    msg_t  m;
    m.msgtyp = MSG_MODE_LOGIN;
    sprintf(usrname,"%s",c->usrname);
    m.username = usrname;
    m.username_len = strlen(c->usrname);
    LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
    int len = sk_msg_pack(logibuf, &m);
    if(c->sockfd>0) {
        send(c->sockfd, logibuf, len, 0);
    }
}
void cli_login_check(cli_t *c)
{
    if(c->stat==RGST_INIT) {
        LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
        cli_login_send(c);
    }
}
void cli_reconnet_check(cli_t *c)
{
    if(c->sockfd<0) {
        LOG("[%s][%d]\n",__FUNCTION__,__LINE__);
        cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
        if(cli.sockfd>0)
            cli_login_send(c);
    }
}
void cli_recv_msg_hander(cli_t *c, unsigned char *msg, int len)
{
    unsigned char msgtype=msg[1];
    if(msgtype==MSG_MODE_LOGIN_ACK) {
        c->stat= RGST_OK;
        LOG("RGST_OK!\n");
    }
    else if(msgtype==MSG_MODE_SEND) {
        unsigned char data[1024]="";
        int datalen = msg[2];
        datalen=datalen*256+msg[3];
        memcpy(data, msg+4, datalen);
        LOG("recv<==%s\n", data);
        printf("<==%s\n", data);
    }
}
/*
客户端数据接收
*/
int cli_recv_proc(cli_t *c)
{
    int fd = c->sockfd;
    unsigned char recvbuf[1450]="";
    int ret =recv(fd, recvbuf, sizeof(recvbuf), 0);
    if(ret<=0) {
        int close_flag = 0;
        if(ret == 0)
        {
            /* connection closed */
            LOG("socket %d close\n", fd);
            close_flag = 1;
        }
        else {
            if(errno!=EAGAIN) {
                LOG("recv error!\n");
                close_flag =1;
            }
        }
        if(close_flag) {
            return -1;
        }
    }
    else {
        LOG("[clisk:%d]recv %d bytes, %s\n", fd, ret, recvbuf);
        cli_recv_msg_hander(c, recvbuf, ret);
        return ret;
    }
    return 0;
}
/*
客户端程序入口
*/
void cli_start(char *argv[])
{
    strcpy(cli.ip,"127.0.0.1");
    cli.port=SERVER_PORT;
    cli.sockfd = sk_tcp_connect_init(cli.ip,cli.port);
    cli.stat = RGST_INIT;
    strcpy(cli.usrname, argv[2]);
    LOG("[%s][%d]cli.usrname:%s\n",__FUNCTION__,__LINE__, cli.usrname);
    struct timeval tv, timeo;
    fd_set master;
    time_t timenow;
    client_chn_init();
    cli_input_start();
    while(1) {
        tv.tv_sec=0;
        tv.tv_usec=20000;
        FD_ZERO(&master);
        time(&timenow);
        int fdmax=0;
        if(cli.sockfd>0) {
            FD_SET(cli.sockfd, &master);
            if (cli.sockfd> fdmax) {
                fdmax = cli.sockfd;
            }
        }
        if(select(fdmax+1, &master, NULL, NULL, &tv)<0)
        {
            LOG("select err\n");
            
        }
        if(FD_ISSET(cli.sockfd, &master)) {
            if(cli_recv_proc(&cli)<0) {
                close(cli.sockfd);
                cli.sockfd = -1;
                cli.stat=RGST_INIT;
            }
        }
        if(abs(timenow-cli.login_timeout)>=10) {
            //定时检查登录状态
            cli.login_timeout = timenow;
            cli_login_check(&cli);
        }
        if(abs(timenow-cli.reconn_timeout)>=30) {
            //定时检查连接状态
            cli.reconn_timeout = timenow;
            cli_reconnet_check(&cli);
        }
    }
}

5.在clientlist.c添加如下内容:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <semaphore.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <fcntl.h>
#include "common.h"
static CIRCLEQ_HEAD(client_Head, client_chn_s) client_head;
void client_chn_init(void)
{
    CIRCLEQ_INIT(&client_head);
}
void client_chn_insert(client_chn_t *elm)
{
    CIRCLEQ_INSERT_HEAD(&client_head, elm, link);
}
void client_chn_del(int skfd)
{
    struct client_chn_s *chn;

    for (chn = client_head.cqh_first; chn != (void *)&client_head;
        chn = chn->link.cqe_next) {
        if (chn->skfd== skfd) {
            CIRCLEQ_REMOVE(&client_head, chn, link);
            free(chn);
            break;
        }
    }

}
int client_chn_add_usr(int skfd, char *usr)
{
    struct client_chn_s *chn;
    int ret=0;
    for (chn = client_head.cqh_first; chn != (void *)&client_head;
        chn = chn->link.cqe_next) {
        if(chn->skfd==skfd) {
            sprintf(chn->usrname, "%s\n",usr);
            LOG("[%s](%d)add user:%s\n", __func__, __LINE__, chn->usrname);
        }
    }
    return ret;
}
int client_chn_add_fds(fd_set *master)
{
    int i;
    int fdmax = 0;
    struct client_chn_s *chn;
    for (chn = client_head.cqh_first; chn != (void *)&client_head;
        chn = chn->link.cqe_next) {
        if(chn->skfd>fdmax) fdmax = chn->skfd;
        FD_SET(chn->skfd, master);
    }
    return fdmax;
}
void client_chn_print()
{
    struct client_chn_s *chn;
    for (chn = client_head.cqh_first; chn != (void *)&client_head;
        chn = chn->link.cqe_next) {
        LOG("[%s](%d)skfd:%d\n", __func__, __LINE__, chn->skfd);
    }
}
client_chn_t *client_chn_get_elm(int fd)
{
    struct client_chn_s *chn;
    for (chn = client_head.cqh_first; chn != (void *)&client_head;
        chn = chn->link.cqe_next) {
       if(chn->skfd==fd) {
        return chn;
       } 
    }
    return NULL;
}
int client_chn_msg_proc(int fd, unsigned char *in, int len)
{
    struct client_chn_s *chn;
    for (chn = client_head.cqh_first; chn != (void *)&client_head;
        chn = chn->link.cqe_next) {
        if(fd>0) {
            if(chn->skfd!=fd) {
                send(chn->skfd, in, len, 0);
            }
        }
        else {
            send(chn->skfd, in, len, 0);
        }
    }
}
int client_chn_get_usr_list(char *out)
{
    struct client_chn_s *chn;
    int ret=0;
    int cnt=0;
    for (chn = client_head.cqh_first; chn != (void *)&client_head;
        chn = chn->link.cqe_next) {
            cnt++;
        ret+=sprintf(out+ret, "%d:%s", cnt, chn->usrname);
        LOG("[%s](%d)pack user:%s\n", __func__, __LINE__, chn->usrname);
    }
    return ret;
}

6.代码编译:

make clean

make

最后生成“chat”可执行文件,用户便可以在Linux环境下启用聊天室了。

7.启动聊天室步骤(这里以三个用户为例子)

7.1启用后台服务端

7.2依次启动三个客户端

至此,所有接入该聊天室的用户都可以开始使用聊天室的基本功能了。

总结:该程序使用了大家熟悉的TCP协议,通过客户端/服务端的方式,在Linux系统下使用C语言实现简单的局域网聊天室的功能,该功能的实现过程中可以让初学者熟悉Linux下socket编程以及不同设备之间的以太网通信机制,为以后做其它的项目打下基础并积累一些经验。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:1 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐