Windows 上のネットワーク チャット ルーム プロジェクト (C++、SQLite、コンピューター ネットワーク) - アイデア + ソース コード + 分析

Windows上でC++で作られた簡易的なチャットルームで、インターフェースは低めですが、必要な機能は概ね実現されています。インターフェースとしては、QT にパッケージ化された QTcpSocket ソケットがあります。これは次のブログで紹介します。主な機能には主に次の機能が含まれます: 現在のオンライン番号の確認、グループ チャット インターフェイスへの入力、プライベート チャット インターフェイスへの入力、チャット履歴の表示、ファイル転送、パスワードの変更、オンライン ログアウト、終了など。
ここに画像の説明を挿入

1. プログラムの調査
1.1 クライアントプログラム
1. まず、クライアントがサーバーに接続した後、ユーザー登録、ログイン、およびログアウトインターフェイスが必要です 2. 登録時には、ユーザー名、
ここに画像の説明を挿入
パスワード、およびログインの有無を設定する必要があります管理者 (注: ここでは、ユーザー名、パスワード、その他の情報を保存するためのコード内に構造を設定し、これらの情報をサーバーに送信して登録し、最後にサーバーのフィードバックから登録成功情報を取得する必要があります。 3.ログインする
ここに画像の説明を挿入
ときは、ユーザー名とパスワード情報を入力し、サーバーにログイン要求を送信する必要があります。ユーザーの登録情報がサーバーに保存されている場合は、 (ここでは、返されるメッセージの違いにより管理者と一般ユーザーを区別し、状況に応じて対処します(インターフェースが異なります))。次のステップでは、サーバーの戻り情報とクライアント自身の操作を監視するために使用されるスレッド分離が必要です。
ここに画像の説明を挿入
4. 通常のユーザー インターフェース (主な機能には、現在のオンライン ユーザー数の確認、グループ チャット インターフェースへの入力、プライベート チャット インターフェースへの入力、チャット記録の表示、ファイル転送、パスワードの変更、オンライン ログアウト、終了などが含まれます)、これらの関数は主にサーバーに渡されます。サーバーはリクエストを送信し、サーバーから分離された (前のステップ) スレッドによってリクエストをリッスンします。
ここに画像の説明を挿入
5. 管理者インターフェースには通常のユーザーより 1 つ多くの管理者インターフェースがあり、主にチャット ルームからの禁止、ミュート解除、および追い出しの機能があります。 6. 現在のオンライン番号の確認機能: send( を通じてサーバーにのみ送信します
ここに画像の説明を挿入
ここに画像の説明を挿入
) ) 関数 リクエストを送信し、別のスレッド監視を通じてサーバー側に保存されているオンライン番号を取得します。効果のイメージは次のとおりです (この時点では、Ban 氏 (管理者)、Zhang San、Li Si がいます)
ここに画像の説明を挿入
7. グループ チャット インターフェイスに入ります。また、send() 関数を通じてサーバーにリクエストを送信し、送信します。サーバーを通じてオンラインの全員に情報を提供し、スレッドはサーバーのフィードバック結果を取得します。効果図は次のとおりです。
ここに画像の説明を挿入
8. プライベート チャット インターフェイスに入ります。また、send() 関数を通じてサーバーにリクエストを送信し、サーバーを通じて送信されるオブジェクトに情報を送信し、サーバーからフィードバック結果を取得します。別のスレッド。レンダリングは次のとおりです
ここに画像の説明を挿入
9. チャット記録の表示: グループ チャット記録の表示と個人記録の表示には主に 2 つのタイプがあり、主にクライアントによって作成されたデータベースを開いて、select * from histroy コマンドを使用して出力を入力します。
ここに画像の説明を挿入
10. ファイル転送は主にファイルを開いて、その内容を send() 関数を通じてサーバーに送信し、サーバーは送信内容に従って送信対象のオブジェクトにファイルを送信します。
ここに画像の説明を挿入
11. パスワードの変更、オンライン ログアウト: 登録情報もサーバーに送信します。

ここに画像の説明を挿入
1.2 サーバースキーム
1. クライアント登録情報 send() からの情報 (主にユーザー、パスワード、接続されたソケット、メッセージ内容、メッセージタイプ、受信者名、ユーザーステータスなどを含む) を保存するユーザーテーブル (SQLite データベース) を作成します。 )
2. さまざまなユーザーからの接続要求を受け入れるループと、処理用の別個のスレッド;
3. クライアント登録要求を処理するときに、データベースを開いてユーザー情報を保存します。使用されるステートメントは次のとおりです: insert into uservalues(' %s',' %s',%d,%d,%d)、成功情報をクライアントにフィードバックします; 4.
クライアントのログイン要求を処理するとき、最初にデータベースを開いてユーザー情報を比較し、(一般ユーザーと管理者は別個に処理する必要があります)、オンライン ステータスが存在する場合は変更し、ソケットを更新します (同じクライアントが接続するたびに異なります)。 5. オンライン ステータス
とソケットを変更した後、次のステップは情報を受信することです。クライアントからメッセージを送信し、さまざまなメッセージ タイプに応じてさまざまな機能を呼び出します (現在のオンライン番号の確認、グループ チャット メッセージの処理、プライベート チャット メッセージの処理など)。

2. ソースコードの実装
次にコードの表示です(不完全な部分もありますがご容赦ください)
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;
}


サーバー.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;
}

おすすめ

転載: blog.csdn.net/AAAA202012/article/details/130131476