这里只是在windows系统下用C++做的简易聊天室,其界面比较low,不过该有的功能已基本实现。至于带界面的,QT里有封装好的QTcpSocket套接字,会在下一篇博客里予以介绍。主要的功能主要包含的功能有:查看当前在线人数、进入群聊界面、进入私聊界面、查看聊天记录、文件传输、更改密码、在线注销、退出等。
一、方案研究
1.1 客户端方案
1.首先客户端在连接到服务器之后,要有用户注册、登录、退出等界面
2.在注册时需要设置用户名、密码、是否为管理员等信息(注意:这里需要在代码里设置一个结构体,来保存用户名、密码等信息,然后向服务器发送这些信息以注册,最后得到服务器反馈而来的注册成功信息,在注册成功之后需要返回刚开始的界面)
3.在登陆时需要输入用户名,密码信息,然后向服务器发送登录请求,若服务器里存放着该用户的注册信息,则反馈回消息(这里根据返回消息的不同区分出管理员和普通用户,然后分情况处理(界面不同))。下一步则需要线程分离,分别用来监听服务器返回信息以及客户端自己进行的操作。
4.普通用户界面(主要包含的功能有:查看当前在线人数、进入群聊界面、进入私聊界面、查看聊天记录、文件传输、更改密码、在线注销、退出等功能),这些功能主要通过向服务器发送请求,然后通过(前一步)分离的线程监听服务器得到。
5.管理员界面比普通用户多了一个管理员界面,里面主要多了一个禁言、解除禁言、踢出聊天室的功能
6.查看当前在线人数函数:仅仅通过send()函数向服务器端发送请求,通过分离的线程监听获得服务器端保存的在线人数。效果图如下(此时有伴君(管理员)、张三、李四)
7.进入群聊界面:同样通过send()函数向服务器端发送请求,通过服务器向在线所有人发送信息,分离的线程获得服务器反馈结果。效果图如下
8.进入私聊界面:同样通过send()函数向服务器端发送请求,通过服务器向要发送的对象发送信息,分离的线程获得服务器反馈结果。效果图如下
9.查看聊天记录:主要有查看群聊记录和个人记录两种,主要通过打开客户端创建的数据库,通过select * from histroy指令进入输出。
10.文件传输,主要就是打开文件,然后将其内容通过send()函数发送到服务器,然后服务器根据其发送内容,将其发送给要发送的对象。
11.更改密码、在线注销:也是向服务器发送注册信息。
1.2 服务器方案
1.创建用户表(SQLite数据库),用来存放客户端注册信息send()来的信息(主要包括用户、密码、连接的socket套接字、消息内容、消息类型、接收者姓名、用户状态等等)
2.循环accept不同用户的连接请求,并分离线程进行处理;
3.处理客户端的注册请求时,打开数据库,将用户信息进行保存,使用的语句为:insert into user values(‘%s’,‘%s’,%d,%d,%d),然后将成功信息反馈给客户端;
4.处理客户端的登录请求时,首先打开数据库,将用户信息进行比对,注意(普通用户和管理员要分别处理),然后若存在就更改其在线状态,并更新套接字(每次同一客户端连接时也会有所不同)
5.在更改完在线状态以及套接字之后,接下来就是从客户端接收信息,并根据消息类型的不同,分别调用不同的函数(查看当前在线人数、处理群聊消息、处理私聊消息等等)。
二、源码实现
接下来就是代码的展示(还有不完善的地方,请海涵)
client.cpp
#include <sys/types.h>
#include<winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <io.h>
#include <pthread.h>
#include <ws2tcpip.h>
#include <iostream>
#include <vector>
#include <string>
#include<fstream>
#include<sstream>
#include<assert.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
//#pragma comment (lib, "wsock32.lib")
#define PORT 9999
char myName[20]; // 保存用户名
char msg1[1024]; // 保存聊天信息
sqlite3 * database;
int flag1 = 0; // 线程退出的判断条件(不退出)
int flag2 = 0; // 文件传输确认信号(无接收)
int flag3 = 0; // 存在文件接收请求的判断(不存在)
int flag4 = 0; // 本地存储是否被禁言(未被禁言)
int flagg = 0;
// 协议
struct Msg
{
char msg[1024]; // 消息内容
int cmd; // 消息类型
char filename[50]; // 保存文件名
char toname[20]; // 接收者姓名
char fromname[20]; // 发送者姓名
int sig; // 用户状态(0:管理员、1:普通用户、2:被禁言)
};
struct Msg msg; // 全局变量两个线程共享
// 注册/登录界面
void interface1()
{
system("clear");
printf("\t*************************** 网络聊天室 *****************************\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* 1、 注册 *\n");
printf("\t* 2、 登录 *\n");
printf("\t* q、 退出 *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t********************************************************************\n\n");
printf("\t***** 请输入命令: ");
}
// 普通用户界面
void interface2()
{
system("clear");
printf("\t*************************** 网络聊天室 *****************************\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* 1、 查看当前在线人数 *\n");
printf("\t* 2、 进入群聊界面 *\n");
printf("\t* 3、 进入私聊界面 *\n");
printf("\t* 4、 查看聊天记录 *\n");
printf("\t* 5、 文件传输 *\n");
printf("\t* 6、 更改密码 *\n");
printf("\t* 7、 在线注销 *\n");
printf("\t* Q、 退出聊天室 返回登录界面 *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t********************************************************************\n\n");
printf("\t***** 请输入命令: ");
}
// 管理员界面
void interface3()
{
system("clear");
printf("\t*************************** 网络聊天室 *****************************\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* 1、 查看当前在线人数 *\n");
printf("\t* 2、 进入群聊界面 *\n");
printf("\t* 3、 进入私聊界面 *\n");
printf("\t* 4、 查看聊天记录 *\n");
printf("\t* 5、 文件传输 *\n");
printf("\t* 6、 更改密码 *\n");
printf("\t* 7、 在线注销 *\n");
printf("\t* 8、 管理员界面 *\n");
printf("\t* Q、 退出聊天室 返回登录界面 *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t********************************************************************\n\n");
printf("\t***** 请输入命令: ");
}
//核心代码
void getFileNames(string path, vector<string>& files)
{
//文件句柄
//注意:我发现有些文章代码此处是long类型,实测运行中会报错访问异常
intptr_t hFile = 0;
//文件信息
struct _finddata_t fileinfo;
string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
//如果是目录,递归查找
//如果不是,把文件绝对路径存入vector中
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFileNames(p.assign(path).append("\\").append(fileinfo.name), files);
}
else
{
files.push_back(fileinfo.name);
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
}
// 用来保存收到的聊天信息
void keep_msg(char * msg1)
{
// 打开数据库
int ret = sqlite3_open("Histroy.db", &database);
if (ret != SQLITE_OK)
{
printf("\t打开数据库失败\n");
return;
}
// 往histroy表中添加信息
char buf[100];
char *errmsg = NULL;
sprintf(buf, "insert into histroy values('%s','%s','%s')", msg.fromname, msg.toname, msg1);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("\t数据库操作失败:%s\n", errmsg);
return;
}
}
// 接收文件
void receive()
{
printf("\n\t正在接收文件.....\n");
FILE * fp;
fp = fopen(msg.filename, "wb");
fwrite(msg.msg, sizeof(char), strlen(msg.msg), fp);
fclose(fp);
//int fd2 = _open(msg.filename, O_WRONLY | O_CREAT, 0777);
//if (fd2 == -1)
//{
// perror("_open fd2");
// return;
//}
_write(fd2, msg.msg, 1023);
//send(fd2, msg.msg, 1023, 0);
//_close(fd2);
}
// 用来监听收到的信息
void *_readMsg(void *v)
{
int socketfd = (int)v;
while (1)
{
if (flag1 == 1) // 判断线程的退出条件
{
flag1 = 0; // 重置线程退出条件
pthread_exit(NULL);
}
//_read(socketfd, &msg, sizeof(msg));
recv(socketfd, (char *)&msg, sizeof(msg), 0);
switch (msg.cmd)
{
case 9001: // 群聊
sprintf(msg1, "收到一条来自%s的群消息:\n\t%s", msg.fromname, msg.msg);
printf("\n\n\t%s\n", msg1);
printf("\n\t回车键返回 \n");
keep_msg(msg1);
break;
case 9002: // 私聊
printf("\n\t%s 发来一条消息:\n\t%s\n", msg.fromname, msg.msg);
sprintf(msg1, "%s 向 %s 发送一条信息:\n\t%s", msg.fromname, msg.toname, msg.msg);
printf("\n\t回车键返回 \n");
keep_msg(msg1);
break;
case 9003: // 处理发送失败
Sleep(50);
printf("\n\t用户不在线或不存在,发送失败\n");
printf("\n\t回车键返回 \n");
break;
case 9004: // 是否存在文件接收确认信号
printf("\n\n\t收到一条信息,输入任一字符进行回复:");
fflush(stdout);
flag3 = 1;
break;
case 9005: // 文件传输请求被拒绝
printf("\n\t您发送的文件传输请求已被拒绝\n");
printf("\n\t回车键返回 \n");
break;
case 9006: // 文件传输请求已通过
printf("\n\t您发送的文件传输请求已通过,请打开文件传输界面进行文件传输\n");
printf("\n\t回车键返回 \n");
break;
case 9007: // 接收文件
if (flagg != 1)
printf("\n\t已成功拦截 %s 给您发送的文件\n", msg.fromname);
else
receive();
break;
case 9008: // 文件传输完成
printf("\n\t%s 给您发送的文件已全部接收完毕,请及时查看\n", msg.fromname);
printf("\n\t回车键返回 \n");
flag2 = 0; // 重置文件传输确认信号
break;
case 9009: // 密码修改成功
printf("\n\t密码修改成功!\n");
Sleep(50);
break;
case 9010: // 密码输入有误
printf("\n\t密码输入有误,修改失败\n");
Sleep(50);
break;
case 9011: // 收到禁言信号
printf("\n\t您已被管理员禁言,将无法发送群聊信息\n");
printf("\n\t回车键返回 \n");
flag4 = 1;
break;
case 9012: // 收到结除禁言信号
printf("\n\t您已被管理员解除禁言\n");
printf("\n\t回车键返回 \n");
flag4 = 0;
break;
case 9013: // 收到被踢出信号
printf("\n\t您已被管理员踢出,即将退出聊天室....\n");
//Sleep (2);
flag1 = 1;
break;
}
Sleep(50);
}
}
// 查看当前在线人数
void display(int socketfd)
{
msg.cmd = 1;
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
Sleep(50);
printf("\n\t当前在线人数为:%d\n", msg.cmd);
printf("\n\t回车键返回 \n");
getchar();
}
// 退出聊天室,返回登录界面
void quit_chatroom(int socketfd)
{
msg.cmd = 10;
strcpy(msg.fromname, myName);
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送退出信号
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
}
// 进入群聊界面
void chat1(int socketfd)
{
if (flag4 == 1)
{
printf("\n\t您已被管理员禁言,无法发送群聊信息...\n");
return;
}
msg.cmd = 2;
strcpy(msg.fromname, myName);
strcpy(msg.toname, "all");
printf("\n\t请输入您要发送的内容:\n\t");
scanf("%s", msg.msg);
getchar();
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
printf("\n\t发送完成,等待处理结果.....\n");
Sleep (50);
}
// 进入私聊界面
void chat2(int socketfd)
{
msg.cmd = 3;
strcpy(msg.fromname, myName);
printf("\n\t请输入您要发送的对象:\n\t");
scanf("%s", msg.toname);
getchar();
printf("\t请输入您要发送的内容:\n\t");
scanf("%s", msg.msg);
getchar();
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
printf("\n\t发送完成,请稍候.....\n");
Sleep(50);
}
// 打印群聊历史记录
void chat1_hst()
{
system("clear");
printf("\t*************************** 网络聊天室 *****************************\n\n\n");
printf("\t群聊历史记录: \n");
// 打开数据库
int ret = sqlite3_open("Histroy.db", &database);
if (ret != SQLITE_OK)
{
printf("\t打开数据库失败\n");
return;
}
// 获取histroy表中的信息
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from histroy";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 1 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], "all") == 0)
{
printf("\n\t%s\n", resultp[i + 1]);
}
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
}
// 打印私聊历史记录
void chat2_hst()
{
system("clear");
printf("\t*************************** 网络聊天室 *****************************\n\n\n");
printf("\t私聊历史记录:\n");
// 打开数据库
int ret = sqlite3_open("Histroy.db", &database);
if (ret != SQLITE_OK)
{
printf("\t打开数据库失败\n");
return;
}
// 获取histroy表中的信息
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from histroy";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 1 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], "all") != 0)
{
printf("\n\t%s\n", resultp[i + 1]);
}
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
}
// 查看聊天记录
void dis_histroy(int socketfd)
{
printf("\n\t a、查看群聊记录\n\t b、查看个人聊天记录\n\t");
printf("\n\t***** 请选择: ");
switch (getchar())
{
case 'a':
chat1_hst();
break;
case 'b':
chat2_hst();
break;
}
printf("\n\t回车键返回 ");
getchar();
}
// 确认传输对象
void convey_confirm(int socketfd)
{
msg.cmd = 5;
strcpy(msg.fromname, myName);
printf("\n\t请输入文件的传输对象:\n\t");
scanf("%s", msg.toname);
getchar();
printf("\n\t传输请求发送完成,请稍等.....\n");
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
}
// 文件传输过程
void convey_chose(int socketfd)
{
msg.cmd = 9007;
strcpy(msg.toname, msg.fromname);
strcpy(msg.fromname, myName);
printf("\n\t当前目录下的文件有: \n\n\t");
vector<string> fileNames;
string path("."); //自己选择目录测试
getFileNames(path, fileNames);
for (const auto &ph : fileNames) {
std::cout<< "\n\t"<< ph << "\n";
}
printf("\n\t请输入您要传送的文件名: ");
printf("\n\t");
scanf("%s", msg.filename);
getchar();
FILE *fp;
int i = 0;
char c[500];
if ((fp = fopen(msg.filename, "r")) == NULL)
{
printf("open file failed.");
return;
}
else
{
printf("\n\t正在传输文件,请稍候......\n");
while (fgets(msg.msg, 1024, fp))
{
//cout << msg.msg << endl;
//_write(socketfd, &msg, sizeof(struct Msg));
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
Sleep(50);
}
}
fclose(fp);
msg.cmd = 9008;
//_write(socketfd, &msg, sizeof(struct Msg));
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
printf("\n\t文件传输完成\n");
// 重新开启一个线程
pthread_t id;
pthread_create(&id, NULL, _readMsg, (void*)socketfd);
pthread_detach(id);
}
// 文件传输界面
void convey(int socketfd)
{
system("clear");
printf("\t*************************** 网络聊天室 *****************************\n\n\n");
printf("\t 1、传输对象确认\n");
printf("\t 2、选择文件\n");
printf("\n\t***** 请选择: ");
char ch[2];
fgets(ch, 2, stdin);
while (getchar() != '\n');
switch (ch[0])
{
case '1': // 确认传输对象
convey_confirm(socketfd);
break;
case '2': // 文件选择
convey_chose(socketfd);
break;
}
printf("\n\t回车键返回 ");
getchar();
}
// 更改密码
void change_pass(int socketfd)
{
char ch[2];
msg.cmd = 6;
printf("\n\t您是否确定需要修改密码?(y/n): ");
fgets(ch, 2, stdin);
while (getchar() != '\n');
if (ch[0] != 'y')
{
printf("\n\t请稍候.....\n");
Sleep(50);
return;
}
printf("\t请输入旧密码: ");
scanf("%s", msg.msg);
getchar();
printf("\t请输入新密码: ");
scanf("%s", msg.filename);
getchar();
strcpy(msg.fromname, myName);
//_write(socketfd, &msg, sizeof(struct Msg)); // 向服务器发送注册信息
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
printf("\n\t正在校验数据,请稍候......\n");
Sleep(50);
}
// 在线注销
void delete_user(int socketfd)
{
msg.cmd = 8;
printf("\n\t正在处理注销操作......\n");
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
Sleep(1);
printf("\t注销完成!\n");
}
// 普通用户操作
void user_do(int socketfd)
{
char ch[2];
while (1)
{
interface2();
if (flag3 == 1)
{
printf("\n\n\t%s 请求传输文件,是否接收?(y/n):", msg.fromname);
fflush(stdout);
fgets(ch, 2, stdin);
while (getchar() != '\n');
if (ch[0] != 'y')
{
printf("\n\t您已拒绝接受文件传输\n");
printf("\n\t回车键返回 \n");
}
else
{
flagg = 1;
printf("\n\t您已接受文件传输请求\n");
printf("\n\t回车键返回 \n");
flag2 = 1;
}
if (flag2 == 0)
{
msg.cmd = 9005; // 不接受文件
strcpy(msg.toname, msg.fromname); // 修改信号发送对象
strcpy(msg.fromname, myName);
//_write(socketfd, &msg, sizeof(struct Msg));
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
}
else if (flag2 == 1)
{
msg.cmd = 9006; // 接受文件
strcpy(msg.toname, msg.fromname); // 修改信号发送对象
strcpy(msg.fromname, myName);
//_write(socketfd, &msg, sizeof(struct Msg));
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
}
flag3 = 0; // 重置是否存在文件接收请求的判断
//flag2 = 0;
}
fgets(ch, 2, stdin);
while (getchar() != '\n');
switch (ch[0])
{
case '1': // 查看当前在线人数
display(socketfd);
break;
case '2': // 进入群聊界面
chat1(socketfd);
printf("\n\t回车键返回 \n");
getchar();
break;
case '3': // 进入私聊界面
chat2(socketfd);
printf("\n\t回车键返回 \n");
getchar();
break;
case '4': // 查看聊天记录
dis_histroy(socketfd);
getchar();
break;
case '5': // 文件传输
convey(socketfd);
break;
case '6': // 更改密码
change_pass(socketfd);
break;
case '7': // 在线注销
printf("\n\t是否确认注销?(y/n): ");
fgets(ch, 2, stdin);
while (getchar() != '\n');
if (ch[0] != 'y')
{
printf("\n\t请稍候.....\n");
Sleep(50);
break;
}
delete_user(socketfd);
case 'q': // 退出聊天室 返回登录界面
flag1 = 1;
quit_chatroom(socketfd);
printf("\n\t正在退出,请稍候......\n");
break;
}
if (flag1 == 1) // 判断退出条件
{
break;
}
system("clear");
}
}
// 将成员禁言
void silent(int socketfd)
{
msg.cmd = 9011;
strcpy(msg.fromname, myName);
printf("\n\t请输入您要禁言的用户:\n\t");
scanf("%s", msg.toname);
getchar();
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
printf("\n\t操作完成,请稍候.....\n");
Sleep(50);
}
// 将成员解除禁言
void silent_del(int socketfd)
{
msg.cmd = 9012;
strcpy(msg.fromname, myName);
printf("\n\t请输入您要解除禁言的用户:\n\t");
scanf("%s", msg.toname);
getchar();
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
printf("\n\t操作完成,请稍候.....\n");
Sleep(50);
}
// 将成员踢出聊天室
void kickout(int socketfd)
{
msg.cmd = 9013;
strcpy(msg.fromname, myName);
printf("\n\t请输入您要踢出的用户:\n\t");
scanf("%s", msg.toname);
getchar();
//_write(socketfd, &msg, sizeof(struct Msg)); //向服务器发送请求
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
printf("\n\t操作完成,请稍候.....\n");
Sleep(50);
}
// 管理员权限
void supuser(int socketfd)
{
system("clear");
printf("\t*************************** 网络聊天室 *****************************\n\n\n");
printf("\t 1、将成员禁言\n");
printf("\t 2、将成员解除禁言\n");
printf("\t 3、将成员踢出聊天室\n");
printf("\n\t***** 请选择: ");
char ch[2];
fgets(ch, 2, stdin);
while (getchar() != '\n');
switch (ch[0])
{
case '1': // 将成员禁言
silent(socketfd);
break;
case '2': // 将成员解除禁言
silent_del(socketfd);
break;
case '3': // 将成员踢出聊天室
kickout(socketfd);
break;
}
printf("\n\t回车键返回 ");
getchar();
}
// 管理员操作
void supuser_do(int socketfd)
{
char ch[2];
while (1)
{
interface3();
if (flag3 == 1)
{
printf("\n\n\t%s 请求传输文件,是否接收?(y/n):", msg.fromname);
fflush(stdout);
fgets(ch, 2, stdin);
while (getchar() != '\n');
if (ch[0] != 'y')
{
printf("\n\t您已拒绝接受文件传输\n");
printf("\n\t回车键返回 \n");
}
else
{
printf("\n\t您已接受文件传输请求\n");
printf("\n\t回车键返回 \n");
flag2 = 1;
}
if (flag2 == 0)
{
msg.cmd = 9005; // 不接受文件
strcpy(msg.toname, msg.fromname); // 修改信号发送对象
strcpy(msg.fromname, myName);
//_write(socketfd, &msg, sizeof(struct Msg));
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
}
else if (flag2 == 1)
{
msg.cmd = 9006; // 接受文件
strcpy(msg.toname, msg.fromname); // 修改信号发送对象
strcpy(msg.fromname, myName);
//_write(socketfd, &msg, sizeof(struct Msg));
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
}
flag3 = 0; // 重置是否存在文件接收请求的判断
flag2 = 0;
}
fgets(ch, 2, stdin);
while (getchar() != '\n');
switch (ch[0])
{
case '1': // 查看当前在线人数
display(socketfd);
break;
case '2': // 进入群聊界面
chat1(socketfd);
printf("\n\t回车键返回 \n");
getchar();
break;
case '3': // 进入私聊界面
chat2(socketfd);
printf("\n\t回车键返回 \n");
getchar();
break;
case '4': // 查看聊天记录
dis_histroy(socketfd);
getchar();
break;
case '5': // 文件传输
convey(socketfd);
break;
case '6': // 更改密码
change_pass(socketfd);
break;
case '8': // 管理员权限操作
supuser(socketfd);
break;
case '7': // 在线注销
printf("\n\t是否确认注销?(y/n): ");
fgets(ch, 2, stdin);
while (getchar() != '\n');
if (ch[0] != 'y')
{
printf("\n\t请稍候.....\n");
Sleep(50);
break;
}
delete_user(socketfd);
case 'q': // 退出聊天室 返回登录界面
flag1 = 1;
quit_chatroom(socketfd);
printf("\n\t正在退出,请稍候......\n");
break;
}
if (flag1 == 1) // 判断退出条件
{
break;
}
system("clear");
}
}
// 登录
void log_in(int socketfd)
{
char password[20];
msg.cmd = 2;
printf("\n\t用户登录:\n");
printf("\t请输入用户名: ");
scanf("%s", myName);
getchar();
printf("\t请输入密码: ");
scanf("%s", password);
getchar();
strcpy(msg.fromname, myName);
strcpy(msg.msg, password);
send(socketfd, (char *)&msg, sizeof(struct Msg), 0);
//_write(socketfd, &msg, sizeof(struct Msg)); // 向服务器发送登录请求
recv(socketfd, (char *)&msg, sizeof(struct Msg), 0);
//_read(socketfd, &msg, sizeof(struct Msg)); // 读取服务器的登录回应
printf("\n\t正在校验数据......\n");
Sleep(50);
if (msg.cmd == 1002)
{
printf("\n\t验证通过,正在登录......\n");
Sleep(50);
flag4 = msg.sig; // 更新禁言状态
// 线程分离,用来监听服务器返回信息
pthread_t id;
pthread_create(&id, NULL, _readMsg, (void*)socketfd);
pthread_detach(id);
user_do(socketfd);
}
else if (msg.cmd == 1003)
{
printf("\n\t验证通过,正在登录......\n");
Sleep(50);
// 线程分离,用来监听服务器返回信息
pthread_t id;
pthread_create(&id, NULL, _readMsg, (void*)socketfd);
pthread_detach(id);
supuser_do(socketfd);
}
else if (msg.cmd == -4)
{
printf("\n\t此账号已在别处登录\n");
}
else if (msg.cmd == -3)
{
printf("\n\t验证失败,请您确认信息后重新登录\n");
}
else if (msg.cmd == -2)
{
printf("\t验证失败,数据库打开失败\n");
}
else if (msg.cmd == -1)
{
printf("\t数据库操作失败\n");
}
Sleep(50);
}
// 注册(可注册管理员)
void reg(int socketfd)
{
msg.cmd = 1;
printf("\t用户注册:\n");
printf("\t请输入用户名: ");
scanf("%s", myName);
getchar();
printf("\t请输入密码: ");
scanf("%s", msg.msg);
getchar();
printf("\t管理员: ");
scanf("%d", &msg.sig);
getchar();
strcpy(msg.fromname, myName);
send(socketfd, (char *)&msg, sizeof(struct Msg),0);
//_write(socketfd, &msg, sizeof(struct Msg)); // 向服务器发送注册信息
recv(socketfd, (char *)&msg, sizeof(struct Msg), 0);
//_read(socketfd, &msg, sizeof(struct Msg)); // 读取服务器的注册回应
printf("\n\t正在校验数据......\n");
Sleep(50);
if (msg.cmd == 1001)
{
printf("\n\t注册成功!\n\t请稍候......\n");
}
else if (msg.cmd == -1)
{
printf("\t注册失败,该用户名已被注册\n");
}
else if (msg.cmd == -2)
{
printf("\t注册失败,数据库打开失败\n");
}
Sleep(50);
}
// 向服务器发送请求
void ask_server(int socketfd)
{
char ch[2];
while (1)
{
interface1();
fgets(ch, 2, stdin);
while (getchar() != '\n');
switch (ch[0])
{
case '1': // 注册
reg(socketfd);
break;
case '2': // 登录
log_in(socketfd);
break;
case 'q': // 退出
exit(1);
}
system("clear");
}
}
//初始化网络库
void InitNetwork() {
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}
}
int main(int argc, char **argv)
{
InitNetwork();
// 打开数据库Histroy.db
int ret = sqlite3_open("Histroy.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
return -1;
}
// 创建 histroy 表
char *errmsg = NULL;
const char *sql = "create table if not exists histroy(fromname TEXT,toname TEXT,msg TEXT)";
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return -1;
}
// 关闭数据库
sqlite3_close(database);
// 创建与服务器通信的套接字
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if (socketfd == -1)
{
perror("socket");
return -1;
}
// 连接服务器
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // 设置地址族
addr.sin_port = htons(PORT); // 设置本地端口
addr.sin_addr.s_addr = inet_addr(argv[1]);
//inet_aton(argv[1], &(addr.sin_addr));
// 连接服务器,如果成功,返回0,如果失败,返回-1
// 成功的情况下,可以通过socketfd与服务器进行通信
ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
perror("connect");
return -1;
}
printf("成功连上服务器\n");
ask_server(socketfd);
// 关闭套接字
_close(socketfd);
return 0;
}
server.cpp
#include <sys/types.h>
#include<winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <io.h>
#include <pthread.h>
#include <ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#define PORT 9999
sqlite3 * database;
// 协议
struct Msg
{
char msg[1024]; // 消息内容
int cmd; // 消息类型
char filename[50]; // 保存文件名
char toname[20]; // 接收者姓名
char fromname[20]; // 发送者姓名
int sig; // 用户状态(0:管理员、1:普通用户、2:被禁言)
};
// 初始化套接字,返回监听套接字
int init_socket()
{
//1、创建socket
int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket == -1)
{
perror("socket");
return -1;
}
// 2、命名套接字,绑定本地的ip地址和端口
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // 设置地址族
addr.sin_port = htons(PORT); // 设置本地端口
//addr.sin_addr.s_addr = htonl(INADDR_ANY); // 使用本地的任意IP地址
addr.sin_addr.s_addr = inet_addr("xxx.xx.x.x");
int ret = bind(listen_socket, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1)
{
perror("bind");
return -1;
}
// 3、监听本地套接字
ret = listen(listen_socket, 5);
if (ret == -1)
{
perror("listen");
return -1;
}
printf("服务器已就绪,等待客户端连接.......\n");
return listen_socket;
}
// 处理客户端连接,返回与连接上的客户端通信的套接字
int MyAccept(int listen_socket)
{
// 4、接收连接
struct sockaddr_in client_addr; // 用来保存客户端的ip和端口信息
int len = sizeof(client_addr);
int client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &len);
if (client_socket == -1)
{
perror("accept");
}
printf("成功接收一个客户端: %s\n", inet_ntoa(client_addr.sin_addr));
return client_socket;
}
// 查看当前在线人数
void display(int client_socket, struct Msg *msg)
{
printf("查看当前在线人数\n");
// 确认flag参数
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 与User表中信息进行比对
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
int count = 0;
int i;
for (i = 3 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], "1") == 0)
{
count++;
}
}
// 返回在线人数
msg->cmd = count;
printf("当前在线人数为:%d\n", msg->cmd);
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
printf("操作完成,已关闭数据库\n");
}
// 退出聊天室,返回登录界面
void quit_chatroom(int client_socket, struct Msg *msg)
{
printf("%s 退出聊天室\n", msg->fromname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
return;
}
char buf[100];
char *errmsg = NULL;
errmsg = NULL;
sprintf(buf, "update user set flag = 0 where name = '%s'", msg->fromname);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
sqlite3_close(database);
printf("登录状态修改完毕,已关闭数据库\n");
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 客户端发送群聊消息
void chat1(int client_socket, struct Msg *msg)
{
printf("%s 发了一条群消息\n", msg->fromname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取数据库中的flag参数信息
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
int i;
for (i = 3 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
// 查询所有在线的用户
if (strcmp(resultp[i], "1") == 0)
{
msg->cmd = 9001;
send(atoi(resultp[i - 1]), (char *)msg, sizeof(struct Msg),0);
//_write(atoi(resultp[i - 1]), msg, sizeof(struct Msg));
}
}
printf("群消息已全部发送完成\n");
}
// 客户端发送私聊消息
void chat2(int client_socket, struct Msg *msg)
{
printf("%s 向 %s 发了一条消息\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取数据库中的flag参数信息,判断是否在线
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
msg->cmd = 9002;
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 处理确认文件传输对象
void convey_confirm(int client_socket, struct Msg *msg)
{
printf("%s 向 %s 发送文件传输请求\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
return;
}
// 获取数据库中的flag参数信息
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
msg->cmd = 9004;
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 用户不接受文件
void refuse(int client_socket, struct Msg *msg)
{
printf("%s 拒绝了 %s 的文件传输请求\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
return;
}
// 获取数据库中 toname 的套接字
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 用户接受文件
void accept_(int client_socket, struct Msg *msg)
{
printf("%s 通过了 %s 的文件传输请求\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
return;
}
// 获取数据库中 toname 的套接字
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 处理文件传输
void convey_chose(int client_socket, struct Msg *msg)
{
printf("%s正在向%s传输文件......\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取数据库中的 flag 参数信息,判断是否在线
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取toname的套接字
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 文件传输完成
void convey_complete(int client_socket, struct Msg *msg)
{
printf("文件传输结束\n");
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取数据库中的 flag 参数信息,判断是否在线
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取toname的套接字
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 更改密码
void change_pass(int client_socket, struct Msg *msg)
{
printf("%s请求修改密码\n", msg->fromname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 与User表中信息进行比对
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from user";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->fromname) == 0 && strcmp(resultp[i + 1], msg->msg) == 0)
{
// 返回确认信息
msg->cmd = 9009;
printf("%s 验证通过\n", msg->fromname);
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
// 修改密码
char buf[100];
errmsg = NULL;
sprintf(buf, "update user set password = '%s' where name = '%s'", msg->filename, msg->fromname);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
printf("密码修改完成,已关闭数据库\n");
return;
}
}
printf("%s 验证不通过,密码输入有误\n", msg->fromname);
msg->cmd = 9010;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
printf("操作完成,已关闭数据库\n");
}
// 客户端请求在线注销
void delete_user(int client_socket, struct Msg *msg)
{
printf("即将处理用户注销\n");
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
return;
}
// 删除 user 表中的信息
char buf[100];
char *errmsg = NULL;
sprintf(buf, "delete from user where name = '%s'", msg->fromname);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
// 关闭数据库
sqlite3_close(database);
printf("删除成功,已关闭数据库\n");
}
// 处理禁言请求
void silent(int client_socket, struct Msg *msg)
{
printf("正在处理管理员 %s 对成员 %s 的禁言请求\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取数据库中的 flag 参数信息,判断是否在线
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取toname的套接字
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
msg->cmd = 9011;
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
char buf[100];
errmsg = NULL;
sprintf(buf, "update user set sig = 2 where name = '%s'", msg->toname);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
sqlite3_close(database);
printf("禁言状态修改完毕,已关闭数据库\n");
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
sqlite3_close(database);
printf("用户不在线,修改失败,已关闭数据库\n");
}
// 处理解除禁言请求
void silent_del(int client_socket, struct Msg *msg)
{
printf("正在处理管理员 %s 对成员 %s 的解除禁言请求\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取数据库中的 flag 参数信息,判断是否在线
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取toname的套接字
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
msg->cmd = 9012;
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
char buf[100];
errmsg = NULL;
sprintf(buf, "update user set sig = 1 where name = '%s'", msg->toname);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
sqlite3_close(database);
printf("禁言状态修改完毕,已关闭数据库\n");
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
sqlite3_close(database);
printf("用户不在线,修改失败,已关闭数据库\n");
}
// 处理踢出成员
void kickout(int client_socket, struct Msg *msg)
{
printf("正在处理管理员 %s 对成员 %s 的踢出请求\n", msg->fromname, msg->toname);
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 获取数据库中的 flag 参数信息,判断是否在线
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from User";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
// 获取toname的套接字
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->toname) == 0 && strcmp(resultp[i + 3], "1") == 0)
{
msg->cmd = 9013;
send(atoi(resultp[i + 2]), (char *)msg, sizeof(struct Msg), 0);
//_write(atoi(resultp[i + 2]), msg, sizeof(struct Msg));
char buf[100];
errmsg = NULL;
sprintf(buf, "update user set flag = 0 where name = '%s'", msg->toname);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return;
}
sqlite3_close(database);
printf("踢出完毕,已关闭数据库\n");
return;
}
}
msg->cmd = 9003;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
sqlite3_close(database);
printf("用户不在线,修改失败,已关闭数据库\n");
}
// 处理用户操作请求函数
void user_do(int client_socket)
{
struct Msg msg;
int sig = 0;
while (1)
{
// 从客户端读一个结构体数据
int ret = recv(client_socket, (char *)&msg, sizeof(msg), 0);
//int ret = _read(client_socket, &msg, sizeof(msg));
if (ret == -1)
{
perror("_read");
break;
}
// 代表客户端退出
if (ret == 0)
{
printf("客户端返回登录界面\n");
break;
}
switch (msg.cmd)
{
case 10: // 退出聊天室,返回登录界面
quit_chatroom(client_socket, &msg);
sig = 1;
break;
case 1: // 查看当前在线人数
display(client_socket, &msg);
break;
case 2: // 处理群聊消息
chat1(client_socket, &msg);
break;
case 3: // 处理私聊消息
chat2(client_socket, &msg);
break;
case 5: // 处理确认文件传输对象
convey_confirm(client_socket, &msg);
break;
case 6: // 更改密码
change_pass(client_socket, &msg);
break;
case 8: // 处理在线注销
delete_user(client_socket, &msg);
break;
case 9005: // 用户不接受文件
refuse(client_socket, &msg);
break;
case 9006: // 用户接受文件
accept_(client_socket, &msg);
break;
case 9007: // 处理文件传输
convey_chose(client_socket, &msg);
break;
case 9008: // 文件传输完成
convey_complete(client_socket, &msg);
break;
case 9011: // 处理禁言请求
silent(client_socket, &msg);
break;
case 9012: // 处理解除禁言请求
silent_del(client_socket, &msg);
break;
case 9013: // 处理踢出成员
kickout(client_socket, &msg);
break;
}
if (sig == 1)
{
printf("即将退出普通用户操作请求函数\n");
break;
}
}
}
// 处理客户端的登录请求
void log_in(int client_socket, struct Msg *msg)
{
printf("%s 请求登录\n", msg->fromname);
// 将用户信息进行比对
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 与User表中信息进行比对
char *errmsg = NULL;
char **resultp = NULL;
int nrow, ncolumn;
const char *sql = "select * from user";
ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
int i;
for (i = 0 + ncolumn; i < (nrow + 1)*ncolumn; i += ncolumn)
{
if (strcmp(resultp[i], msg->fromname) == 0 && strcmp(resultp[i + 1], msg->msg) == 0)
{
if (strcmp(resultp[i + 3], "1") == 0)
{
msg->cmd = -4;
printf("%s 已经在别处登录\n", msg->fromname);
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
printf("操作完成,已关闭数据库\n");
return;
}
if (strcmp(resultp[i + 4], "0") != 0)
{
// 普通用户
msg->cmd = 1002;
msg->sig = atoi(resultp[i + 4]);
printf("普通用户 %s 验证通过\n", msg->fromname);
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
else
{
// 管理员
msg->cmd = 1003;
msg->sig = atoi(resultp[i + 4]);
printf("管理员 %s 验证通过\n", msg->fromname);
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
}
// 修改在线状态、更新套接字
char buf[100];
errmsg = NULL;
sprintf(buf, "update user set socket = '%d',flag = 1 where name = '%s'", client_socket, msg->fromname);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
printf("在线状态已更新,已关闭数据库\n");
printf("进入用户操作请求处理功能\n");
user_do(client_socket);
return;
}
}
printf("%s 验证不通过\n", msg->fromname);
msg->cmd = -3;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
sqlite3_free_table(resultp);
// 关闭数据库
sqlite3_close(database);
printf("操作完成,已关闭数据库\n");
}
// 处理客户端的注册请求
void reg(int client_socket, struct Msg *msg)
{
printf("%s 进行注册\n", msg->fromname);
// 将用户进行保存
// 打开数据库
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
msg->cmd = -2;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 往User表中添加信息
char buf[100];
char *errmsg = NULL;
sprintf(buf, "insert into user values('%s','%s',%d,%d,%d)", msg->fromname, msg->msg, client_socket, 0, msg->sig);
ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
msg->cmd = -1;
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
return;
}
// 返回确认信息
msg->cmd = 1001;
printf("%s 注册成功\n", msg->fromname);
send(client_socket, (char *)msg, sizeof(struct Msg), 0);
//_write(client_socket, msg, sizeof(struct Msg));
// 关闭数据库
sqlite3_close(database);
printf("操作完成,已关闭数据库\n");
}
// 线程的工作函数,即处理客户端请求的函数
void* hanld_client(void* v)
{
int client_socket = (int)v;
printf("%d\n",client_socket);
struct Msg msg;
while (1)
{
printf("处理客户端请求的函数函数已就绪\n");
// 从客户端读一个结构体数据
int ret = recv(client_socket, (char *)&msg, sizeof(msg), 0);
//int ret = _read(client_socket, &msg, sizeof(msg));
if (ret == -1)
{
perror("_read");
break;
}
// 代表客户端退出
if (ret == 0)
{
printf("客户端退出\n");
break;
}
switch (msg.cmd)
{
case 1: // 客户端进行注册
reg(client_socket, &msg);
break;
case 2: // 客户端进行登录
log_in(client_socket, &msg);
break;
}
}
_close(client_socket);
}
//初始化网络库
void InitNetwork() {
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}
}
int main()
{
InitNetwork();
// 打开数据库User.db
int ret = sqlite3_open("User.db", &database);
if (ret != SQLITE_OK)
{
printf("打开数据库失败\n");
return -1;
}
// 创建 user 表
char *errmsg = NULL;
const char *sql = "create table if not exists user(name TEXT,password TEXT,socket INTEGER,flag INTEGER,sig INTEGER,primary key(name))";
ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
printf("数据库操作失败:%s\n", errmsg);
return -1;
}
printf("数据库准备就绪......\n");
// 关闭数据库
sqlite3_close(database);
// 初始化套接字
int listen_socket = init_socket();
while (1)
{
// 获取与客户端连接的套接字
int client_socket = MyAccept(listen_socket);
// 创建一个线程去处理客户端的请求,主线程依然负责监听
pthread_t id;
pthread_create(&id, NULL, hanld_client, (void *)client_socket);
pthread_detach(id); // 线程分离
}
_close(listen_socket);
return 0;
}