Linux下使用C语言实现简单的聊天室程序

news2024/10/6 0:28:00

本文章介绍一种基于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编程以及不同设备之间的以太网通信机制,为以后做其它的项目打下基础并积累一些经验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/385804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaWeb】HTML常用标签

HTML标签结构 HTML语言主要都是由标签构成的。 标签名 在 <> 中 如<body> 标签大部分成对出现&#xff0c;代表开始和结束 如 <body>标签中的内容</body> 少部分单个出现&#xff0c;叫单标签 </br> 代表换行 标签中可以加属性&#xff0c;多个…

Oracle使用序列触发器实现主键id自动增长

记录一下oracle中的字段自增问题 在mysql中我们可以使用auto_increment来实现自动增长功能 而在oracle主键没有自动增长类型&#xff0c;所以一般使用序列产生的值作为某张表的主键,实现主键自增。 序列是oracle用来生产一组等间隔的数值。序列是递增&#xff0c;而且连续的。…

The JSON value could not be converted to System.DateTime

项目场景&#xff1a; 我们在使用Asp.Net Core WebApi和前端进行交互时&#xff0c;经常会因为提交数据中有时间自动而导致服务端接收不到数据&#xff0c;前端报错的问题。 问题描述 在开发中经常是后端同学把WebApi开发完毕后&#xff0c;然后前端同学进行接口对接开发。理…

Java:Java vs Kotlin–Android应用程序开发的最佳语言?

在移动应用技术领域&#xff0c;Java和Kotlin是最流行的搜索词之一。如果你也在寻找Java与Kotlin框架的比较&#xff0c;那么这篇文章就是为你准备的!让我们来全面了解一下这两种技术之间的区别。编程语言的定义Java的工作原理&#xff1a;Java的工作原理是“编写一次&#xff…

前端整理 —— javascript 2

1. generator 详细介绍 generator 介绍 generator 是 ES6 提供的一种异步编程解决方案&#xff0c;在语法上&#xff0c;可以把它理解为一个状态机&#xff0c;内部封装了多种状态。执行generator&#xff0c;会生成返回一个遍历器对象。返回的遍历器对象&#xff0c;可以依次…

软考中级-结构化开发

1 系统设计的基本原理&#xff08;1&#xff09;抽象&#xff08;2&#xff09;信息隐蔽&#xff08;3&#xff09;模块化将待开发软件分解为多个可独立开发、测试的模块&#xff0c;分而治之&#xff0c;最后组装。&#xff08;4&#xff09;模块独立每个模块完成一个相对独立…

世界文明的脉络

人类文明大体上可分为农耕文明、海洋文明和游牧文明三大类别&#xff0c;文明的标志一般是文字、青铜器、城市以及礼仪性建筑等要素。据此&#xff0c;史学家目前已发现了巴比伦文明、埃及文明、印度文明、华夏文明、希腊文明和波斯文明六种主要文明&#xff0c;其中前四种文明…

字节跳动测开岗,4面都过了,HR告诉我被刷在这个地方....

说在前面 面试时最好不要虚报工资。本来字节跳动是很想去的&#xff0c;几轮面试也通过了&#xff0c;最后没offer&#xff0c;自己只想到下面几个原因&#xff1a; 虚报工资&#xff0c;比实际高30%&#xff1b;有更好的人选&#xff0c;这个可能性不大&#xff0c;我看还在…

stm32CubeIDE FMC 驱动LCD(8080)

一&#xff0c;TFT屏硬件接口16位&#xff0c;80并口。二&#xff0c;FMC介绍。FSMC&#xff08;Flexible Static Memory Controller&#xff09;&#xff0c;译为灵活的静态存储控制器。STM32F1 系列芯片使用 FSMC 外设来管理扩展的存储器&#xff0c;它可以用于驱动包括 SRAM…

【halcon】模板匹配参数之金字塔级数

背景 今天&#xff0c;在使用模板匹配的时候&#xff0c;突然程序卡死&#xff0c;CPU直接飙到100%。最后排查发现是模板匹配其中一个参数 NumLevels 导致的&#xff1a; NumLevels: The number of pyramid levels used during the search is determined with numLevels. If n…

java25种设计模式抽象工厂模式

Java设计模式 - 抽象工厂模式 抽象工厂模式是另一个创建模式。 抽象工厂模式&#xff0c;也称为工厂的工厂&#xff0c;有一个工厂创建其他工厂。 当使用抽象工厂模式时&#xff0c;我们首先使用超级工厂创建工厂&#xff0c;然后使用创建的工厂创建对象。 简单来说&#xff0…

事件循环机制(Event Loop)和宏任务(macro-tast)微任务(micro-tast),详细讲解!!!

“事件循环机制” 和 “宏任务微任务” 也是前端面试中常考的面试题了。首先&#xff0c;要深刻理解这些概念的话&#xff0c;需要回顾一些知识点。知识点回顾1、进程与线程进程。 程序运行需要有它自己的专属内存空间&#xff0c;可以把这块内存空间简单的理解为进程每个应用至…

计算机求解满足三角形各边数字之和相等的数字填充

圆圈处不重复的填入1至9&#xff0c;使得每条边的四个数字相加的总和相等。 求解思路&#xff1a; 数组中存放1到9的数字&#xff0c;每次随机交换两个数字&#xff0c;构建出新的数字组合&#xff0c;计算这个数字组合是否符合要求。 #include <stdio.h> #include <…

SpringBoot实现Excel导入导出,简单好用

EasyPoi简介 POI是Java操作MicroOffice&#xff08;如对Excel的导入导出&#xff09;的一个插件。POI的全称是&#xff08;Poor Obfuscation Implementation&#xff09;&#xff0c;POI官网地址是 http://poi.achache.org/index.html 。 EasyPoi对POI进行了优化&#xff0c;…

SVN vs Git 不是技术之争,而是生态之争

师父&#xff08;80 后老员工&#xff09;&#xff1a;小吉啊&#xff0c;我看我们文档越来越多了&#xff0c;手动管理起来很费劲。你去搞一个 SVN 来用一哈&#xff0c;做个版本控制&#xff0c;这样大家都方便。 徒弟&#xff08;95 后新力量&#xff09;&#xff1a;师父啊…

电脑快捷方式删除文件后四种找回方法

快捷指令是一种用作替代快捷键操作的技术。也可以称为“快捷键”&#xff0c;“快捷方式”或“快捷键序列”&#xff0c;它们允许用户在非常快速和方便的方式建立特定操作序列&#xff0c;这对于执行重复性或提高效率非常有用。通过使用快捷指令&#xff0c;您可以执行快速复制…

pod进阶

一.资源限制当定义 Pod时可以选择性地为每个容器设定所需要的资源数量。最常见的可设定资源是CPU和内存大小&#xff0c;以及其他类型的资源。当为 pod 中的容器指定了request资源时&#xff0c;调度器就使用该信息来决定将Pod 调度到哪个节点上。当还为容器指定了limit资源时&…

C++Primer16.1.6节练习

练习16.28&#xff1a; 简易的shared_ptr代码如下 #include <iostream> #include <vector> #include <list> using namespace std;//shared_ptr模板 template<typename T>class SharedPtr {friend SharedPtr<T>& MakeShared(T* t); public…

docker部署MySQL主从服务

一.主从同步流程关于MySQL主从复制主要同步的是binlog日志&#xff0c;涉及到三个线程&#xff0c;一个运行在主节点&#xff08;log dump thread&#xff09;&#xff0c;其余两个(I/O thread, SQL thread)运行在从节点&#xff0c;如下图所示:当主库数据发生变更时&#xff0…

【JavaWeb】EL表达式(191-202)

191.EL表达式-什么是EL表达式&#xff0c;以及它的作用 什么是 EL 表达式&#xff0c;EL 表达式的作用? EL 表达式的全称是&#xff1a;Expression Language。是表达式语言。 EL 表达式的什么作用&#xff1a;输出&#xff0c;EL 表达式主要是代替 jsp 页面中的表达式脚本在…