0


LinuxC TCP实现简易聊天室


1.概述

1.1聊天室设计内容

1.用户管理

    实现用户注册,登录,找回密码

2.聊天室管理

    用户登录,本地添加删除好友,修改备注,注册vip,vip可以实现禁言,解除禁言,强制好友下线

3.聊天管理

    用户可以私发,群发消息,表情包或者文件,好友上线提醒

2.系统设计

2.1系统功能设计

网络聊天室系统分为服务器和客户端,客户端主要进行命令的发送,以及接受服务器发送过来的信息。服务器主要进行指令的解析并执行相关的函数,将执行的结果反馈给客户端。

2.1.1用户管理

用户管理要完成用户注册、登录、和找回密码。注册的信息存储在服务器的数据库中。数据库保存用户的昵称,账号,密码,权限,套接字,以及在线状态。当用户登录时,查询服务器数据库中的账号密码是否匹配,若是成功匹配,则登陆成功,否则登录失败。反馈客户端用户账号或者密码错误。

用户注册时,引导用户输入用户名,密码,密保以及答案,将注册信息存储到数据库中,若成功执行,则反馈给用户一个六位数的账号。

当用户湾及密码是,需要输入正确的账号,发送给服务器,服务器从数据中查找,找到对应账号则反馈给客户端密保问题,用户回答正确后反馈相应的密码。

当客户端退出程序时,向服务器发送下线数据,服务器更新相应的套接字以及在线状态。

2.1.2聊天室管理

用户登录聊天室需要输入正确的账号密码。登陆成功后服务器会发送给其好友,提醒好友已上线。用户可以选择私发,群发,注册会员,以及退出聊天。

2.1.3聊天管理

用户发送消息的情况有两种,分别是1、发送给私人。2、发送给多人。用户发送信息采用write命令,可以根据菜单选项确定用户发送的时那种模式。

1、私聊

发送给指定用户消息,提示用户输入私聊的对象的昵称或者备注,客户端检查用户是否有该对象好友,若没有,靠苏用户没有该好友,若有则发送消息,当用户发送私聊信息时,服务器检查发送方权限(是否被禁言),然后检查对方是否在线,若用户被禁言,则提示用户“您以被禁言”,若被发送方不在线,则提醒用户。

2、群聊

发送给指定的多个用户,原理同上。

2.2系统数据结构设计

1.注册登录选项结构体

选项结构体包option选项用来判断用户即将进行的操作,共用体存放的是对应操作需要的信息。

2.发送的消息结构体

struct file结构体用来存放文件大小,文件名字,需要发送的次数以及发送文件的buff。

2.3系统主要函数设计

2.3.1客户端

1.int main()

客户端主程序主要完成设置服务器地址,创建客户端流式套接字,创建好友数据库,表情包数据库。

2.user_options()

用户操作函数,根据用户对应的选项执行相应的操作

3.chat_start_c()

用户登陆成功后,创建读写线程,进行聊天,读线程根据用户的选项决定是消息,表情包还是文件的发送,读线程,对接受到的消息进行相应的处理。

2.3.2服务器

1.int main()

服务器主程序主要完成服务器地址的设置,创建服务器流式套接字,将地址结构和套接字绑定,设置侦听接口。每接收到一个客户端登录成功,就创建一个线程。

2.do_use_fd()

服务器根据客户端发送的消息进行相应的处理。

3.creat_sqlite()

创建数据库,建立注册信息表,以及聊天记录表,分别用于存放用户的账户信息以及聊天记录。

3.系统实现

系统环境:Ubuntu 18.04

gcc版本:7.50

编辑器:visual studio code version

3.2功能模块的程序流程图及运行界面

3.2.1功能模块流程图

1.客户端

2.写线程流程图

3.读线程流程图

4.服务器流程图

3.2.2运行界面

注册信息数据库:

图片太多就不全放了,这里只展示一部分功能。

4.源代码

4.1客户端

#ifndef _CHAT_C_H_
#define _CHAT_C_H_

#define _DEBUG_
#ifndef _DEBUG_
#define debug_msg(fmt, args...)
#else
#define debug_msg(fmt, args...) printf(fmt, ##args)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <netinet/in.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sqlite3.h>
#include <errno.h>

#define IPADDR "127.0.0.1"

#define TCP_PORT 8000
#define MAX_EVENTS 20
#define MSG_BUFFSIZE 100
#define PASSWORD_SIZE 10
#define NICKNAME_SIZE 10
#define SC_PRO 10
#define ANSWER 10
#define EXP_SIZE 10
#define FILE_S 1024
#define FILE_N 20
#define SQL_SIZE 300
#define MAX_GROUP 20
#define MUSHIN -1
#define ACCOUNT_ERROR -1
#define ANSWER_ERROR -2

#define CTRL1 30
#define CTRL2 30
#define CTRL3 18

#define SEARCH_ERROR "查找失败!!"
#define PED_ERROR "回答错误!!"
#define NO_FRIEND "查无此人!!"
#define PAY "支付"
#define PAY_SUCCED "支付成功"
#define PAY_FAILED "支付失败"
#define ERO_VIP "注册会员成功"
#define NOT_ONLINE -3
#define ONLINE -4
#define OFFLINE -5

enum option
{
    EXIT,
    ERO,
    LOG,
    FGPD
};
enum chatmode
{
    STOO = 1,
    STOA,
    ADD,
    DEL,
    VIP,
    MUS,
    CANCLE,
    OUT
};
enum chatoption
{
    MSG = 1,
    EXP,
    FS,
    GAME,
};

// 注册信息
struct enrollinfo
{
    char nickname[NICKNAME_SIZE];
    char password[PASSWORD_SIZE];
    char sc_protect[SC_PRO];
    char answer[ANSWER];
    int account;
};

// 登录信息
struct logininfo
{
    int account;
    char password[PASSWORD_SIZE];
};

// 注册登录选项包
struct options
{
    int option;
    union
    {
        struct enrollinfo eninfo;
        struct logininfo loginfo;
        int fgpwd_account;
    } info;
};

struct file
{
    int times;
    unsigned int file_size;
    char file_name[FILE_N];
    char file_buff[FILE_S];
};

// 聊天消息包
struct msg_buff
{
    int chat_mode;
    int my_account;            //自己账号
    int account[MAX_GROUP];          // 对方账号
    char op_nickname[NICKNAME_SIZE]; // 自己昵称
    int num;                         // 聊天人数
    int mushin_flag;     //禁言踢人标志位
    int option;
    union
    {
        struct file fileinfo;
        char chat_msg[MSG_BUFFSIZE];
    } chat_opt;
};

// 回调函数参数(套接口和数据库文件描述符)
struct cp
{
    int confd;
    int account;
    FILE *fp;
    sqlite3 *pdb;
};

// 创建数据库(好友信息(设置备注),账号)
int creat_sqlite(sqlite3 **pdb);
int creat_table_chat(sqlite3 *pdb);
int creat_table(sqlite3 *pdb, char *sql);

// 创建tcp/udp套接口
void create_tcp_client(int *sockfd, struct sockaddr_in *s_addr);
void create_udp_client(int *sockfd, struct sockaddr_in *s_addr);

// 菜单选项
void options_menu();
void enroll_menu();
void log_menu();
void chat_interface();
void mode();
void mode1();
void mode2();
void mode3();
void mode4();
void modevip();
void loging();

// 用户选择
void user_options(const int confd, sqlite3 *pdb);
// 查询权限
int sure_my_permision(int confd, int account);
// 操作
void insert_enroll_infomation(struct options *option);
void ero_account_c(int confd, struct options *option);

void select_friends(sqlite3 *pdb);
int display(void *para, int colcount, char **colvalue, char **colname);
// 登录
void log_opreation_c(int confd, struct options *option, sqlite3 *pdb);
void log_infomation(struct options *option);
void chat_start_c(int confd, struct options *option, sqlite3 *pdb);

// 找回密码
void get_password(int confd, struct options *option);

// 读写分离
void *read_thread(void *arg);
void *write_thread(void *arg);

void say_to_all(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp);
void say_to_one(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp);
void send_msg_c(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp);
void send_exp_c(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp);
void send_file_c(int confd, struct msg_buff *msg, sqlite3 *pdb);

int file_size(char *name);

int search_account(struct msg_buff *msg, char *name, sqlite3 *pdb);
void add_friends(int confd, struct msg_buff *msg, sqlite3 *pdb);
int insert_into_chat_friend(sqlite3 *pdb, int account, char *remarkname, char *nickname);
int delete_friend(sqlite3 *pdb);
int ero_vip(int confd, struct msg_buff *msg);
void mush_friend_cancle(int confd, struct msg_buff *msg, sqlite3 *pdb);

#endif
#include "chat_c.h"

void create_tcp_client(int *sockfd, struct sockaddr_in *s_addr)
{
    socklen_t s_len = sizeof(struct sockaddr_in);
    *sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(*sockfd < 0)
    {
        perror("sockfd creat error:");
        exit(1);
    }   

    bzero(s_addr, sizeof(struct sockaddr_in));
    (*s_addr).sin_family = AF_INET;
    (*s_addr).sin_port = htons(TCP_PORT);
    (*s_addr).sin_addr.s_addr = inet_addr(IPADDR);
}

int main()
{
    int sockfd_tcp;

    sqlite3 *pdb;

    struct sockaddr_in s_addr_tcp;

    // 创建数据库
    if (creat_sqlite(&pdb) != SQLITE_OK)
    {
        sqlite3_close(pdb);
        exit(1);
    }
    // 创建注册人员啊信息表和聊天记录表
    if (creat_table_chat(pdb) != SQLITE_OK)
    {
        sqlite3_close(pdb);
        exit(1);
    }

    create_tcp_client(&sockfd_tcp, &s_addr_tcp);

    if (connect(sockfd_tcp, (struct sockaddr *)&s_addr_tcp, sizeof(s_addr_tcp)) < 0)
    {
        perror("connect error");
        exit(1);
    }

    user_options(sockfd_tcp, pdb);

    close(sockfd_tcp);
    sqlite3_close(pdb);

    return 0;
}
#include "chat_c.h"

void user_options(const int confd, sqlite3 *pdb)
{
    int flag = 0;
    struct options option;

    do
    {
        memset(&option, 0, sizeof(option));
        if (flag == 0)
        {
            system("clear");
            flag = 1;
        }
        options_menu();
        printf("\033[%dC请选择:>   ", CTRL1 + 4);
        printf("\033[2D");
        scanf("%d", &option.option);
        getchar();
        switch (option.option)
        {
        case ERO:
            ero_account_c(confd, &option);
            break;

        case LOG:
            log_opreation_c(confd, &option, pdb);
            break;

        case FGPD:
            get_password(confd, &option);
            break;

        case EXIT:
            system("clear");
            option.option = EXIT;
            write(confd, &option, sizeof(option));
            break;

        default:
            printf("input error:\n");
            break;
        }
    } while (option.option);
    return;
}

void insert_enroll_infomation(struct options *option)
{
    char pswd_buff[10];

    enroll_menu();
    scanf("%s", option->info.eninfo.nickname);
    do
    {
        printf("\033[%dC\033[1B", CTRL1 + 15);
        scanf("%s", pswd_buff);
        printf("\033[%dC\033[1B", CTRL1 + 15);
        scanf("%s", option->info.eninfo.password);
        if (0 != strcmp(pswd_buff, option->info.eninfo.password))
        {
            printf("\033[4A");
        }
    } while (0 != strcmp(pswd_buff, option->info.eninfo.password));
    printf("\033[%dC\033[1B", CTRL1 + 15);
    scanf("%s", option->info.eninfo.sc_protect);
    printf("\033[%dC\033[1B", CTRL1 + 15);
    scanf("%s", option->info.eninfo.answer);
    return;
}

void ero_account_c(const int confd, struct options *option)
{
    int account;
    insert_enroll_infomation(option);
    write(confd, option, sizeof(struct options));
    read(confd, &account, sizeof(int));
    system("clear");
    printf("\033[4B\033[%dC你的账号是 :%d\033[4A\n", CTRL1 + 2, account);
    return;
}

void log_infomation(struct options *option)
{
    int i = 0;
    char a[20];
    log_menu();
    scanf("%d", &option->info.loginfo.account);
    getchar();
    printf("\033[1B\033[%dC", CTRL2 + 15);
    while (1)
    {
        system("stty -echo");
        a[i] = getchar();
        if (a[i] == '\n')
        {
            break;
        }
        fflush(stdin);
        system("stty echo");
        printf("*");
        fflush(stdout);
        i++;
    }
    system("stty echo");
    a[i] = '\0';
    strcpy(option->info.loginfo.password, a);
}

void log_opreation_c(int confd, struct options *option, sqlite3 *pdb)
{
    char buff[10];
    memset(buff, 0, sizeof(buff));

    log_infomation(option);

    write(confd, option, sizeof(struct options));
    read(confd, buff, sizeof(buff));

    if (0 == strcmp(buff, "failed"))
    {
        loging();
        printf("\033[2B\033[%dC登录失败!!\n", CTRL2 + 6);
        sleep(2);
        return;
    }
    if (0 == strcmp(buff, "success"))
    {
        loging();
        chat_start_c(confd, option, pdb);
    }
    return;
}

void get_password(int confd, struct options *option)
{
    char buff[100];
    memset(buff, 0, sizeof(buff));
    int account;
    printf("\033[%dC请输入账号:> \033[K", CTRL1 + 4);
    scanf("%d", &option->info.fgpwd_account);
    write(confd, option, sizeof(struct options));

    read(confd, buff, sizeof(buff));
    if (0 == strcmp(buff, SEARCH_ERROR))
    {
        printf("账号输入错误:\n");
        return;
    }
    printf("\033[%dC密保问题是:> %s\n", CTRL1 + 4, buff);

    printf("\033[%dC请输入答案:> \033[K", CTRL1 + 4);
    memset(buff, 0, sizeof(buff));
    scanf("%s", buff);
    write(confd, buff, sizeof(buff));
    memset(buff, 0, sizeof(buff));
    read(confd, buff, sizeof(buff));

    if (0 == strcmp(buff, PED_ERROR))
    {
        printf("答案错误!!!\n");
        printf("\033[%dC答案错误!!!\n", CTRL1 + 4);
    }
    else
        printf("\033[%dC你的密码是:> %s\n", CTRL1 + 4, buff);
    printf("\033[u");
}
#include "chat_c.h"

void chat_start_c(int confd, struct options *option, sqlite3 *pdb)
{
    pthread_t tid_read;
    pthread_t tid_write;

    FILE *fp = fopen("chat_record.text", "a");
    struct cp arg;
    memset(&arg, 0, sizeof(arg));
    arg.confd = confd;
    arg.pdb = pdb;
    arg.account = option->info.loginfo.account;
    arg.fp = fp;

    if (pthread_create(&tid_read, NULL, (void *)read_thread, (void *)&arg) < 0)
    {
        perror("thread read error:");
        exit(1);
    }
    if (pthread_create(&tid_write, NULL, (void *)write_thread, (void *)&arg) < 0)
    {
        perror("thread read error:");
        exit(1);
    }

    pthread_join(tid_write, NULL);
    pthread_cancel(tid_read);
    return;
}
#include "chat_c.h"

int creat_sqlite(sqlite3 **pdb)
{
    int ret;
    if ((ret = sqlite3_open("chat_room_c.db", pdb)) != SQLITE_OK)
    {
        perror("creat sqlite error");
        exit(1);
    }
    return SQLITE_OK;
}

int creat_table_chat(sqlite3 *pdb)
{
    char *sql = "create table if not exists chat_friend   (Account integer primary key,         \
                                                            Nickname text     NOT NULL,          \
                                                            Remarkname text       );";

    char *sql1 = "create table if not exists expression   (id integer primary key,exp text);";

    if (creat_table(pdb, sql) != SQLITE_OK)
    {
        debug_msg("creat table account error\n");
        return -1;
    }

    if (creat_table(pdb, sql1) != SQLITE_OK)
    {
        debug_msg("creat table expression error\n");
        return -1;
    }
    return SQLITE_OK;
}

int creat_table(sqlite3 *pdb, char *sql)
{
    int ret;

    char *errmsg = NULL;

    if ((ret = sqlite3_exec(pdb, sql, NULL, NULL, &errmsg)) != SQLITE_OK)
    {
        perror("creat table error:");
        return -1;
    }

    return SQLITE_OK;
}

int insert_into_chat_friend(sqlite3 *pdb, int account, char *remarkname, char *nickname)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;

    sprintf(sql, "insert OR IGNORE into  chat_friend   (Account,Nickname,Remarkname) values (%d,'%s','%s');",
            account, nickname, remarkname);

    if (SQLITE_OK != sqlite3_exec(pdb, sql, NULL, NULL, &errmsg))
    {
        printf("insert friend error:%s\n", errmsg);
        sqlite3_free(errmsg);
        return -1;
    }
    return 0;
}
#include "chat_c.h"

void *write_thread(void *arg)
{
    int flag = 0;
    int bytes_send;
    int confd;
    int account;

    FILE *fp;

    sqlite3 *pdb;

    struct cp args;
    memset(&args, 0, sizeof(args));
    args = *((struct cp *)arg);
    confd = args.confd;
    pdb = args.pdb;
    fp = args.fp;
    struct msg_buff msg;

    system("clear");

    // 查看自己是否是vip
    chat_interface();
    if (sure_my_permision(confd, args.account) > 1)
    {
        flag = 1;
    }
    mode4();
    do
    {
        if (1 == flag)
        {
            modevip();
        }
        memset(&msg, 0, sizeof(msg));
        msg.my_account = args.account;

        select_friends(pdb);
        printf("\033[u");
        scanf("%d", &msg.chat_mode);
        getchar();
        switch (msg.chat_mode)
        {
        case STOO:
            mode1();
            say_to_one(confd, &msg, pdb, fp);
            break;
        case STOA:
            mode1();
            say_to_all(confd, &msg, pdb, fp);
            break;
        case ADD:
            add_friends(confd, &msg, pdb);
            break;
        case DEL:
            if (delete_friend(pdb) != 0)
            {
                printf("删除好友失败\n");
            }
            break;
        case VIP:
            if (0 == ero_vip(confd, &msg))
            {
                modevip();
            }
            break;
        case MUS:
            mush_friend_cancle(confd, &msg, pdb);
            break;
        case CANCLE:
            mush_friend_cancle(confd, &msg, pdb);
            break;
        case OUT:
            mush_friend_cancle(confd, &msg, pdb);
            break;
        case EXIT:
            system("clear");
            msg.chat_mode = EXIT;
            write(confd, &msg, sizeof(msg));
            fclose(fp);
            exit(0);
        default:
            break;
        };
    } while (msg.chat_mode);

    return NULL;
}

void say_to_all(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp)
{
    do
    {
        mode();
        printf("\033[u");
        scanf("%d", &msg->option);
        switch (msg->option)
        {
        case MSG:
            send_msg_c(confd, msg, pdb, fp);
            break;
        case EXP:
            send_exp_c(confd, msg, pdb, fp);
            break;
        case FS:
            send_file_c(confd, msg, pdb);
            break;
        case EXIT:
            system("clear");
            chat_interface();
            mode2();
            mode4();
            break;
        default:
            break;
        }

    } while (msg->option);
    return;
}

void say_to_one(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp)
{
    char buff[100];
    do
    {
        mode();
        printf("\033[u");
        scanf("%d", &msg->option);
        switch (msg->option)
        {
        case MSG:
            send_msg_c(confd, msg, pdb, fp);
            break;
        case EXP:
            send_exp_c(confd, msg, pdb, fp);
            break;
        case FS:
            send_file_c(confd, msg, pdb);
            break;
        case EXIT:
            system("clear");
            chat_interface();
            mode2();
            mode4();
            break;
        default:
            break;
        }

    } while (msg->option);
    return;
}

// 客户端发送消息
void send_msg_c(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp)
{
    int ret;
    char name[NICKNAME_SIZE];
    char record[200];
    char buff[100];

    memset(buff, 0, sizeof(buff));
    memset(record, 0, sizeof(record));

    msg->num = 1;

    if (msg->chat_mode == STOO)
    {
        mode3();
        printf("\033[2B\033[10D请输入聊天好友:>\033[K");
        memset(name, 0, sizeof(name));
        scanf("%s", name);
        ret = search_account(msg, name, pdb);
        if (ret < 1)
        {
            return;
        }
        msg->account[0] = ret;
        printf("\033[u");
        scanf("%s", msg->chat_opt.chat_msg);
        write(confd, msg, sizeof(struct msg_buff));
        sprintf(record, "%s %s 我-->%s:%s\n", __DATE__, __TIME__, name, msg->chat_opt.chat_msg);
        fputs(record, fp);
    }
    else if (msg->chat_mode == STOA)
    {
        mode3();
        int num;
        printf("\033[2B\033[10D请输入群聊人数:>\033[K");
        scanf("%d", &num);

        msg->num = num;
        for (int i = 0; i < num; i++)
        {
            memset(name, 0, sizeof(name));
            printf("\033[%dC请输入聊天好友:>\033[K", CTRL3 + 2);
            scanf("%s", name);
            ret = search_account(msg, name, pdb);
            if (ret < 1)
            {
                return;
            }
            msg->account[i] = ret;
        }

        printf("\033[u");
        scanf("%s", msg->chat_opt.chat_msg);
        write(confd, msg, sizeof(struct msg_buff));
        sprintf(record, "%s %s 我-->群发:%s\n", __DATE__, __TIME__, msg->chat_opt.chat_msg);
        fputs(record, fp);
    }

    mode4();
    return;
}

// 显示表情库
void show_explib(sqlite3 *pdb)
{
    char *sql = "select * from expression;";
    char *errmsg = NULL;
    char **result = NULL;
    int row, col;

    if (sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg) != SQLITE_OK)
    {
        printf("%s\n", errmsg);
        sqlite3_free(errmsg);
        return;
    }
    printf("\033[u\033[10A\033[12D");
    for (int i = 2; i < (row * col) + 2; i++)
    {
        printf("%-4s", result[i]);
        if ((i + 1) % 2 == 0)
        {
            if ((i - 1) % 4 == 0)
            {
                printf("\n");
                printf("\033[%dC", CTRL3 + 4);
            }
            else
                printf("  ");
        }
    }
    printf("\n");
    printf("\033[u");
}

// 查询本地表情库
int find_exp(struct msg_buff *msg, int id, sqlite3 *pdb)
{
    int row, col;
    char *errmsg = NULL;
    char sql[SQL_SIZE];
    char **presult = NULL;

    sprintf(sql, "select exp from expression where id = %d;", id);

    if (sqlite3_get_table(pdb, sql, &presult, &row, &col, &errmsg) != SQLITE_OK)
    {
        debug_msg("find exp error:%s\n", errmsg);
        printf("查找失败:\n");
        sqlite3_free(errmsg);
        return -1;
    }
    if (0 == row)
    {
        sqlite3_free_table(presult);
        printf("输入错误\n");
        return -1;
    }
    else
    {
        strcpy(msg->chat_opt.chat_msg, presult[1]);
        sqlite3_free_table(presult);
        return 0;
    }
}

// 发送表情
void send_exp_c(int confd, struct msg_buff *msg, sqlite3 *pdb, FILE *fp)
{
    int ret;
    char name[NICKNAME_SIZE];
    char buff[100];
    char record[200];
    int id;

    memset(buff, 0, sizeof(buff));
    memset(record, 0, sizeof(record));
    msg->num = 1;

    if (msg->chat_mode == STOO)
    {
        mode3();
        printf("\033[2B\033[10D请输入聊天好友:>\033[K");
        memset(name, 0, sizeof(name));
        scanf("%s", name);
        ret = search_account(msg, name, pdb);
        if (ret < 1)
        {
            return;
        }
        msg->account[0] = ret;

        show_explib(pdb);
        mode4();
        scanf("%d", &id);
        find_exp(msg, id, pdb);
        write(confd, msg, sizeof(struct msg_buff));
        sprintf(record, "%s %s 我-->%s:%s\n", __DATE__, __TIME__, name, msg->chat_opt.chat_msg);
        fputs(record, fp);
    }
    else if (msg->chat_mode == STOA)
    {
        mode3();
        int num;
        printf("\033[2B\033[10D请输入群聊人数:>\033[K");
        scanf("%d", &num);

        msg->num = num;
        for (int i = 0; i < num; i++)
        {
            memset(name, 0, sizeof(name));
            printf("\033[%dC请输入聊天好友:>\033[K", CTRL3 + 2);
            scanf("%s", name);
            ret = search_account(msg, name, pdb);
            if (ret < 1)
            {
                return;
            }
            msg->account[i] = ret;
        }
        show_explib(pdb);
        mode4();
        scanf("%d", &id);
        find_exp(msg, id, pdb);
        write(confd, msg, sizeof(struct msg_buff));
        sprintf(record, "%s %s 我-->群发:%s\n", __DATE__, __TIME__, msg->chat_opt.chat_msg);
        fputs(record, fp);
    }
    mode4();
    return;
}

// 查找联系人列表的账号通过备注
int search_account(struct msg_buff *msg, char *name, sqlite3 *pdb)
{
    int row, col;
    int account;
    char *errmsg = NULL;
    char sql[SQL_SIZE];
    char **presult = NULL;

    sprintf(sql, "select Account from chat_friend where Remarkname = '%s' OR Nickname = '%s';", name, name);

    if (sqlite3_get_table(pdb, sql, &presult, &row, &col, &errmsg) != SQLITE_OK)
    {
        debug_msg("write thread %d   %s\n", __LINE__, errmsg);
        printf("查找失败:\n");
        sqlite3_free(errmsg);
        return -1;
    }
    if (0 == row)
    {
        sqlite3_free_table(presult);
        printf("名字输入错误:\n");
        return -1;
    }
    else
    {
        account = atoi(presult[1]); // presult[0] 是字段名
        sqlite3_free_table(presult);
        return account;
    }
}

// 添加好友,单方面添加
void add_friends(int confd, struct msg_buff *msg, sqlite3 *pdb)
{
    printf("\033[u");
    char buff[100];
    char remarkname[NICKNAME_SIZE];
    memset(buff, 0, sizeof(buff));
    printf("\033[2B\033[10D请输入好友账号:>\033[K");
    scanf("%d", &msg->account[0]);

    write(confd, msg, sizeof(struct msg_buff));
    read(confd, buff, sizeof(buff));
    if (0 == strcmp(buff, NO_FRIEND))
    {
        printf(NO_FRIEND);
        printf("\n");
        return;
    }
    printf("\033[%dC您添加的好友昵称为%s\n", CTRL3 + 6, buff);
    printf("\033[%dC设置备注:>\033[K", CTRL3 + 6);
    scanf("%s", remarkname);
    if (insert_into_chat_friend(pdb, msg->account[0], remarkname, buff) < 0)
    {
        printf("\033[%dC添加好友失败!!\n", CTRL3 + 6);
    }
    else
    {
        printf("\033[%dC添加好友成功!!\n", CTRL3 + 6);
    }
}

// 删除好友   单方面删除,对面无法知道
int delete_friend(sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;
    char name[NICKNAME_SIZE];

    printf("请输入要删除的好友:>\033[K");
    scanf("%s", name);

    sprintf(sql, "delete from  chat_friend  where Remarkname = '%s';", name);

    if (SQLITE_OK != sqlite3_exec(pdb, sql, NULL, NULL, &errmsg))
    {
        printf("delete friend error:%s\n", errmsg);
        sqlite3_free(errmsg);
        return -1;
    }
    return 0;
}

// 注册vip
int ero_vip(int confd, struct msg_buff *msg)
{
    char buff[100];

    int money;

    memset(buff, 0, sizeof(buff));

    printf("\033[u");

    msg->mushin_flag = VIP;
    write(confd, msg, sizeof(struct msg_buff));
    read(confd, buff, sizeof(buff));
    if (0 != strcmp(buff, PAY))
    {
        printf("注册失败\n");
        return -1;
    }
    printf("\033[2B\033[10DC请支付15元:>\033[K");
    scanf("%d", &money);
    if (money == 15)
    {
        write(confd, PAY_SUCCED, sizeof(PAY_SUCCED));
        read(confd, buff, sizeof(buff));
        printf("\033[%dC%s\n", CTRL3 + 2, buff);
        printf("\033[u");
    }
    else
    {
        write(confd, PAY_FAILED, sizeof(PAY_FAILED));
        printf("\033[%dC注册会员失败!!\n", CTRL3 + 2);
        printf("\033[u");
        return -1;
    }
    return 0;
}

// 显示好友列表
void select_friends(sqlite3 *pdb)
{
    char *sql = "select Remarkname,account from chat_friend;";
    char *errmsg = NULL;
    char **result = NULL;
    int row, col;

    if (sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg) != SQLITE_OK)
    {
        printf("%s\n", errmsg);
        sqlite3_free(errmsg);
        return;
    }
    printf("\033[s");
    printf("\033[17A");
    printf("\033[%dC", CTRL3 + 18);
    for (int i = 2; i < (row * col) + 2; i++)
    {
        printf("%-7s", result[i]);
        if ((i + 1) % 2 == 0)
        {
            printf("\n");
            printf("\033[%dC", CTRL3 + 52);
        }
    }
    printf("\n");
    printf("\033[u");
}

// 通过服务器查询自己的权限,更新界面
int sure_my_permision(int confd, int account)
{
    int per;
    write(confd, &account, sizeof(int));
    read(confd, &per, sizeof(per));
    return per;
}

// 具有禁言解除禁言踢人操作功能
void mush_friend_cancle(int confd, struct msg_buff *msg, sqlite3 *pdb)
{
    char name[NICKNAME_SIZE];
    char buff[100];
    int ret;

    memset(buff, 0, sizeof(buff));

    printf("\033[u\033[2B\033[10D");
    printf("请输入操作的好友:>\033[K");
    scanf("%s", name);

    if ((ret = search_account(msg, name, pdb)) < 0)
    {
        printf("\033[%dC查无此人!!]", CTRL3 + 2);
        printf("\033[u");
        return;
    }

    msg->account[0] = ret;
    write(confd, msg, sizeof(struct msg_buff));
    read(confd, buff, sizeof(buff));
    printf("\033[%dC%s!!", CTRL3 + 6, buff);
    printf("\033[u");
}

void send_file_c(int confd, struct msg_buff *msg, sqlite3 *pdb)
{
    int ret;
    int bytes_read;
    char name[NICKNAME_SIZE];

    struct stat info;

    msg->num = 1;

    if (msg->chat_mode == STOO)
    {
        mode3();
        printf("\033[2B\033[10D请输入聊天好友:>\033[K");
        memset(name, 0, sizeof(name));
        scanf("%s", name);
        ret = search_account(msg, name, pdb);
        if (ret < 1)
        {
            return;
        }
        msg->account[0] = ret;
        printf("\033[u");
        printf("\033[2B\033[10D请输入文件名:>\033[K");

        scanf("%s", msg->chat_opt.fileinfo.file_name);
        if ((ret = file_size(msg->chat_opt.fileinfo.file_name)) < 0)
        {
            debug_msg("file   %d\n", ret);
            sleep(5);
            return;
        }
        msg->chat_opt.fileinfo.file_size = ret;
        FILE *fp = fopen(msg->chat_opt.fileinfo.file_name, "r");

        if (ret % FILE_S == 0)
            msg->chat_opt.fileinfo.times = ret / FILE_S;
        else
            msg->chat_opt.fileinfo.times = ret / FILE_S + 1;

        while (msg->chat_opt.fileinfo.times)
        {
            msg->chat_opt.fileinfo.times--;
            bytes_read = fread(msg->chat_opt.fileinfo.file_buff, FILE_S, 1, fp);
            write(confd, msg, sizeof(struct msg_buff));
        }

        fclose(fp);
    }
    else if (msg->chat_mode == STOA)
    {
        mode3();
        int num;
        printf("\033[2B\033[10D请输入群聊人数:>\033[K");
        scanf("%d", &num);

        msg->num = num;
        for (int i = 0; i < num; i++)
        {
            memset(name, 0, sizeof(name));
            printf("\033[%dC请输入聊天好友:>\033[K", CTRL3 + 2);
            scanf("%s", name);
            ret = search_account(msg, name, pdb);
            if (ret < 1)
            {
                return;
            }
            msg->account[i] = ret;
        }

        printf("\033[2B\033[10D请输入文件名:>\033[K");

        scanf("%s", msg->chat_opt.fileinfo.file_name);
        if ((ret = file_size(msg->chat_opt.fileinfo.file_name)) < 0)
        {
            return;
        }
        msg->chat_opt.fileinfo.file_size = ret;
        FILE *fp = fopen(msg->chat_opt.fileinfo.file_name, "r");
        if (ret % FILE_S == 0)
            msg->chat_opt.fileinfo.times = ret / FILE_S;
        else
            msg->chat_opt.fileinfo.times = ret / FILE_S + 1;

        while (msg->chat_opt.fileinfo.times)
        {
            bytes_read = fread(msg->chat_opt.fileinfo.file_buff, FILE_S, 1, fp);
            write(confd, msg, sizeof(struct msg_buff));
            msg->chat_opt.fileinfo.times--;
        }

        fclose(fp);
    }

    mode4();
}

int file_size(char *name)
{
    struct stat info;
    // 查看文件大小
    int ret = stat(name, &info);
    if (ret < 0)
    {
        perror("stat error:");
        return -1;
    }
    return info.st_size;
}
#include "chat_c.h"

// 查找是否是好友上线
int search_friend_name(char *name, int account, sqlite3 *pdb)
{
    int row, col;
    char *errmsg = NULL;
    char sql[SQL_SIZE];
    char **presult = NULL;

    sprintf(sql, "select Remarkname from chat_friend where Account = %d;", account);

    if (sqlite3_get_table(pdb, sql, &presult, &row, &col, &errmsg) != SQLITE_OK)
    {
        debug_msg("write thread %d   %s\n", __LINE__, errmsg);
        printf("查找失败:\n");
        sqlite3_free(errmsg);
        return -1;
    }
    if (0 == row)
    {
        return -1;
    }
    else
    {
        strcpy(name, presult[1]);
        sqlite3_free_table(presult);
        return 0;
    }
}
// 查找是谁发的消息
int search_who_send(char *name, int account, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char **result = NULL;
    char *errmsg = NULL;

    int row, col;

    sprintf(sql, "select Remarkname from chat_friend where Account = %d;", account);

    if (SQLITE_OK != sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg))
    {
        printf("error :%s\n", errmsg);
        sqlite3_free(errmsg);
        return -1;
    }
    if (0 == row)
    {
        strcpy(name, "someone");
    }
    else if (1 == row)
    {
        strcpy(name, result[1]);
    }
    sqlite3_free_table(result);
    return SQLITE_OK;
}

// 接收文件
void accept_file(struct msg_buff *msg, sqlite3 *pdb)
{
    int bytes_write;
    int ret;
    char name[NICKNAME_SIZE];
    // 查找是谁发的消息
    if (search_who_send(name, msg->my_account, pdb) < 0)
    {
        printf("error:\n");
    }
    printf("\033[u");
    printf("\033[16A\033[16D收到%s的文件:%s(%d)\n", name, msg->chat_opt.fileinfo.file_name,
           msg->chat_opt.fileinfo.file_size);
    FILE *fp = fopen("accept.text", "w+");
    bytes_write = fwrite(msg->chat_opt.fileinfo.file_buff, FILE_S, 1, fp);
    if (msg->chat_opt.fileinfo.times == 0)
    {
        printf("\033[u\033[2B\033[13D接收完毕!!\n");
    }
    fclose(fp);
    printf("\033[3A\033[%dC", CTRL3 + 16);
}

void read_msg(struct msg_buff *msg, sqlite3 *pdb, FILE *fp)
{

    char record[200];
    memset(record, 0, sizeof(record));
    char name[NICKNAME_SIZE];
    // 查找是谁发的消息
    if (search_who_send(name, msg->my_account, pdb) < 0)
    {
        printf("error:\n");
    }
    printf("\033[u");
    printf("\033[16A\033[13D%s向你发送了:%s\n", name, msg->chat_opt.chat_msg);
    sprintf(record, "%s %s %s-->我:%s\n", __DATE__, __TIME__, name, msg->chat_opt.chat_msg);
    fputs(record, fp);
    printf("\033[15B\033[%dC", CTRL3 + 16);
}

void read_choice(struct msg_buff *msg, sqlite3 *pdb, FILE *fp)
{
    switch (msg->option)
    {
    case MSG:
        read_msg(msg, pdb, fp);
        break;
    case EXP:
        read_msg(msg, pdb, fp);
        break;
    case FS:
        accept_file(msg, pdb);
        break;

    default:
        break;
    }
}

void *read_thread(void *arg)
{
    int bytes_recv;
    int confd;
    int choice;
    int account;
    struct sockaddr_in s_addr;
    socklen_t s_len = sizeof(s_addr);

    sqlite3 *pdb;
    FILE *fp;

    struct cp args = *((struct cp *)arg);

    confd = args.confd;
    pdb = args.pdb;
    fp = args.fp;
    struct msg_buff msg;

    while (1)
    {
        if ((bytes_recv = read(confd, &msg, sizeof(msg))) != sizeof(msg))
        {
            continue;
        }
        if (msg.mushin_flag == MUSHIN)
        {
            printf("\033[16A\033[11D您已经被禁言!!!\n");
            printf("\033[u");
            continue;
        }
        else if (msg.mushin_flag == -2)
        {
            fclose(fp);
            system("clear");
            exit(0);
        }
        else if (msg.mushin_flag == NOT_ONLINE)
        {
            printf("\033[16A\033[11D对方不在线!!!\n");
            printf("\033[u");
        }
        else if (msg.mushin_flag == ONLINE)
        {
            char name[NICKNAME_SIZE];
            search_friend_name(name, msg.my_account, pdb);
            printf("\033[4A\033[36C%s上线了\n", name);
            printf("\033[u");
        }
        else if (msg.mushin_flag == OFFLINE)
        {
            char name[NICKNAME_SIZE];
            search_friend_name(name, msg.my_account, pdb);
            printf("\033[4A\033[36C%s下线了\n", name);
            printf("\033[u");
        }
        else
        {
            read_choice(&msg, pdb, fp);
        }
    }
}
#include "chat_c.h"

void options_menu()
{
    printf("\033[s");
    printf("\n\n\n\n");
    printf("\033[%dC______________________\n", CTRL1);
    printf("\033[%dC|                    |\n", CTRL1);
    printf("\033[%dC|                    |\n", CTRL1);
    printf("\033[%dC|     1.注    册     |\n", CTRL1);
    printf("\033[%dC|                    |\n", CTRL1);
    printf("\033[%dC|     2.登    录     |\n", CTRL1);
    printf("\033[%dC|                    |\n", CTRL1);
    printf("\033[%dC|     3.忘记密码     |\n", CTRL1);
    printf("\033[%dC|                    |\n", CTRL1);
    printf("\033[%dC|     0.退    出     |\n", CTRL1);
    printf("\033[%dC|                    |\n", CTRL1);
    printf("\033[%dC|                    |\n", CTRL1);
    printf("\033[%dC|____________________|\n", CTRL1);
    printf("\n");
}

void enroll_menu()
{
    system("clear");
    printf("\n\n\n");
    printf("\033[%dC___________________________\n", CTRL1);
    printf("\033[%dC|                         |\n", CTRL1);
    printf("\033[%dC|                         |\n", CTRL1);
    printf("\033[%dC|  请输入昵称:           |\n", CTRL1);
    printf("\033[%dC|                         |\n", CTRL1);
    printf("\033[%dC|  请输入密码:           |\n", CTRL1);
    printf("\033[%dC|                         |\n", CTRL1);
    printf("\033[%dC|  请确认密码:           |\n", CTRL1);
    printf("\033[%dC|                         |\n", CTRL1);
    printf("\033[%dC|  密保问题  :            |\n", CTRL1);
    printf("\033[%dC|                         |\n", CTRL1);
    printf("\033[%dC|  密保答案  :            |\n", CTRL1);
    printf("\033[%dC|                         |\n", CTRL1);
    printf("\033[%dC|_________________________|\n", CTRL1);
    printf("\033[11A\033[%dC", CTRL1 + 15);
}
void log_menu()
{
    system("clear");
    printf("\n\n\n\n\n");
    printf("\033[%dC_______________________________\n", CTRL2);
    printf("\033[%dC|                             |\n", CTRL2);
    printf("\033[%dC|                             |\n", CTRL2);
    printf("\033[%dC|            聊天室           |\n", CTRL2);
    printf("\033[%dC|                             |\n", CTRL2);
    printf("\033[%dC|  请输入账号:               |\n", CTRL2);
    printf("\033[%dC|                             |\n", CTRL2);
    printf("\033[%dC|  请输入密码:               |\n", CTRL2);
    printf("\033[%dC|                             |\n", CTRL2);
    printf("\033[%dC|                             |\n", CTRL2);
    printf("\033[%dC|_____________________________|\n", CTRL2);
    printf("\033[6A\033[%dC", CTRL2 + 15);
}

void chat_interface()
{
    printf("\n\n\n\n\n");
    printf("\033[%dC%s  %s\n", CTRL3, __DATE__, __TIME__);
    printf("\033[%dC—————————————————————————————————————————————————————————————————————\n", CTRL3);
    printf("\033[%dC|                            |                 |     好友列表       |\n", CTRL3);
    printf("\033[%dC|                            |    \033[31m会员大放送\33[37m   |                    |\n", CTRL3);
    printf("\033[%dC|                            |                 |                    |\n", CTRL3);
    printf("\033[%dC|                            |     超值会员    |                    |\n", CTRL3);
    printf("\033[%dC|                            |                 |                    |\n", CTRL3);
    printf("\033[%dC|                            |     让你拥有    |                    |\n", CTRL3);
    printf("\033[%dC|                            |      帝王般     |                    |\n", CTRL3);
    printf("\033[%dC|                            |      的权利     |                    |\n", CTRL3);
    printf("\033[%dC|                            |                 |                    |\n", CTRL3);
    printf("\033[%dC|                            |      禁言       |                    |\n", CTRL3);
    printf("\033[%dC|                            |      踢人       |                    |\n", CTRL3);
    printf("\033[%dC|                            |                 |                    |\n", CTRL3);
    printf("\033[%dC|----------------------------|    你值得拥有   |                    |\n", CTRL3);
    printf("\033[%dC|    1.私聊      2.群聊      |   还在等什么呢  |                    |\n", CTRL3);
    printf("\033[%dC|    3.加好友    4.删好友    |    赶快行动吧   |                    |\n", CTRL3);
    printf("\033[%dC|    5.注册会员  0.退出      |                 |                    |\n", CTRL3);
    printf("\033[%dC---------------------------------------------------------------------\n", CTRL3);
    printf("\033[%dC|                                                                   |\n", CTRL3);
    printf("\033[%dC|                                                                   |\n", CTRL3);
    printf("\033[%dC—————————————————————————————————————————————————————————————————————\n", CTRL3);
    printf("\033[2A\033[%dC", CTRL3 + 16);
    printf("\033[s");
    return;
}
void modevip()
{
    printf("\033[u");
    printf("\033[1A\033[11D\033[31m6.禁言    7.解除禁言     8.踢人\033[37m\n");
    printf("\033[u");
}
void mode()
{
    printf("\033[u");
    printf("\033[1A\033[11D                                 \n");
    printf("\033[u");
}
void mode1()
{
    printf("\033[u");
    printf("\033[5A\033[12D1.发消息   2.发表情\n");
    printf("\033[%dC                        \n", CTRL3 + 4);
    printf("\033[%dC3.发文件   0.退出 \n", CTRL3 + 4);
    printf("\033[u");
}

void mode2()
{
    printf("\033[u");
    printf("\033[5A\033[12D1.私聊     2.群聊  \n");
    printf("\033[%dC3.加好友   4.删好友 \n", CTRL3 + 4);
    printf("\033[%dC5.注册会员 0.退出   \n", CTRL3 + 4);
    printf("\033[u");
}
void mode3()
{
    printf("\033[u\033[11D");
    printf("消息内容:[                                                ]\n");
    printf("\033[u");
}
void mode4()
{
    printf("\033[u\033[11D");
    printf("  请选择:>                                                 \n");
    printf("\033[u");
}
void loging()
{
    int i;
    printf("\033[?25l");                              //隐藏光标
    printf("\033[2B\033[13D正在登入:");
    fflush(stdout);
    for (i = 0; i < 10; i++)
    {
        printf("*");
        fflush(stdout);
        usleep(200000);
    }
    printf("\033[?25h");
    printf("\n");
}

4.2服务器

#ifndef _CHAT_S_H_
#define _CHAT_S_H_

#define _DEBUG_
#ifndef _DEBUG_
#define debug_msg(fmt, args...)
#else
#define debug_msg(fmt, args...) printf(fmt, ##args)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <netinet/in.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>

#define TCP_PORT 8000
#define MAX_EVENTS 20
#define MSG_BUFFSIZE 100
#define PASSWORD_SIZE 10
#define NICKNAME_SIZE 10
#define SC_PRO 10
#define ANSWER 10
#define EXP_SIZE 10
#define LOG_SUCCESS 1
#define FILE_S 1024
#define FILE_N 20
#define SQL_SIZE 300
#define MAX_GROUP 20
#define MUSHIN -1
#define ACCOUNT_ERROR -1
#define ANSWER_ERROR -2

#define SEARCH_ERROR "查找失败!!"
#define PED_ERROR "回答错误!!"
#define NO_FRIEND "查无此人!!"
#define PAY "支付"
#define PAY_SUCCED "支付成功"
#define PAY_FAILED "支付失败"
#define ERO_VIP "注册会员成功"
#define NOT_ONLINE  -3
#define ONLINE -4
#define OFFLINE -5

enum option
{
    EXIT,
    ERO,
    LOG,
    FGPD
};

enum chatmode
{
    STOO = 1,
    STOA,
    ADD,
    DEL,
    VIP,
    MUS,
    CANCLE,
    OUT
};
enum chatoption
{
    MSG = 1,
    EXP,
    FS,
    GAME,
};

// 注册信息
struct enrollinfo
{
    char nickname[NICKNAME_SIZE];
    char password[PASSWORD_SIZE];
    char sc_protect[SC_PRO];
    char answer[ANSWER];
    int account;
};

// 登录信息
struct logininfo
{
    int account;
    char password[PASSWORD_SIZE];
};

// 选项结构体
struct options
{
    int option;
    union
    {
        struct enrollinfo eninfo;
        struct logininfo loginfo;
        int fgpwd_account;    //找回密码标志位
    } info;
};

struct file
{
    int times;                        //发送次数
    unsigned int file_size;           //文件大小
    char file_name[FILE_N];           //文件名字
    char file_buff[FILE_S];           //缓冲区
};

// 聊天数据结构体
struct msg_buff
{
    int chat_mode;                   //聊天模式(私聊、群聊)
    int my_account;                  //自己账号
    int account[MAX_GROUP];          // 对方账号
    char op_nickname[NICKNAME_SIZE]; // 自己昵称
    int num;                         // 聊天人数
    int mushin_flag;     //标志位
    int option;
    union
    {
        struct file fileinfo;
        char chat_msg[MSG_BUFFSIZE];
    } chat_opt;
};

struct arg
{
    int fd;
    sqlite3 *pdb;
};

// 创建数据库
int creat_sqlite(sqlite3 **pdb);

// 创建注册人员信息表及聊天记录数据库
int creat_table(sqlite3 *pdb, char *sql);
int creat_table_chat(sqlite3 *pdb);
int update_sqlite3_fd(int confd, int account, sqlite3 *pdb);
int update_sqlite3_statu(int account, sqlite3 *pdb);

// 超级用户创建
int super_root(sqlite3 *pdb);

// 创建tcp/udp套接口
void create_tcp_sever(int *sockfd, struct sockaddr_in *s_addr);

// 注册账号
void ero_account_s(const int confd, struct enrollinfo *eninfo, sqlite3 *pdb);
// 登录
int log_operation(int confd, struct options *option, sqlite3 *pdb);
// void chat_start_s(void *args);
void chat_start_s(int confd, sqlite3 *pdb);

// 套接口操作
void *do_use_fd(void *arg);
void say_to_one(int confd, struct msg_buff *msg, sqlite3 *pdb);
void say_to_all(int confd, struct msg_buff *msg, sqlite3 *pdb);

// 保存聊天记录
void save_chat_record(struct msg_buff *msg, sqlite3 *pdb);

// 添加好友
void add_friends(int confd, struct msg_buff *msg, sqlite3 *pdb);
// 注册会员修改权限
void ero_vip_s(int confd, struct msg_buff *msg, sqlite3 *pdb);
int change_permision(int account, sqlite3 *pdb);

// 找回密码
int search_password(int confd, struct options *option, sqlite3 *pdb);

// 禁言,解除(设置权限)
void set_someone_per(int confd, int per, int myaccount, int account, sqlite3 *pdb);
void tick_out(int confd, int my_account, int account, sqlite3 *pdb);
#endif
#include "chat_s.h"

void create_tcp_sever(int *sockfd, struct sockaddr_in *s_addr)
{
    socklen_t s_len = sizeof(struct sockaddr_in);
    *sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (*sockfd < 0)
    {
        perror("sockfd creat error:");
        exit(1);
    }
    // 解决无法绑定问题, 重复绑定
    int opt = 1;
    setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(s_addr, sizeof(struct sockaddr_in));
    (*s_addr).sin_family = AF_INET;
    (*s_addr).sin_port = htons(TCP_PORT);
    (*s_addr).sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(*sockfd, (struct sockaddr *)s_addr, s_len) == -1)
    {
        perror("bind error:");
        close(*sockfd);
        exit(1);
    }
}

int main()
{
    int listenfd;
    int confd;
    int nfds;
    int ret;

    pthread_t chat_id;
    sqlite3 *pdb;

    struct sockaddr_in s_addr_tcp;
    struct sockaddr_in c_addr;
    socklen_t c_len = sizeof(c_addr);

    struct epoll_event ev, events[MAX_EVENTS];

    srand((unsigned)time(NULL));

    // 创建数据库
    if (creat_sqlite(&pdb) != SQLITE_OK)
    {
        sqlite3_close(pdb);
        exit(1);
    }
    // 创建注册人员啊信息表和聊天记录表
    if (creat_table_chat(pdb) != SQLITE_OK)
    {
        sqlite3_close(pdb);
        exit(1);
    }

    create_tcp_sever(&listenfd, &s_addr_tcp);
    listen(listenfd, 1024);

    while (1)
    {
        if ((confd = accept(listenfd, (struct sockaddr *)&c_addr, &c_len)) < 0)
        {
            perror("accept error:");
            close(listenfd);
            exit(1);
        }
        struct arg args;
        args.fd = confd;
        args.pdb = pdb;
        pthread_create(&chat_id, NULL, do_use_fd, (void *)&args);
    }

    close(listenfd);

    return 0;
}
#include "chat_s.h"

int creat_sqlite(sqlite3 **pdb)
{
    int ret;
    if((ret = sqlite3_open("chat_room.db", pdb)) != SQLITE_OK)
    {
        perror("creat sqlite error");
        exit(1);
    }
    return SQLITE_OK;
}

int creat_table_chat(sqlite3 *pdb)
{
    char *sql1 = "create table if not exists chat_account   (Account integer primary key,         \
                                                            Nickname text     NOT NULL,          \
                                                            Password text     NOT NULL,          \
                                                            Permision integer NOT NULL,          \
                                                            Question text,                        \
                                                            Answer   text,                        \
                                                            Fd       integer,                      \
                                                            STATU    text                 );";
    
    char *sql2 = "create table if not exists chat_record   (TIME text,  \
                                                            SEND integer,  \
                                                            RECV integer,  \
                                                            MESG text );";        

    if(creat_table(pdb, sql1) != SQLITE_OK)
    {
        debug_msg("creat table account error\n");
        return -1;
    }
    
    if(super_root(pdb)!= SQLITE_OK)
    {
        sqlite3_close(pdb);
        exit(1);
    }
    if(creat_table(pdb, sql2) != SQLITE_OK)
    {
        debug_msg("creat table record error\n");
        return -1;
    }

    

    return SQLITE_OK;
}

int creat_table(sqlite3 *pdb, char *sql)
{
    int ret;

    char *errmsg = NULL;

    if((ret = sqlite3_exec(pdb, sql, NULL, NULL,&errmsg)) != SQLITE_OK)
    {
        perror("creat table error:");
        return -1;
    }

   
    return SQLITE_OK;
}

int super_root(sqlite3 *pdb)
{
    int ret;
    char *errmsg;
    char *sql = NULL;

    sql = "insert OR IGNORE into chat_account (Account, Nickname, Password, Permision) values (10000, 'root', 'admin', 4);";
    if((ret = sqlite3_exec(pdb, sql, NULL, NULL,&errmsg)) != SQLITE_OK)
    {
        perror("insert root error:");
        printf("%s\n",errmsg);

        return -1;
    }
    return SQLITE_OK;
}

//客户端上线更新fd
int update_sqlite3_fd(int confd, int account,sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;
    sprintf(sql,"update chat_account set Fd = %d,STATU = 'online' where Account = %d;", confd, account);
    
    
    if(sqlite3_exec(pdb,sql,NULL,NULL,&errmsg) != SQLITE_OK)
    {
        debug_msg("%s\n",errmsg);
        return -1;
    }
    return SQLITE_OK;

}

int update_sqlite3_statu(int account,sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;
    sprintf(sql,"update chat_account set STATU = 'notonline',Fd = -1 where Account = %d;",account);
    
    debug_msg("update account %d\n",account);
    
    if(sqlite3_exec(pdb,sql,NULL,NULL,&errmsg) != SQLITE_OK)
    {
        debug_msg("%s\n",errmsg);
        return -1;
    }
    return SQLITE_OK;
}
#include "chat_s.h"

void *do_use_fd(void *arg)
{
    struct options option;
    struct arg args;

    pthread_t chat_id;

    int bytes_read;
    int account;

    struct arg tmp = *((struct arg *)arg);
    int confd = tmp.fd;
    sqlite3 *pdb = tmp.pdb;

    memset(&option, 0, sizeof(option));

    while (1)
    {
        if (bytes_read = read(confd, &option, sizeof(option)) <= 0)
        {
            if (bytes_read == 0)
            {
                printf("%d offline\n", confd);
                if (SQLITE_OK != update_sqlite3_statu(option.info.loginfo.account, pdb))
                {
                    printf("update stat error\n");
                }
                close(confd);
                return NULL;
            }
        }

        switch (option.option)
        {
        case ERO:
            ero_account_s(confd, &option.info.eninfo, pdb);
            break;

        case LOG:
            if (log_operation(confd, &option, pdb) != 0)
            {
                break;
            }
            chat_start_s(confd, pdb);
            break;

        case FGPD:
            search_password(confd, &option, pdb);
            break;

        case EXIT:
            if (SQLITE_OK != update_sqlite3_statu(option.info.loginfo.account, pdb))
            {
                printf("update stat error\n");
            }
            debug_msg("do_use  %d offline\n", confd);
            close(confd);
            return NULL;
            break;

        default:
            break;
        }
    }
    return NULL;
}

void ero_account_s(const int confd, struct enrollinfo *eninfo, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;

    debug_msg("ero_account  %d", __LINE__);

    eninfo->account = rand() % 100000 + 100000;
    sprintf(sql, "insert into chat_account (Account, Nickname, Password, Permision, Question, Answer, Fd, STATU) \
                                    values (%d, '%s', '%s', 1, '%s', '%s', %d, 'notonline');",
            eninfo->account, eninfo->nickname, eninfo->password, eninfo->sc_protect, eninfo->answer, confd);

    while (sqlite3_exec(pdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        debug_msg("insert new account error:%s\n", errmsg);
        if (0 == strcmp(errmsg, "UNIQUE constraint failed: chat_count.Account"))
        {
            eninfo->account = rand() % 100000 + 100000;
            sprintf(sql, "insert into chat_account (Account, Nickname, Password, Permision, Question, Answer, IP, STATU) \
                                    values (%d, '%s', '%s', 1, '%s', '%s', %d, 'notonline');",
                    eninfo->account, eninfo->nickname, eninfo->password, eninfo->sc_protect, eninfo->answer, confd);
            sqlite3_free(errmsg);
        }
        else
        {
            debug_msg("insert new account error:%s\n", errmsg);
            return;
        }
    }
    write(confd, &eninfo->account, sizeof(int));

    return;
}

// 登录操作
int log_operation(int confd, struct options *option, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char **presult = NULL;
    char *errmsg = NULL;

    int row, col;

    sprintf(sql, "select Account,Password from chat_account       \
                    where Account = %d AND Password = '%s';",
            option->info.loginfo.account, option->info.loginfo.password);

    if (sqlite3_get_table(pdb, sql, &presult, &row, &col, &errmsg) != SQLITE_OK)
    {
        perror("log error:");
        sqlite3_free_table(presult);
        sqlite3_free(errmsg);
        return -1;
    }
    if (0 == row)
    {
        debug_msg("no account\n");
        write(confd, "failed", strlen("failed"));
        return -1;
    }
    else
    {
        write(confd, "success", strlen("success"));
    }
    if (update_sqlite3_fd(confd, option->info.loginfo.account, pdb) < 0)
    {
        printf("update confd error\n");
    }
    sqlite3_free_table(presult);
    return 0;
}

// 找回密码
int search_password(int confd, struct options *option, sqlite3 *pdb)
{
    char buff[100];
    char sql[SQL_SIZE];
    char **result = NULL;
    char *errmsg = NULL;

    int row, col;
    char password[PASSWORD_SIZE];

    sprintf(sql, "select Question from chat_account where account = %d;", option->info.fgpwd_account);
    sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg);
    if (0 == row)
    {
        write(confd, SEARCH_ERROR, strlen(SEARCH_ERROR));
        return -1;
    }
    strcpy(buff, result[1]);
    write(confd, buff, sizeof(buff));
    sqlite3_free_table(result);
    result = NULL;

    read(confd, buff, sizeof(buff));
    sprintf(sql, "select Answer from chat_account where account = %d;", option->info.fgpwd_account);
    sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg);

    if (0 == strcmp(buff, result[1]))
    {
        sprintf(sql, "select Password from chat_account where account = %d;", option->info.fgpwd_account);
        sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg);
        strcpy(buff, result[1]);
        write(confd, buff, sizeof(buff));
    }
    else
    {
        write(confd, PED_ERROR, sizeof(PED_ERROR));
    }

    return SQLITE_OK;
}
#include "chat_s.h"

// 查找所有在线好友
void search_all_fd(int account, int stat, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char **result = NULL;
    char *errmsg = NULL;

    int row, col;

    struct msg_buff msg;
    memset(&msg, 0, sizeof(msg));
    sprintf(sql, "select Account,Fd from chat_account where STATU = 'online' and Account != %d;", account);
    if (SQLITE_OK != sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg))
    {
        debug_msg("chat_s %d  %s\n", __LINE__, errmsg);
        sqlite3_free(errmsg);
        return;
    }
    if (0 == row)
    {
        debug_msg("no account\n");
        return;
    }
    msg.my_account = account;
    for (int i = col; i < (row + 1) * col; i++)
    {
        if (i % row == 0 && i + 1 < (row + 1) * col)
        {
            msg.mushin_flag = stat;
            debug_msg("online account:%d  fd:%d\n", account, atoi(result[i + 1]));
            write(atoi(result[i + 1]), &msg, sizeof(msg));
        }
    }

    sqlite3_free_table(result);
}

// 查询权限
int query_permision(const int account, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char **result = NULL;
    char *errmsg = NULL;

    int row, col;
    int per;

    sprintf(sql, "select Permision from chat_account where Account = %d;", account);

    if (sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg) != SQLITE_OK)
    {
        debug_msg("chat_s %d  %s\n", __LINE__, errmsg);
        sqlite3_free(errmsg);
        return -2;
    }

    per = atoi(result[1]);

    return per;
}

void chat_start_s(int confd, sqlite3 *pdb)
{
    int bytes_read;
    int account;
    int a[100][2];
    int per;
    char buff[100];

    struct msg_buff msg;
    memset(buff, 0, sizeof(buff));
    memset(&account, 0, sizeof(int));
    memset(&per, 0, sizeof(int));
    memset(a, 0, sizeof(a));

#if 1
    read(confd, &account, sizeof(int));
    per = query_permision(account, pdb);
    write(confd, &per, sizeof(int));
#endif

#if 1
    // 好友上线提醒
    search_all_fd(account, ONLINE, pdb);
#endif

    while (1)
    {
        memset(&msg, 0, sizeof(msg));
        if ((bytes_read = read(confd, &msg, sizeof(msg))) <= 0)
        {
            if (bytes_read < 0)
            {
                perror("read error");
            }
            if (0 == bytes_read)
            {
                debug_msg("client offline\n");
                if (SQLITE_OK != update_sqlite3_statu(account, pdb))
                {
                    printf("update stat error\n");
                }
                close(confd);
            }
        }
        switch (msg.chat_mode)
        {
        case STOA:
            say_to_one(confd, &msg, pdb);
            break;
        case STOO:
            say_to_all(confd, &msg, pdb);
            break;

        case ADD:
            add_friends(confd, &msg, pdb);
            break;

        case VIP:
            ero_vip_s(confd, &msg, pdb);
            break;

        case MUS:
            set_someone_per(confd, 0, msg.my_account, msg.account[0], pdb);
            break;

        case CANCLE:
            set_someone_per(confd, 1, msg.my_account, msg.account[0], pdb);
            break;

        case OUT:
            tick_out(confd, msg.my_account, msg.account[0], pdb);
            break;

        case EXIT:
            if (SQLITE_OK != update_sqlite3_statu(account, pdb))
            {
                printf("update stat error\n");
            }
            debug_msg("chat_s %d offline\n", confd);
            search_all_fd(account, OFFLINE, pdb);  //好友下线提醒
            close(confd);
            pthread_exit(NULL);
            return;
        default:
            break;
        }
    }
}

// 判断发送者的Permision是否小于1
int is_smaller_sp(const int account, sqlite3 *pdb)
{

    int per = query_permision(account, pdb);

    if (per < 1)
    {
        return -1;
    }
    return 0;
}

// 查找聊天好友套接字
int search_friend_fd(int account, sqlite3 *pdb)
{

    char sql[SQL_SIZE];
    char **result = NULL;
    char *errmsg = NULL;

    int row, col;
    int fd;

    sprintf(sql, "select Fd from chat_account where Account = %d;", account);
    if (SQLITE_OK != sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg))
    {
        debug_msg("chat_s %d  %s\n", __LINE__, errmsg);
        sqlite3_free(errmsg);
        return -1;
    }
    if (0 == row)
    {
        debug_msg("no account\n");
        return -1;
    }
    else
    {
        fd = atoi(result[1]);
    }

    sqlite3_free_table(result);

    return fd;
}

void say_to_all(int confd, struct msg_buff *msg, sqlite3 *pdb)
{
    int ret;
    char sql[SQL_SIZE];
    char name[NICKNAME_SIZE];

    // 判断发送者的Permision是否小于1
    ret = is_smaller_sp(msg->my_account, pdb);
    if (ret < 0)
    {
        msg->mushin_flag = MUSHIN;
        write(confd, msg, sizeof(struct msg_buff));
        return;
    }
    else
    {
        // 查找聊天对象fd
        for (int i = 0; i < msg->num; i++)
        {
            ret = search_friend_fd(msg->account[i], pdb);
            if (ret < 0)
            {
                msg->mushin_flag = NOT_ONLINE;
                write(confd, msg, sizeof(struct msg_buff));
                continue;
            }
            write(ret, msg, sizeof(struct msg_buff));
            if (msg->option == MSG || msg->option == EXP)
            {
                save_chat_record(msg, pdb);
            }
        }
    }
}

void say_to_one(int confd, struct msg_buff *msg, sqlite3 *pdb)
{
    int ret;
    char sql[SQL_SIZE];
    char name[NICKNAME_SIZE];

    // 判断发送者的Permision是否小于1
    ret = is_smaller_sp(msg->my_account, pdb);
    if (ret < 0)
    {
        msg->mushin_flag = MUSHIN;
        write(confd, msg, sizeof(struct msg_buff));
        return;
    }
    else
    {
        // 查找聊天对象fd
        ret = search_friend_fd(msg->account[0], pdb);
        if (ret < 0)
        {
            msg->mushin_flag = NOT_ONLINE;
            write(confd, msg, sizeof(struct msg_buff));
            return;
        }
        write(ret, msg, sizeof(struct msg_buff));
        if (msg->option == MSG || msg->option == EXP)
        {
            save_chat_record(msg, pdb);
        }
    }
}

int search_friend(int account, char name[NICKNAME_SIZE], sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char **result = NULL;
    char *errmsg = NULL;

    int row, col;
    sprintf(sql, "select Nickname from chat_account where Account = %d;", account);
    if (SQLITE_OK != sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg))
    {
        debug_msg("chat_s %d  %s\n", __LINE__, errmsg);
        sqlite3_free(errmsg);
        return -1;
    }
    if (0 == row)
    {
        debug_msg("no account\n");
        return -1;
    }

    strcpy(name, result[1]);
    sqlite3_free_table(result);

    return 0;
}

void add_friends(int confd, struct msg_buff *msg, sqlite3 *pdb)
{
    char name[NICKNAME_SIZE];
    memset(name, 0, sizeof(name));
    debug_msg("here\n");
    debug_msg("add  %d\n", msg->account[0]);

    if (search_friend(msg->account[0], name, pdb) != 0)
    {
        debug_msg("here1\n");
        write(confd, NO_FRIEND, sizeof(NO_FRIEND));
        return;
    }
    else
    {
        debug_msg("here2\n");
        write(confd, name, sizeof(name));
    }
}

int change_permision(int account, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;
    sprintf(sql, "update chat_account set Permision = 3 where Account = %d;", account);
    if (sqlite3_exec(pdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        debug_msg("%s\n", errmsg);
        return -1;
    }
    return SQLITE_OK;
}

void ero_vip_s(int confd, struct msg_buff *msg, sqlite3 *pdb)
{
    char buff[100];
    memset(buff, 0, sizeof(buff));
    if (msg->mushin_flag == VIP)
    {
        write(confd, PAY, sizeof(PAY));
        read(confd, buff, sizeof(buff));
        if (0 == strcmp(buff, PAY_SUCCED))
        {
            change_permision(msg->my_account, pdb);
            write(confd, ERO_VIP, sizeof(ERO_VIP));
        }
    }
    return;
}

void set_someone_per(int confd, int per, int myaccount, int account, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;

    if (query_permision(account, pdb) < 3 || query_permision(myaccount, pdb) > 3)
    {
        sprintf(sql, "update chat_account set Permision = %d where Account = %d;", per, account);

        if (sqlite3_exec(pdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
        {
            printf("set permision error:%s\n", errmsg);
            sqlite3_free(errmsg);
            return;
        }
        write(confd, "操作成功", strlen("操作成功"));
    }
    else
    {
        write(confd, "操作失败", strlen("操作失败"));
    }
}

void save_chat_record(struct msg_buff *msg, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg;

    for (int i = 0; i < msg->num; i++)
    {
        sprintf(sql, "insert into chat_record (TIME,SEND,RECV,MESG)  \
                                values('%s %s',%d,%d,'%s');",
                __DATE__, __TIME__, msg->my_account, msg->account[i], msg->chat_opt.chat_msg);

        if (sqlite3_exec(pdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
        {
            printf("insert chat record error:%s\n", errmsg);
            sqlite3_free(errmsg);
            return;
        }
    }
}

void tick_out(int confd, int myaccount, int account, sqlite3 *pdb)
{
    char sql[SQL_SIZE];
    char *errmsg = NULL;
    char **result = NULL;
    int row, col;
    int fd;
    struct msg_buff msg;

    memset(&msg, 0, sizeof(msg));

    if (query_permision(account, pdb) < 3 || query_permision(myaccount, pdb) > 3)
    {
        sprintf(sql, "select Fd from chat_account where Account = %d;", account);

        if (sqlite3_get_table(pdb, sql, &result, &row, &col, &errmsg) != SQLITE_OK)
        {
            printf("tick out:%s\n", errmsg);
            sqlite3_free(errmsg);
            return;
        }
        if (0 == row)
        {
            write(confd, "查无此人", strlen("查无此人"));
            return;
        }
        else if (1 == row)
        {
            fd = atoi(result[1]);
            if (fd > 2)
            {
                msg.mushin_flag = -2;
                write(fd, &msg, sizeof(msg));
                update_sqlite3_fd(0, account, pdb);
                update_sqlite3_statu(account, pdb);
                close(fd);
            }
            else
            {
                write(confd, "操作失败", strlen("操作失败"));
                return;
            }
        }
        write(confd, "操作成功", strlen("操作成功"));
    }
    else
    {
        write(confd, "操作失败", strlen("操作失败"));
    }
}

注:存在问题

1.有时候登录会阻塞无法正常显示界面

2.读线程读完消息后,光标不能回到原来位置

标签: c语言

本文转载自: https://blog.csdn.net/shujsvhab/article/details/130459025
版权归原作者 一只快乐的哈士奇 所有, 如有侵权,请联系我们删除。

“LinuxC TCP实现简易聊天室”的评论:

还没有评论