目录
1.概述
1.1聊天室设计内容
2.系统设计
2.1系统功能设计
2.1.1用户管理
2.1.2聊天室管理
2.1.3聊天管理
2.2系统数据结构设计
2.3系统主要函数设计
3.系统实现
3.2功能模块的程序流程图及运行界面
3.2.1功能模块流程图
3.2.2运行界面
4.源代码
4.1客户端
4.2服务器
注:存在问题
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.有时候登录会阻塞无法正常显示界面文章来源:https://www.toymoban.com/news/detail-483689.html
2.读线程读完消息后,光标不能回到原来位置文章来源地址https://www.toymoban.com/news/detail-483689.html
到了这里,关于LinuxC TCP实现简易聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!