Proyecto de sala de chat en red bajo Windows (C++, SQLite, red informática) - idea + código fuente + análisis

Esta es solo una sala de chat simple hecha con C++ bajo el sistema de Windows.Su interfaz es relativamente baja, pero las funciones necesarias se han realizado básicamente. En cuanto a la interfaz, hay un socket QTcpSocket empaquetado en QT, que se presentará en el próximo blog. Las funciones principales incluyen principalmente las siguientes funciones: verificar el número en línea actual, ingresar a la interfaz de chat grupal, ingresar a la interfaz de chat privado, ver el historial de chat, transferir archivos, cambiar la contraseña, cerrar sesión en línea, salir, etc.
inserte la descripción de la imagen aquí

1. Investigación del programa
1.1 Programa del cliente
1. Primero, después de que el cliente esté conectado al servidor, debe haber interfaces de registro de usuario, inicio de sesión y cierre de sesión
inserte la descripción de la imagen aquí
2. Al registrarse, debe establecer el nombre de usuario, la contraseña y si está un administrador (nota: aquí debe configurar una estructura en el código para guardar el nombre de usuario, la contraseña y otra información, y luego enviar esta información al servidor para registrarse, y finalmente obtener la información de éxito del registro de los comentarios del servidor, después el registro es exitoso, debe volver a la interfaz inicial)
inserte la descripción de la imagen aquí
3. Al iniciar sesión, debe ingresar el nombre de usuario y la información de la contraseña, y luego enviar una solicitud de inicio de sesión al servidor. Si la información de registro del usuario está almacenada en el servidor, se devolverá un mensaje (aquí, los administradores y los usuarios normales se distinguen según la diferencia en el mensaje devuelto, luego lo tratan de acuerdo con la situación (la interfaz es diferente)). El siguiente paso requiere la separación de subprocesos, que se utiliza para monitorear la información de retorno del servidor y las propias operaciones del cliente.
inserte la descripción de la imagen aquí
4. Interfaz de usuario ordinaria (las funciones principales incluyen: verificar el número actual de usuarios en línea, ingresar a la interfaz de chat grupal, ingresar a la interfaz de chat privado, ver registros de chat, transferir archivos, cambiar contraseña, cerrar sesión en línea, salir, etc.), estas funciones se pasan principalmente a El servidor envía la solicitud, que luego es escuchada por un hilo (anteriormente) separado.
inserte la descripción de la imagen aquí
5. La interfaz de administrador tiene una interfaz de administrador más que los usuarios normales, que principalmente tiene la función de prohibir, desactivar el silencio y expulsar de la sala de chat. 6. Verifique la función de
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
número en línea actual: solo envíe al servidor a través del envío ( ) función Envíe una solicitud y obtenga la cantidad de usuarios en línea guardados en el lado del servidor a través de un monitoreo de hilo separado. La imagen del efecto es la siguiente (en este momento están el Sr. Ban (administrador), Zhang San y Li Si)
inserte la descripción de la imagen aquí
7. Ingrese a la interfaz de chat grupal: también envíe una solicitud al servidor a través de la función enviar () y envíe información a todos en línea a través del servidor. El hilo obtiene el resultado de la retroalimentación del servidor. El diagrama de efectos es el siguiente
inserte la descripción de la imagen aquí
8. Ingrese a la interfaz de chat privado: también envíe una solicitud al servidor a través de la función enviar (), envíe información al objeto para que se envíe a través del servidor y obtenga el resultado de la retroalimentación del servidor en un hilo separado. Las representaciones son las siguientes
inserte la descripción de la imagen aquí
9. Ver registros de chat: hay principalmente dos tipos de ver registros de chat grupales y registros personales, principalmente abriendo la base de datos creada por el cliente e ingresando la salida a través del comando select * from histroy.
inserte la descripción de la imagen aquí
10. La transferencia de archivos es principalmente para abrir el archivo y luego enviar su contenido al servidor a través de la función enviar (), y luego el servidor lo envía al objeto para que se envíe de acuerdo con el contenido que envía.
inserte la descripción de la imagen aquí
11. Cambiar contraseña, cerrar sesión en línea: también enviar información de registro al servidor.

inserte la descripción de la imagen aquí
1.2 Esquema del servidor
1. Cree una tabla de usuario (base de datos SQLite) para almacenar la información del envío de información de registro del cliente() (que incluye principalmente usuario, contraseña, socket conectado, contenido del mensaje, tipo de mensaje, nombre del destinatario, estado del usuario, etc. )
2. Bucle para aceptar solicitudes de conexión de diferentes usuarios y subprocesos separados para el procesamiento;
3. Al procesar solicitudes de registro de clientes, abra la base de datos y guarde la información del usuario. La instrucción utilizada es: insert into user values(' %s',' %s',%d,%d,%d), y luego envíe la información de éxito al cliente; 4.
Al procesar la solicitud de inicio de sesión del cliente, primero abra la base de datos y compare la información del usuario, preste atención a (Usuarios normales y los administradores deben manejarse por separado), y luego cambiar su estado en línea si existe, y actualizar el socket (será diferente cada vez que se conecte el mismo cliente) 5.
Después de cambiar el estado en línea y el socket, el siguiente paso es recibir información desde el cliente y llamar a diferentes funciones de acuerdo con los diferentes tipos de mensajes (verificar el número en línea actual, procesar mensajes de chat grupales, procesar mensajes de chat privados, etc.).

2. Implementación del código fuente
Lo siguiente es la visualización del código (todavía hay algunas imperfecciones, por favor perdónenme)
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;
}


servidor.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;
}

Supongo que te gusta

Origin blog.csdn.net/AAAA202012/article/details/130131476
Recomendado
Clasificación