网络编程之网络聊天室(C语言)

前言:

最近看到大佬所写网络聊天室设计颇有感触,恰逢刚好学到这块,因此结合自己理解和自己构思写了这篇拙作,如有不当之处请见谅,菜鸟之作可以提点合理化建议。本人参考这篇文章https://blog.csdn.net/weixin_43164603/article/details/107301548icon-default.png?t=N7T8https://blog.csdn.net/weixin_43164603/article/details/107301548

一.概述

网络聊天室设计内容

1.用户管理

用户注册,登录,退出,注销

2.聊天室管理

聊天室注册,登录,查看已有房间,退出,注销

3.聊天管理

聊天,退出,查看聊天历史记录

二.知识储备

在该项目中用到文件IO,标准IO,Makefile,sqlite3数据库,TCP协议,epoll并发技术及相关网络编程技术,C语言相关知识

三.设计思维

1.并发流程图

2.客户机流程图

四.系统设计

网络聊天室项目基于TCP协议的服务器和客户机实现,同时利用epoll并发技术可以实现多用户同时登陆进行相互聊天,在客户端可以进行用户注册进而转至一级界面进行用户登录和退出,在用户登陆成功后可以注册聊天室与用户注册同理,同时也可以查看已有聊天室数量,可以选择退出或者进行用户注销返回用户登录界面,用户登录聊天室则实现服务器与客户机连接进行传输数据,可以输入“quit”退出聊天界面,同样可以查看聊天记录和退出返回上级界面或注销聊天室,在这里聊天记录由服务器记录因此是所有聊天室聊天记录,大致设计如此。

1.系统数据结构设计

 对于用户定义结构体初始化用户名和密码,注册则将结构体中信息插入数据库中保存,对于聊天室定义结构体初始化房间号和密码,注册则将结构体中信息插入数据库中保存

typedef struct _users{
    char name[256];//用户名
    int password;//密码
}user_t;//用户
typedef struct _rooms{
    int room_number;//房间号
    int password;//密码
}room_t;//聊天室

2.系统主要函数设计

2.1客户机
2.1.1void print_menu_one_func();

该函数功能为打印一级界面采单位用户提供选择

2.1.2void print_menu_two_func();

该函数功能为打印二级界面采单位用户提供选择

2.1.3void print_menu_three_func();

该函数功能为打印三级界面采单位用户提供选择

2.1.4int create_tables_func(sqlite3 *my_db,const char *sql);

该函数功能为在数据库中创建表格保存用户的用户名和密码

参数:my_db:数据库句柄

          sql:创建表格的sql语句

2.1.5int insert_tables_user_func(sqlite3 *my_db,user_t user,const char *tablename);

该函数功能为向哪个表中插入用户信息

参数:my_db:数据库句柄

           user:用户结构体

          tablename:数据库中对应的表名

2.1.6int judge_user_func(sqlite3 *my_db,user_t user,const char *tablename);

该函数功能为判断该用户是否已经注册

参数:my_db:数据库句柄

           user:用户结构体

          tablename:数据库中对应的表名

2.1.7int delete_user_func(sqlite3 *my_db,user_t user,const char *tablename);

该函数功能为注销用户,在数据库中删除用户信息

参数:my_db:数据库句柄

           user:用户结构体

          tablename:数据库中对应的表名

2.1.8int register_user_func(sqlite3 *my_db,user_t *user,const char *tablename);

该函数功能为用户登录,在登录前会判断该用户是否已经注册,在登陆的同时会将登录时间及用户名写入登录日志

参数:my_db:数据库句柄

           user:用户结构体(这里使用地址传参,后面查询单词,并将记录写入文件要用到用户信息)

          tablename:数据库中对应的表名

2.1.9int insert_tables_room_func(sqlite3 *my_db,room_t room,const char *tablename);

该函数功能为向哪个表中插入房间信息

参数:my_db:数据库句柄

           room:房间结构体

          tablename:数据库中对应的表名

2.1.10int judge_room_func(sqlite3 *my_db,room_t room,const char *tablename);

该函数功能为判断该房间是否已经注册

参数:my_db:数据库句柄

           room:房间结构体

          tablename:数据库中对应的表名

2.1.11int register_room_func(sqlite3 *my_db,room_t *room,const char *tablename);

该函数功能为房间登录,在登录前会判断该房间是否已经注册,在登陆的同时会将登录时间及房间号写入登录日志

参数:my_db:数据库句柄

           room:房间结构体

          tablename:数据库中对应的表名

2.1.12int delete_room_func(sqlite3 *my_db,room_t room,const char *tablename);

该函数功能为注销房间,在数据库中删除房间信息

参数:my_db:数据库句柄

           room:用户结构体

          tablename:数据库中对应的表名

2.1.13int search_room_func(sqlite3 *my_db,const char *tablename);

该函数功能为查找所有已经注册了的房间

参数:my_db:数据库句柄

          tablename:数据库中对应的表名

2.1.14int search_room_history_func();

该函数功能为查看所有聊天室聊天记录

2.1.15int client_user_func(int socket_fd);

该函数功能为客户机与服务器进行连接

参数:socket_fd,依据TCP协议创建的流式套接字

2.1.16int main函数

该函数主要创建客户端流式套接字,数据库句柄,打印相关帮助信息为用户提供选择,不同选择会进入不同分支

2.2服务器

2.2.1int sever_func(int socket_fd);

该函数功能为流式套接字绑定相关属性,并设为监听状态

参数:socket_fd,依据TCP协议创建的流式套接字

2.2.2int create_user_history_func(const char *msg);

该函数功能为创建聊天历史记录

参数:msg 客户机发来的数据

2.2.3int main函数

该主函数主要负责创建流式套接字,创建epoll实例,事件结构体,就绪事件数组,通过epoll并发实现与多客户机接收和发送数据

五.运行效果

1.客户机

1.1用户注册

1.2用户登录

1.3注册房间

1.4登录房间

1.5查看已有房间

1.6聊天

1.7聊天记录

1.8房间注销

1.9用户注销

1.10最终退出

2.服务器

六.总结

通过这次项目编写,和大佬对比也找到了自己的差距,知道哪一方面做的不是很好,后期可以完善,经过项目实战也让自己对知识有了更深入的了解,对TCP和epoll也更加的熟练了,当然在代码实现这块也存在很多不足,像数据收发存在不严谨问题,无法显示对方名称,也无法查看仅自己聊天室记录,功能实现起来不完善,这些都是以后需要改进的地方,总之,这一项目对我来说试一次很好的练习,也知道了自己的不足,以后会继续完善下去,我想更多的是有了自己的理解,有什么不当的请指正,谢谢!

七.代码

1.客户机

1.1client.c
#include"client.h"
//一级界面
void print_menu_one_func(){
    printf("\t-----------------------------------------------------------\n");
    printf("\t|                      网络聊天室                      (1)|\n");
    printf("\t| 版本 :4.6.2                                             |\n");
    printf("\t| 创作者 :晴耕雨读912                                     |\n");
    printf("\t| 功能 :(用户管理)                                        |\n");
    printf("\t|       [1] 登录                                          |\n");
    printf("\t|       [2] 注册                                          |\n");
    printf("\t|       [3] 退出                                          |\n");
    printf("\t| 注意 :用户只有登陆成功后才能进入查询聊天室状态界面      |\n");
    printf("\t-----------------------------------------------------------\n");
}
//二级界面
void print_menu_two_func(){
    printf("\t-----------------------------------------------------------\n");
    printf("\t|                      网络聊天室                      (2)|\n");
    printf("\t| 版本 :4.6.2                                             |\n");
    printf("\t| 创作者 :晴耕雨读912                                     |\n");
    printf("\t| 功能 :(聊天室管理)                                      |\n");
    printf("\t|       [1] 登录                                          |\n");
    printf("\t|       [2] 注册                                          |\n");
    printf("\t|       [3] 查看                                          |\n");
    printf("\t|       [4] 退出                                          |\n");
    printf("\t|       [5] 注销用户                                      |\n");
    printf("\t| 注意 :用户只有登陆成功后才能进入聊天室聊天界面          |\n");
    printf("\t-----------------------------------------------------------\n");
}
//三级界面
void print_menu_three_func(){
    printf("\t-----------------------------------------------------------\n");
    printf("\t|                      网络聊天室                      (3)|\n");
    printf("\t| 版本 :4.6.2                                             |\n");
    printf("\t| 创作者 :晴耕雨读912                                     |\n");
    printf("\t| 功能 :(聊天管理)                                        |\n");
    printf("\t|       [1] 聊天                                          |\n");
    printf("\t|       [2] 历史记录                                      |\n");
    printf("\t|       [3] 退出                                          |\n");
    printf("\t|       [4] 注销聊天室                                    |\n");
    printf("\t| 注意 :用户只有登陆成功后才能进入聊天室聊天界面          |\n");
    printf("\t-----------------------------------------------------------\n");
}
//创建用户数据表函数
int create_tables_func(sqlite3 *my_db,const char *sql){
    if(NULL==my_db || NULL==sql){
        printf("入参失败,请检查...\n");
        return -1;
    }
    char *errmage;
    int ret=sqlite3_exec(my_db,sql,NULL,NULL,&errmage);
    if(SQLITE_OK!=ret){
        printf("创建数据表失败:%s\n",errmage);
        return -2;
    }
    return 0;
}
//用户数据表插入数据函数
int insert_tables_user_func(sqlite3 *my_db,user_t user,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    memset(&user,0,sizeof(user));
    printf("请输入用户名>>");
    scanf("%s",user.name);
    printf("请输入用户名密码>>");
    scanf("%d",&user.password);
    char *sql_user=sqlite3_mprintf("insert into %q values('%q',%d)",tablename,user.name,user.password);
    char *errmage;
    int ret=sqlite3_exec(my_db,sql_user,NULL,NULL,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表插入数据失败:%s\n",errmage);
        return -2;
    }
    sqlite3_free(sql_user);
    return 0;
}
//判断用户是否存在函数
int judge_user_func(sqlite3 *my_db,user_t user,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    int my_callback_func1(void *data,int argc,char **argv,char **zazColName){
        char *name=(char *)data;
        //printf("%s\n",argv[0]);
        //printf("%s\n",name);
        int a=strcmp(name,argv[0]);
        //printf("%d\n",a);
        if(strcmp(name,argv[0])){
            printf("用户名不存在...\n");
            return -1;
        }
        return 0;
    }
    int my_callback_func2(void *data,int argc,char **argv,char **zazColName){
        int password=*((int *)data);
        //printf("%s\n",argv[1]);
        //printf("%d\n",password);
        int value=atoi(argv[1]);
        //printf("%d\n",value);
        if(password!=value){
            printf("密码不正确,请重新输入...\n");
            return -1;
        }
        return 0;
    }
    char *errmage;
    const char *sql_func1=sqlite3_mprintf("select *from %q where 用户名='%q'",tablename,user.name);
    const char *sql_func2=sqlite3_mprintf("select *from %q where 用户名='%q'",tablename,user.name);
    //printf("%s\n",sql_func1);
    //printf("%s\n",sql_func2);
    int ret=sqlite3_exec(my_db,sql_func1,&my_callback_func1,&user.name,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表查找用户名数据失败:%s\n",errmage);
        return -2;
    }
    ret=sqlite3_exec(my_db,sql_func2,&my_callback_func2,&user.password,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表查找用户密码数据失败:%s\n",errmage);
        return -2;
    }
    return 0;
}
//用户登录函数
int register_user_func(sqlite3 *my_db,user_t *user,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    memset(user,0,sizeof(user));
    printf("请输入用户名>>");
    scanf("%s",user->name);
    printf("请输入用户名密码>>");
    scanf("%d",&user->password);
    int value=judge_user_func(my_db,*user,tablename);
    int get_line(FILE *fp){
        int ret=0;
        int count=0;
        while((ret=fgetc(fp))!=-1){
            if(ret=='\n'){
                count++;
            }
        }
        return count;
    }
    if(0==value){
        printf("登录成功,请开始您的非凡之旅...\n");
        //创建日志文件,将用户登录信息写入日志
        FILE *fp=fopen("user_register.txt","a+");
        if(NULL==fp){
            perror("fopen error");
            return -1;
        }
        static time_t sec=0;
        static time_t old_sec=0;
        int line=get_line(fp);
        time(&sec);
        if(sec!=old_sec){
            struct tm *my_time=localtime(&sec);
            line++;
            //拼接用户信息
            fprintf(fp, "%d.%4d-%02d-%02d %02d:%02d:%02d:%s\n", line, \
                my_time->tm_year+1900, my_time->tm_mon+1, my_time->tm_mday, \
                my_time->tm_hour, my_time->tm_min, my_time->tm_sec,user->name);
            fflush(fp);//手动刷新缓冲区
            old_sec = sec;
        } 
    }else{
        return -1;
    }
    return 0;
}
//注销用户函数
int delete_user_func(sqlite3 *my_db,user_t user,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    memset(&user,0,sizeof(user));
    printf("请输入用户名>>");
    scanf("%s",user.name);
    printf("请输入用户名密码>>");
    scanf("%d",&user.password);
    int value=judge_user_func(my_db,user,tablename);
    char *sql_user=sqlite3_mprintf("delete from %q where 用户名='%q'",tablename,user.name);
    char *errmage;
    int ret=sqlite3_exec(my_db,sql_user,NULL,NULL,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表删除数据失败:%s\n",errmage);
        return -2;
    }
    sqlite3_free(sql_user);
    printf("注销用户成功...\n");
    return 0;
}
//房间数据表插入数据函数
int insert_tables_room_func(sqlite3 *my_db,room_t room,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    memset(&room,0,sizeof(room));
    printf("请输入房间号>>");
    scanf("%d",&room.room_number);
    printf("请输入房间密码>>");
    scanf("%d",&room.password);
    char *sql_room=sqlite3_mprintf("insert into %q values(%d,%d)",tablename,room.room_number,room.password);
    char *errmage;
    int ret=sqlite3_exec(my_db,sql_room,NULL,NULL,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表插入数据失败:%s\n",errmage);
        return -2;
    }
    sqlite3_free(sql_room);
    return 0;
}
//判断房间是否存在函数
int judge_room_func(sqlite3 *my_db,room_t room,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    int my_callback_func1(void *data,int argc,char **argv,char **zazColName){
        int number=*((int *)data);
        //printf("%d\n",number);
        //printf("%s\n",argv[0]);
        int value=atoi(argv[0]);
        //printf("%d\n",value);
        if(number!=value){
            printf("房间不存在...\n");
            return -1;
        }
        return 0;
    }
    int my_callback_func2(void *data,int argc,char **argv,char **zazColName){
        int password=*((int *)data);
        //printf("%s\n",argv[1]);
        printf("%d\n",password);
        int value=atoi(argv[1]);
        //printf("%d\n",value);
        if(password!=value){
            printf("房间密码不正确,请重新输入...\n");
            return -1;
        }
        return 0;
    }
    
    const char *sql_func1=sqlite3_mprintf("select *from %q where 房间号=%d",tablename,room.room_number);
    const char *sql_func2=sqlite3_mprintf("select *from %q where 房间号=%d",tablename,room.room_number);
    //printf("%s\n",sql_func1);
    //printf("%s\n",sql_func2);
    char *errmage;
    int ret=sqlite3_exec(my_db,sql_func1,&my_callback_func1,&room.room_number,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表查找房间号数据失败:%s\n",errmage);
        return -2;
    }
    ret=sqlite3_exec(my_db,sql_func2,&my_callback_func2,&room.password,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表查找房间密码数据失败:%s\n",errmage);
        return -2;
    }
    return 0;
}
//房间登录函数
int register_room_func(sqlite3 *my_db,room_t *room,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    memset(room,0,sizeof(room));
    printf("请输入房间号>>");
    scanf("%d",&room->room_number);
    printf("请输入房间密码>>");
    scanf("%d",&room->password);
    int value=judge_room_func(my_db,*room,tablename);
    int get_line(FILE *fp){
        int ret=0;
        int count=0;
        while((ret=fgetc(fp))!=-1){
            if(ret=='\n'){
                count++;
            }
        }
        return count;
    }
    if(0==value){
        printf("登录成功,请开始您的聊天之旅...\n");
        //创建日志文件,将用户登录信息写入日志
        FILE *fp=fopen("room_register.txt","a+");
        if(NULL==fp){
            perror("fopen error");
            return -1;
        }
        static time_t sec=0;
        static time_t old_sec=0;
        int line=get_line(fp);
        time(&sec);
        if(sec!=old_sec){
            struct tm *my_time=localtime(&sec);
            line++;
            //拼接用户信息
            fprintf(fp, "%d.%4d-%02d-%02d %02d:%02d:%02d:%3d\n", line, \
                my_time->tm_year+1900, my_time->tm_mon+1, my_time->tm_mday, \
                my_time->tm_hour, my_time->tm_min, my_time->tm_sec,room->room_number);
            fflush(fp);//手动刷新缓冲区
            old_sec = sec;
        } 
    }else{
        return -1;
    }
    return 0;
}
//注销房间函数
int delete_room_func(sqlite3 *my_db,room_t room,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    memset(&room,0,sizeof(room));
    printf("请输入房间号>>");
    scanf("%d",&room.room_number);
    printf("请输入房间密码>>");
    scanf("%d",&room.password);
    int value=judge_room_func(my_db,room,tablename);
    char *sql_roomr=sqlite3_mprintf("delete from %q where 房间号=%d",tablename,room.room_number);
    char *errmage;
    int ret=sqlite3_exec(my_db,sql_roomr,NULL,NULL,&errmage);
    if(SQLITE_OK!=ret){
        printf("数据表删除数据失败:%s\n",errmage);
        return -2;
    }
    sqlite3_free(sql_roomr);
    printf("注销房间成功...\n");
    return 0;
}
//查看已有聊天室函数
int search_room_func(sqlite3 *my_db,const char *tablename){
    if(NULL==my_db || NULL==tablename){
        printf("入参失败,请检查...\n");
        return -1;
    }
    int i=0;
    int numb=0;
    int j=1;
    //回调函数
    int my_callback_func(void *data,int argc,char **argv,char **zazColName){
        //打印表头
        if((*(int *)data)++==0){
            printf("-");
            for(i=0;i<argc;i++){
                printf("%9s",zazColName[i]);
            }
            printf("-\n");
        }   
        printf("[");
        for(i=0;i<argc;i++){
            printf("%8s",argv[i]);
        }
        printf("]\n");
        return 0;
    }
    char *errmsg;
    char *sql=sqlite3_mprintf("select *from %q",tablename);
    //printf("%s\n",sql);
    int ret=sqlite3_exec(my_db,sql,&my_callback_func,&numb,&errmsg);
    if(SQLITE_OK!=ret){
        printf("数据查找失败:%s\n",errmsg);
        return -2;
    }
    sqlite3_free(sql);
    printf("查找完成...\n");
    return 0;
}
//查看聊天记录函数---针对所有聊天室
int search_room_history_func(){
    char pathname[1024]="history.txt";
    FILE *fp=fopen(pathname,"r");
    if(NULL==fp){
        perror("fopen error");
        return -1;
    }
    char buf[512]={0};
    while(fgets(buf,sizeof(buf),fp)!=NULL){
        printf("%s\n",buf);
    }
    return 0;
}
//客户机启动函数
int client_user_func(int socket_fd){
    //目标主机地址信息结构体
    struct sockaddr_in severInfo={.sin_family=AF_INET,.sin_addr=inet_addr("192.168.250.100"),.sin_port=htons(6666)};
    int socket_len=sizeof(severInfo);
    //链接目标主机
    int ret=connect(socket_fd,(struct sockaddr *)&severInfo,socket_len);
    if(-1==ret){
        perror("connect error:");
        return -1;
    }
    printf("TCP客户端启动\n");
    return 0;
}
1.2client_main.c
#include"client.h"
int main(int argc, char const *argv[])
{
    #if 1
    sqlite3 *my_database;
    user_t user;
    room_t room;
    int ret=0;
    printf("欢迎您使用网络聊天室...\n");
    ret=sqlite3_open("my_epoll_chat.db",&my_database);
    if(SQLITE_OK!=ret){
        printf("创建数据库失败...\n");
        exit(EXIT_FAILURE);
    }
    int option1=0;
    int option2=0;
    int option3=0;
    int value=0;
    char buf[4096]={0};
    while(true){
        print_menu_one_func();
        printf("请输入您的选择>>");
        scanf("%d",&option1);
        if(3==option1){//用户退出
            printf("欢迎您下次使用...\n");
            exit(EXIT_SUCCESS);
        }else if(2==option1){//用户注册
            //注册用户则将信息写入数据库保存
            //创建用户数据表
            char *sql1="create table if not exists user1(用户名 text primary key,密码 int)";
            create_tables_func(my_database,sql1);
            //将用户信息写入数据表
            insert_tables_user_func(my_database,user,"user1");
            printf("恭喜您,注册成功...\n");
            continue;
        }else if(1==option1){//用户登录
            //判断用户是否已注册
            //printf("1\n");
            value=register_user_func(my_database,&user,"user1");
            if(0!=value){
                continue;
            }
            while(true){
                print_menu_two_func();
                printf("请输入您的选择>>");
                scanf("%d",&option2);
                if(5==option2){//用户注销
                    delete_user_func(my_database,user,"user1");
                    printf("注销成功,期待您的再次使用...\n");
                    break;
                }else if(4==option2){//退出
                    break;
                }else if(3==option2){//查看已有聊天室
                    search_room_func(my_database,"room1");
                }else if(2==option2){//注册聊天室
                    //注册房间则将信息写入数据库保存
                    //创建房间数据表
                    char *sql2="create table if not exists room1(房间号 int primary key,密码 int)";
                    create_tables_func(my_database,sql2);
                    //将用户信息写入数据表
                    insert_tables_room_func(my_database,room,"room1");
                    printf("恭喜您,注册成功...\n");
                }else if(1==option2){//登录聊天室
                    //判断房间是否已注册
                    value=register_room_func(my_database,&room,"room1");
                    if(0!=value){
                        continue;
                    }
                    int socket_fd=socket(AF_INET,SOCK_STREAM,0);
                    if(-1==socket_fd){
                        perror("socket error");
                        exit(EXIT_FAILURE);
                    }
                    client_user_func(socket_fd);
                    while(true){
                        print_menu_three_func();
                        printf("请输入您的选择>>");
                        scanf("%d",&option3);
                        if(4==option3){//注销聊天室
                            delete_room_func(my_database,room,"room1"); 
                            printf("注销成功,期待您的再次使用...\n");
                            break;
                        }else if(3==option3){//退出
                            break;
                        }else if(2==option3){//聊天历史记录
                            //聊天历史记录由服务器记录,客户机查看
                            search_room_history_func();
                        }else if(1==option3){//聊天--客户机与服务器收发数据
                            while(true){
                                memset(buf,0,sizeof(buf));
                                //printf("1111\n");
                                printf("请输入要发送的数据>>");
                                fgets(buf,sizeof(buf),stdin);
                                //buf[strlen(buf)-1]='\0';
                                //printf("%s\n",buf);
                                //printf("2222\n");
                                int a=strcmp(buf,"quit\n");
                                //printf("%d\n",a);
                                if(!strcmp(buf,"quit\n")){
                                    printf("已退出聊天...\n");
                                    break;
                                }
                                int nbytes=write(socket_fd,buf,strlen(buf));
                                if(-1==nbytes){
                                    perror("write errro:");
                                    continue;
                                }
                                memset(buf,0,sizeof(buf));
                                nbytes=read(socket_fd,buf,sizeof(buf));
                                if(-1==nbytes){
                                    perror("read error:");
                                    exit(EXIT_FAILURE);
                                }
                                if(0==nbytes){
                                    printf("服务器已关闭\n");
                                    break;
                                }
                                printf("数据:%s\n",buf);
                            }
                        }
                    }
                    close(socket_fd);
                }
            }
        }
    }
    #else
    print_menu_one_func();
    print_menu_two_func();
    print_menu_three_func();
    #endif
    return 0;
}
1.3client.h
#ifndef _CLIENT_H
#define _CLIENT_H

#include<stdio.h>
#include<stdio.h>
#include <stdlib.h>
#include<sys/types.h>        
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<string.h>
#include<stdbool.h>
#include<sys/epoll.h>
#include<sqlite3.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

typedef struct _users{
    char name[256];
    int password;
}user_t;
typedef struct _rooms{
    int room_number;
    int password;
}room_t;

void print_menu_one_func();
void print_menu_two_func();
void print_menu_three_func();
int create_tables_func(sqlite3 *my_db,const char *sql);
int insert_tables_user_func(sqlite3 *my_db,user_t user,const char *tablename);
int judge_user_func(sqlite3 *my_db,user_t user,const char *tablename);
int delete_user_func(sqlite3 *my_db,user_t user,const char *tablename);
int register_user_func(sqlite3 *my_db,user_t *user,const char *tablename);
int insert_tables_room_func(sqlite3 *my_db,room_t room,const char *tablename);
int judge_room_func(sqlite3 *my_db,room_t room,const char *tablename);
int register_room_func(sqlite3 *my_db,room_t *room,const char *tablename);
int delete_room_func(sqlite3 *my_db,room_t room,const char *tablename);
int search_room_func(sqlite3 *my_db,const char *tablename);
int search_room_history_func();
int client_user_func(int socket_fd);

#endif

2.服务器

2.1sever.c
#include"sever.h"
//服务器启动函数
int sever_func(int socket_fd){
    //绑定套接字
    struct sockaddr_in severInfo={.sin_family=AF_INET,.sin_port=htons(6666),.sin_addr.s_addr=INADDR_ANY};
    int socket_len=sizeof(severInfo);
    int ret=bind(socket_fd,(struct sockaddr *)&severInfo,socket_len);
    if(-1==ret){
        perror("bind error");
        return -1;
    }
    //将套接字设为监听状态
    ret=listen(socket_fd,10);
    if(-1==ret){
        perror("listen error");
        return -1;
    }
    printf("epoll---TCP服务器启动\n");
    return 0;
}
//聊天历史记录函数
int create_user_history_func(const char *msg){
    if(NULL==msg){
        printf("入参失败,请检查...\n");
        return -1;
    }
    char pathname[1024]="history.txt";
    FILE *fp=fopen(pathname,"a+");
    if(NULL==fp){
        perror("fopen error");
        return -1;
    }
    int get_line(FILE *fp){
        int ret=0;
        int count=0;
        while((ret=fgetc(fp))!=-1){
            if(ret=='\n'){
                count++;
            }
        }
        return count;
    }
    static time_t sec=0;
    static time_t old_sec=0; 
    int line=get_line(fp);
    time(&sec);
    if(sec!=old_sec){
        struct tm *my_time=localtime(&sec);
        line++;
        //拼接用户信息
        fprintf(fp, "%d.%4d-%02d-%02d %02d:%02d:%02d---%s\n", line, \
            my_time->tm_year+1900, my_time->tm_mon+1, my_time->tm_mday, \
            my_time->tm_hour, my_time->tm_min, my_time->tm_sec,msg);
        fflush(fp);//手动刷新缓冲区
        old_sec = sec;
    }
}
2.2sever_main.c
#include"sever.h"
int main(int argc, char const *argv[])
{
    int socket_fd=socket(AF_INET,SOCK_STREAM,0);
    if(-1==socket_fd){
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    sever_func(socket_fd);
    struct sockaddr_in clientInfo={.sin_family=AF_INET};
    int client_len=sizeof(clientInfo);
    //创建文件描述符集合体
    int epoll_fd=epoll_create1(0);
    if(-1==epoll_fd){
        perror("epoll creat error");
        exit(EXIT_FAILURE);
    }
    //创建事件结构体
    struct epoll_event my_event={.events=EPOLLIN,.data.fd=socket_fd};
    //将关心的文件描述符放入事件实例中
    int ret=epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socket_fd,&my_event);
    if(-1==ret){
        perror("epoll ctl add error");
        exit(EXIT_FAILURE);
    }
    //创建就绪事件结构体数组
    struct epoll_event my_user_revents[2048]={0};
    int maxrevents=sizeof(my_user_revents)/sizeof(struct epoll_event);
    //定义聊天室最大人数为200人
    int my_fds=0;
    int connect_fds[200]={0};
    int nbtyes=0;
    int i=0,j=0;
    int connect_fd=0;
    char buf[512]={0};
    //进行数据循环收发
    while(true){
        int fds=epoll_wait(epoll_fd,my_user_revents,maxrevents,-1);
        if(-1==fds){
            perror("epoll_wait error");
            exit(EXIT_FAILURE);
        }
        for(i=0;i<fds;i++){
            //判断就绪事件类型
            if(socket_fd==my_user_revents[i].data.fd){
                connect_fd=accept(my_user_revents[i].data.fd,(struct sockaddr *)&clientInfo,&client_len);
                if(-1==connect_fd){
                    perror("accept error");
                    continue;
                }
                printf("客户机IP:%s 端口:%d\n",inet_ntoa(clientInfo.sin_addr),ntohs(clientInfo.sin_port));
                //将新产生的就绪事件放入实例中
                my_event.data.fd=connect_fd;
                my_event.events=EPOLLIN;
                epoll_ctl(epoll_fd,EPOLL_CTL_ADD,connect_fd,&my_event);
                //将客户链接产生的fd放入到已连接fd数组中
                if(my_fds<200){
                    connect_fds[my_fds]=connect_fd;
                    my_fds++;
                }else{
                    const char errmsg[1024]="聊天室人数已满,请选择其他聊天室...\n";
                    nbtyes=write(connect_fd,errmsg,sizeof(errmsg));
                    if(-1==nbtyes){
                        perror("write error");
                        continue;
                    }
                }
            }else{
                memset(buf,0,sizeof(buf));
                nbtyes=read(my_user_revents[i].data.fd,buf,sizeof(buf));
                if(-1==nbtyes){
                    perror("read error");
                    continue;
                }
                if(0==nbtyes){
                    printf("客户机已关闭\n");
                    //将此事件fd关闭
                    for(j=0;j<my_fds;j++){
                        if(connect_fds[j]==my_user_revents[i].data.fd){
                            my_fds--;
                            connect_fds[j]=connect_fds[my_fds];
                            connect_fds[my_fds]=-1 ;
                        }
                    }
                    //将就绪事件删除
                    epoll_ctl(epoll_fd,EPOLL_CTL_DEL,my_user_revents[i].data.fd,&my_user_revents[i]);
                    close(my_user_revents[i].data.fd);
                    continue;
                }
                buf[strlen(buf)-1]='\0';
                printf("客户端数据%s\n",buf);
                create_user_history_func(buf);
                for(j=0;j<my_fds;j++){
                    //不用向自己发送数据了
                    if(connect_fds[j]==my_user_revents[i].data.fd){
                        continue;
                    }
                    //向数组中所有套接字发送数据
                    nbtyes=write(connect_fds[j],buf,sizeof(buf));
                    if(-1==nbtyes){
                        perror("write error");
                        continue;
                    }
                }
            }
        }
    }
    return 0;
}
2.4sever.h
#ifndef _SEVER_H
#define _SEVER_H

#include<stdio.h>
#include<stdio.h>
#include <stdlib.h>
#include<sys/types.h>        
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<string.h>
#include<stdbool.h>
#include<sys/epoll.h>
#include<sqlite3.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

int sever_func(int socket_fd);
int create_user_history_func(const char *msg);

#endif

  • 26
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴耕雨读912

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值