網絡編程項目:linux下基於C/S架構的聊天室

一、項目要求:

1. 採用 Client/Server 架構 

2. Client A 登陸聊天服務器前,需要註冊自己的 ID 和密碼 
3. 註冊成功後,Client A 就可以通過自己的 ID 和密碼登陸聊天服務器 
4. 多個 Client X 可以同時登陸聊天服務器之後,與其他用戶進行通訊聊天 
5. Client A 成功登陸後可以查看當前聊天室內其他在線用戶 Client x 
6. Client A 可以選擇發消息給某個特定的 Client X,即”悄悄話”功能 
7. Client A 可以選擇發消息全部的在線用戶,即”羣發消息”功能 
8. Client A 在退出時需要保存聊天記錄 

9. Server 端維護一個所有登陸用戶的聊天會的記錄文件,以便備查 

10.可以進行文件傳輸

二、附加功能:

1. Server 可以內建一個特殊權限的賬號 root,即超級管理員,用於管理聊天室 
2. Admin 可以將某個 Client X “提出聊天室” 
3. Admin 可以將某個 Client X ”設爲只能旁聽,不能發言,即禁言功能


三、項目所需相關知識點:網絡編程(C/S模型的架構),文件I/O編程(將用戶信息和聊天記錄保存在數據庫文件中,文件傳輸),多進程或多線程編程(一個服務器可以連接多個客戶端)


四、項目使用(測試)說明:將client.c, client.c和server.c分別置於不同目錄下,編譯命令:gcc  client.c  -o  client   -lpthread  -lsqlite3

運行命令:./client     (server.c同樣編譯運行)


五、項目代碼

服務器:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sqlite3.h>

#include <sys/stat.h>
#include <fcntl.h>

#define SIZE 20
#define PORT  9999
#define TRUE   1
#define FALSE -1

struct Usr
{
	char name[SIZE];
	int socket;
	int flag;//用來判斷該用戶是否被禁言,沒有被禁設爲0,被禁設爲1
};

struct Msg
{
	struct Usr usr[SIZE];
	char msg[1024];
	char buf[1024];
	char name[SIZE];
	char fromname[SIZE];
	char toname[SIZE];
	char password[SIZE];
	int cmd;
	int filesize;
	int flag;    //用來判斷用戶權限   0代表普通用戶,1代表超級用戶
};

struct Usr usr[SIZE];
int count;

pthread_mutex_t mutex; //互斥鎖,用來避免兩個客戶端同時訪問全局變量

//查看在線用戶
void see_online(int client_socket, struct Msg *msg)
{
	int i;
	for(i=0; i<20; i++)
	{
		msg->usr[i] = usr[i];
	}
	
	write(client_socket, msg, sizeof(struct Msg));
}

//保存一條聊天記錄
void insert_record(struct Msg *msg)
{
	sqlite3 * database;
	int ret = sqlite3_open("allrecord.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return ;
	}
	
	//獲取系統當前時間
	time_t timep;  
    char s[30];   
    time(&timep);  
    strcpy(s,ctime(&timep));
	int count = strlen(s);
	s[count-1] = '\0';
	
	char *errmsg = NULL;
	char *sql = "create table if not exists allrecord(time TEXT,fromname TEXT,toname TEXT,word TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("聊天記錄表創建失敗:%s\n", errmsg);
		return;
	}
	
	char buf[100];
	sprintf(buf, "insert into allrecord values('%s','%s','%s','%s')",s,msg->fromname, msg->toname,msg->msg);
	ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("添加聊天記錄失敗:%s\n", errmsg);
		return ;
	}
	
	sqlite3_close(database);
	return ;
}

//羣聊
void chat_group(int client_socket, struct Msg *msg)
{
	int i = 0;
	for (i = 0; i < SIZE; i++)
	{
		if (usr[i].socket != 0 && strcmp(msg->fromname, usr[i].name) == 0)
		{
			break;
		}
	}
	if(usr[i].flag == 0)     //判斷該用戶有沒有被禁言
	{
		printf ("%s 發一次羣消息\n", msg->fromname);
		insert_record(msg);
		
		for (i = 0; i < SIZE; i++)
		{
			if (usr[i].socket != 0 && strcmp(msg->fromname, usr[i].name) != 0)
			{
				write (usr[i].socket, msg, sizeof(struct Msg));	
			}
		}
	}
	else
	{
		msg->cmd = 1003;
		write (client_socket, msg, sizeof(struct Msg));
	}
	
}

//私聊
void chat_private(int client_socket, struct Msg *msg)
{
	int i;
	for (i = 0; i < SIZE; i++)
	{
		if (usr[i].socket != 0 && strcmp(msg->fromname, usr[i].name) == 0)
		{
			break;
		}
	}
	if(usr[i].flag == 0)
	{
		printf("%s給%s發了一條消息\n", msg->fromname, msg->toname);
		insert_record(msg);
		
		for (i = 0; i < SIZE; i++)
		{
			if (usr[i].socket != 0 && strcmp(usr[i].name, msg->toname)==0)
			{
				write (usr[i].socket, msg, sizeof(struct Msg));	
				break;
			}
		}
	}
	else
	{
		msg->cmd = 1003;
		write (client_socket, msg, sizeof(struct Msg));
	}
}

//獲取文件大小
int file_size(char* filename)  
{  
    struct stat statbuf;  
    stat(filename,&statbuf);  
    int size=statbuf.st_size;  
  
    return size;  
}

//上傳文件
void send_file(int client_socket, struct Msg *msg)
{
	printf("用戶%s在聊天室內上傳了一個文件%s\n",msg->fromname,msg->msg);
	
	int i;
	for (i = 0; i < SIZE; i++)
	{
		if (usr[i].socket != 0 && strcmp(usr[i].name, msg->fromname) != 0)
		{
			write (usr[i].socket, msg, sizeof(struct Msg));	
			break;
		}
	}
	
	int fd = open(msg->msg, O_RDWR | O_CREAT, 0777);
	if(fd == -1)
	{
		perror("open");
		printf("文件傳輸失敗\n");
	}
	
	int size = msg->filesize;
	char buf[65535];
	memset(buf, 0, 65535);
	
	int ret = read(client_socket, buf, size);
	if(ret == -1)
	{
		perror("read");
		return;
	}	
	write(fd, buf, ret);
	close(fd);
}

//從用戶數據庫刪除一個用戶
void del_fromsql(struct Msg *msg)
{
	sqlite3 * database;
	int ret = sqlite3_open("usr.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return;
	}
	
	char *errmsg = NULL;
	char buf[100];
	sprintf(buf, "delete from usr where name = '%s'", msg->name);
	ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("刪除用戶失敗:%s\n", errmsg);
		return;
	}
	
	sqlite3_close(database);
	return;
}

//註銷用戶
void logout(int client_socket,struct Msg *msg)
{
	int i,j;
	for(i=0; i<count; i++)
	{
		if(strcmp(msg->name, usr[i].name) == 0)
			break;
	}
	for(j=i; j<count; j++)
	{
		usr[j] = usr[j+1];
	}
	count--;
	printf("正在註銷用戶%s\n",msg->name);
	del_fromsql(msg);
	
	write(client_socket, msg, sizeof(struct Msg));
	return;
	
}

//用戶下線
void off_line(int client_socket,struct Msg *msg)
{
	pthread_mutex_lock(&mutex);    // 搶鎖
	
	printf("用戶%s下線\n",msg->name);
	int i,j;
	for(i=0; i<count; i++)
	{
		if(strcmp(msg->name, usr[i].name) == 0)
			break;
	}
	for(j=i; j<count; j++)
	{
		usr[j] = usr[j+1];
	}
	count--;
	
	pthread_mutex_unlock(&mutex);  // 解鎖
	write(client_socket, msg, sizeof(struct Msg));
	
	return;
}

//用戶下載文件
void download_file(int client_socket,struct Msg *msg)
{
	printf("用戶%s下載了文件%s\n", msg->name, msg->msg);
	int size = file_size(msg->msg);
	msg->filesize = size;
	write(client_socket, msg, sizeof(struct Msg));
	
	usleep(100000);
	
	int fd = open(msg->msg, O_RDONLY, 0777);
	if(fd == -1)
	{
		perror("open");
		printf("文件下載失敗\n");
	}
	
	char buf[65535];
	memset(buf, 0, 65535);

	int ret = read(fd, buf, size);
	if(ret == -1)
	{
		perror("read");
		return;
	}
    	
	write(client_socket, buf, ret);
	close(fd);	
}

//設置禁言
void forbid_speak(int client_socket,struct Msg *msg)
{
	msg->cmd = 1003;
	printf("用戶%s已被禁言\n",msg->msg);
	
	pthread_mutex_lock(&mutex);
	int i;
	for (i = 0; i < SIZE; i++)
	{
		if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg)==0)
		{
			write (usr[i].socket, msg, sizeof(struct Msg));
			usr[i].flag = 1;
			break;
		}
	}
	pthread_mutex_unlock(&mutex);
}

//解除禁言
void relieve_speak(int client_socket,struct Msg *msg)
{
	msg->cmd = 1004;
	printf("用戶%s已被解除禁言\n",msg->msg);
	
	pthread_mutex_lock(&mutex);
	int i;
	for (i = 0; i < SIZE; i++)
	{
		if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg)==0)
		{
			write (usr[i].socket, msg, sizeof(struct Msg));
			usr[i].flag = 0;
			break;
		}
	}
	pthread_mutex_unlock(&mutex);
}

//踢出聊天室
void kickout_room(int client_socket,struct Msg *msg)
{
	msg->cmd = 1005;
	printf("用戶%s已被踢出聊天室\n",msg->msg);
	
	pthread_mutex_lock(&mutex);
	int i;
	for (i = 0; i < SIZE; i++)
	{
		if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg) != 0)
		{
			//給在線用戶通知某某某被踢出聊天室
			write (usr[i].socket, msg, sizeof(struct Msg));
		}
	}
	pthread_mutex_unlock(&mutex);
	
	for (i = 0; i < SIZE; i++)
	{
		if (usr[i].socket != 0 && strcmp(usr[i].name, msg->msg) == 0)
		{
			break;
		}
	} 
	msg->cmd = 1006;
	//strcpy(msg->name,msg->msg);
	//off_line(usr[i].socket,msg);
	write (usr[i].socket, msg, sizeof(struct Msg));
}

//超級用戶
void surperusr(int client_socket)
{
	struct Msg msg;
	
	while(1)
	{
		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:
				see_online(client_socket, &msg);
				break;
			case 2:
				chat_group(client_socket, &msg);
				break;
			case 3:
				chat_private(client_socket, &msg);
				break;
			case 6:
				forbid_speak(client_socket, &msg);  // 設置禁言
				break;                           
			case 7:		                         
				relieve_speak(client_socket,&msg);  // 解除禁言
				break;                           
			case 8:  
				off_line(client_socket,&msg);
				return;			
			case 9:  
				kickout_room(client_socket,&msg);   // 踢出聊天室
				break;			
				
		}
	}
}

//普通用戶
void commonusr(int client_socket)
{
	struct Msg msg;
	
	while(1)
	{
		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:
				see_online(client_socket, &msg);
				break;
			case 2:
				chat_group(client_socket, &msg);
				break;
			case 3:
				chat_private(client_socket, &msg);
				break;
			case 6:
				send_file(client_socket, &msg);
				break;
			case 7:		
				logout(client_socket,&msg);
				return;
			case 8:                  
				off_line(client_socket,&msg);
				return;
			case 9:
				download_file(client_socket,&msg);
				break;		
		}
	}
	
}

//添加到在線用戶列表
void add_usr(struct Msg *msg, int client_socket)
{
	pthread_mutex_lock(&mutex);    // 搶鎖
	
	strcpy(usr[count].name, msg->name);
	usr[count].socket = client_socket;
	count++;
	
	pthread_mutex_unlock(&mutex);  // 解鎖
}

// 註冊
void reg(int client_socket, struct Msg *msg)
{
	//查找用戶是否已經被註冊
	printf("正在查找該用戶是否被註冊...\n");
	if(find_name(msg) == TRUE)
	{
		printf("用戶%s已經被註冊\n",msg->name);
		msg->cmd = 0;	
	}
	else
	{
		if(insert_sql(msg) == TRUE)
		{
			msg->cmd = 1;
			printf("用戶%s成功添加到數據庫\n",msg->name);
		}
	}
	write(client_socket, msg, sizeof(struct Msg));
}

// 登陸
void login(int client_socket, struct Msg *msg)
{
	int flag1 = 0; //用來判斷該用戶有沒有成功登陸  1代表成功
	//檢查該用戶有沒有註冊
	printf("正在查找該用戶有沒有註冊...\n");
	if(find_name(msg) == TRUE)
	{
		if(find_np(msg) == TRUE)
		{
			if(check_ifonline(msg) == TRUE)
			{
				msg->cmd = 3;
				printf("用戶%s已經登陸過了\n",msg->name);
			}
			else
			{
				msg->cmd = 0;
				
				printf("檢查該用戶權限\n");
				if(check_root(msg) == TRUE)
				{
					printf("該用戶是超級用戶\n");
					msg->flag = 1;
				}
				else
				{
					printf("該用戶是普通用戶\n");
					msg->flag = 0;
				}
				printf("用戶%s登陸成功\n",msg->name);
				flag1 = 1;
				
				add_usr(msg, client_socket);    //添加到在線用戶列表
				
			}	
		}
		else
		{
			msg->cmd = 1;
			printf("用戶%s密碼輸入錯誤\n",msg->name);
		}
	}
	else
	{
		msg->cmd = 2;
		printf("用戶%s還沒有註冊\n",msg->name);
	}
	
	write(client_socket, msg, sizeof(struct Msg));
	
	if(flag1 == 1)
	{
		if(msg->flag == 1)
			surperusr(client_socket);
		if(msg->flag == 0)
			commonusr(client_socket);
	}
}


//查看用戶權限
int check_root(struct Msg *msg)
{
	if(strcmp(msg->name, "root") == 0)
		return TRUE;
	else 
		return FALSE;
}


void* hanld_client(void* v)
{
	struct Msg msg;
	int client_socket = (int)v;
	
	while(1)
	{
		// 從客戶端讀一個結構體數據
		int ret = read(client_socket, &msg, sizeof(msg));
		if (ret == -1)
		{
			perror ("read");
			break;
		}
		// 代表客戶端退出
		if (ret == 0)
		{
			printf ("客戶端退出\n");
			break;
		}
		
		printf("從客戶端讀到一個用戶:%s, %s, %d\n", msg.name, msg.password, msg.cmd);
		
		switch (msg.cmd)
		{
			case 1:
				reg(client_socket, &msg);
				break;
			case 2:
				login(client_socket, &msg);
				break;
		}	
	}
	close (client_socket);
}

//檢查該用戶是否在線
int check_ifonline(struct Msg *msg)
{
	int i;
	for(i=0; i<count; i++)
	{
		if(strcmp(msg->name, usr[i].name) == 0)
			return TRUE;
	}
	if(i == count)
		return FALSE;
}

//查找用戶名
int find_name(struct Msg *msg)
{
	sqlite3 * database;
	int ret = sqlite3_open("usr.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return FALSE;
	}
	
	char *errmsg = NULL;
	char **resultp = NULL;
	int nrow, ncolumn;
	char *sql = "select name from usr";
	ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("用戶查找失敗:%s\n", errmsg);
		return FALSE;
	}
	
	int i;
	for(i=0; i<nrow+1; i++)
	{
		if(strcmp(resultp[i], msg->name) == 0)
			return TRUE;
	}
	return FALSE;
}

//查找用戶名和密碼
int find_np(struct Msg *msg)
{
	sqlite3 * database;
	int ret = sqlite3_open("usr.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return FALSE;
	}
	
	char *errmsg = NULL;
	char **resultp = NULL;
	int nrow, ncolumn;
	char *sql = "select * from usr";
	ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("用戶查找失敗:%s\n", errmsg);
		return FALSE;
	}
	
	int i;
	for(i=0; i<(nrow+1)*ncolumn; i++)
	{
		if(strcmp(resultp[i], msg->name) == 0 &&
		strcmp(resultp[i+1], msg->password) == 0)
			return TRUE;
	}
	return FALSE;
}

//添加用戶到數據庫
int insert_sql(struct Msg *msg)
{
	sqlite3 * database;
	int ret = sqlite3_open("usr.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return FALSE;
	}
	
	char *errmsg = NULL;
	char buf[100];
	sprintf(buf, "insert into usr values('%s','%s')", msg->name, msg->password);
	ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("添加用戶失敗:%s\n", errmsg);
		return FALSE;
	}
	
	sqlite3_close(database);
	return TRUE;
}

//建立所有用戶的聊天記錄數據庫
void setup_record()
{
	sqlite3 * database;
	
	int ret = sqlite3_open("allrecord.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return;
	}

	char *errmsg = NULL;
	char *sql = "create table if not exists allrecord(time TEXT,fromname TEXT,toname TEXT,word TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("聊天記錄表創建失敗:%s\n", errmsg);
		return;
	}
	
	sqlite3_close(database);
	return;
}

//建立用戶數據庫,並在裏面添加超級用戶
int setup_sql()
{
	sqlite3 * database;
	
	int ret = sqlite3_open("usr.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return FALSE;
	}

	char *errmsg = NULL;
	char *sql = "create table if not exists usr(name TEXT,password TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("用戶表創建失敗:%s\n", errmsg);
		return FALSE;
	}
	
	struct Msg msg;
	strcpy(msg.name, "root");
	strcpy(msg.password, "123");
	
	insert_sql(&msg);
	
	sqlite3_close(database);
	return TRUE;
}

// 初始化套接字,返回監聽套接字
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地址
	
	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)
{
	struct sockaddr_in client_addr; 
	int len = sizeof(client_addr);
	int client_socket = accept(listen_socket, (struct sockaddr *)&client_addr,  &len);
	if (client_socket == -1)
	{
		perror ("accept");
		return -1;
	}
	
	printf ("成功接收一個客戶端: %s\n", inet_ntoa(client_addr.sin_addr));
	
	return client_socket;
}

int main()
{
	setup_sql();
	setup_record();
	
	pthread_mutex_init(&mutex, NULL);
	
	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);
	
	pthread_mutex_destroy(&mutex);
	return 0;
}


客戶端:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdlib.h>
#include <sqlite3.h>

#include <sys/stat.h>
#include <fcntl.h>

#define PORT  9999
#define SIZE 20
int flag = 0;
int offline = 0; //用來判斷該用戶有沒有被踢  0代表沒有,1代表被踢
struct Usr
{
	char name[SIZE];
	int socket;
	int flag;//用來判斷該用戶是否被禁言,沒有被禁設爲0,被禁設爲1
};

struct Msg
{
	struct Usr usr[SIZE];
	char msg[1024];
	char buf[1024];
	char name[SIZE];
	char fromname[SIZE];
	char toname[SIZE];
	char password[SIZE];
	int cmd;
	int filesize;
	int flag;    //用來判斷用戶權限   0代表普通用戶,1代表超級用戶
};

struct Msg tmp;

//顯示空行
void display_line()
{
	system("clear");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("\n");
}

//顯示主界面
void maininterface()
{
	display_line();
	printf("\t\t1、    註冊\n");
	printf("\n");
	printf("\t\t2、    登陸\n");
	printf("\n");
	printf("\t\t3、    退出\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("\n");
	printf("\t\t請輸入指令:\n");
}

//顯示普通用戶界面
void cominterface(struct Msg *msg)
{
	display_line();
	printf("\t\t-.- %s:\n", msg->name);
	printf("\n");
	printf("\t\t1、    查看在線用戶\n");
	printf("\n");
	printf("\t\t2、    羣發消息\n");
	printf("\n");
	printf("\t\t3、    發送悄悄話\n");
	printf("\n");
	printf("\t\t4、    刪除聊天記錄\n");
	printf("\n");
	printf("\t\t5、    查看聊天記錄\n");
	printf("\n");
	printf("\t\t6、    上傳文件\n");
	printf("\n");
	printf("\t\t7、    註銷當前用戶\n");
	printf("\n");
	printf("\t\t8、    退出當前賬號\n");
	printf("\n");
	printf("\t\t9、    下載文件\n");
	printf("\n");
	printf("\t\t請輸入指令:");
	
}

//顯示超級用戶界面
void supinterface(struct Msg *msg)
{
	display_line();
	printf("\t\t-.- %s:\n", msg->name);
	printf("\n");
	printf("\t\t1、    查看在線用戶\n");
	printf("\n");
	printf("\t\t2、    羣發消息\n");
	printf("\n");
	printf("\t\t3、    發送悄悄話\n");
	printf("\n");
	printf("\t\t4、    刪除聊天記錄\n");
	printf("\n");
	printf("\t\t5、    查看聊天記錄\n");
	printf("\n");
	printf("\t\t6、    設置禁言\n");
	printf("\n");
	printf("\t\t7、    解除禁言\n");
	printf("\n");
	printf("\t\t8、    退出當前賬號\n");
	printf("\n");
	printf("\t\t9、    踢出聊天室\n");
	printf("\n");
	printf("\t\t請輸入指令:");
	
}

//查看在線用戶
void see_online(int socketfd, struct Msg *msg)
{
	msg->cmd = 1;
	write(socketfd, msg, sizeof(struct Msg));
	
	//struct Usr usr[SIZE];
	//read(socketfd, usr, sizeof(usr));
	
	
	return;
}

//保存一條聊天記錄
void insert_mysql(struct Msg *msg)
{
	sqlite3 * database;
	int ret = sqlite3_open("person.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return ;
	}
	
	//獲取系統當前時間
	time_t timep;  
    char s[30];   
    time(&timep);  
    strcpy(s,ctime(&timep));
	int count = strlen(s);
	s[count-1] = '\0';
	
	char *errmsg = NULL;
	char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("用戶表創建失敗:%s\n", errmsg);
		return;
	}
	
	char buf[100];
	sprintf(buf, "insert into person values('%s','%s','%s','%s')",s,msg->fromname, msg->toname,msg->msg);
	ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("添加聊天記錄失敗:%s\n", errmsg);
		return ;
	}
	
	sqlite3_close(database);
	return ;
}

//羣聊
void chat_group(int socketfd, struct Msg *msg)
{
	msg->cmd = 2;
	printf ("\t\t請輸入要發送的內容: ");
	//scanf("%s",msg->msg);
	//while(getchar() != '\n');
	fgets(msg->msg, 1024, stdin);
	int len = strlen(msg->msg);
	msg->msg[len-1] = '\0';
	strcpy(msg->fromname, msg->name);
	strcpy(msg->toname, "all");
	insert_mysql(msg);
	
	write (socketfd, msg, sizeof(struct Msg));
	//printf("aaa\n");
}

//發送悄悄話
void  chat_private(int socketfd, struct Msg *msg)
{
	msg->cmd = 3;
	
	printf ("\t\t請輸入要發送的對象名稱: \n");
	fgets(msg->toname, SIZE, stdin);
	int len = strlen(msg->toname);
	msg->toname[len-1] = '\0';
	printf ("\t\t請輸入要發送的內容: \n");
	fgets(msg->msg, 1024, stdin);
	len = strlen(msg->msg);
	msg->msg[len-1] = '\0';
	strcpy(msg->fromname, msg->name);
	
	insert_mysql(msg);
	
	write(socketfd, msg, sizeof(struct Msg));
}

//刪除聊天記錄
void del_personsql()
{
	sqlite3 * database;
	int ret = sqlite3_open("person.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return;
	}
	
	char *errmsg = NULL;
	char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("用戶表創建失敗:%s\n", errmsg);
		return;
	}
	
	char *buf = "drop table person";
	ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("刪除聊天記錄失敗:%s\n", errmsg);
		return;
	}
	
	sqlite3_close(database);
	display_line();
	printf("\t\t刪除成功\n");
	printf("\t\t按ENTER鍵返回\n");
	while(getchar() != '\n');
	return;
}

//查看聊天記錄
void see_record()
{
	sqlite3 * database;
	int ret = sqlite3_open("person.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return ;
	}
	
	char *errmsg = NULL;
	char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("用戶表創建失敗:%s\n", errmsg);
		return;
	}
	
	char **resultp = NULL;
	int nrow, ncolumn;
	char *buf = "select * from person";
	ret = sqlite3_get_table(database, buf, &resultp, &nrow, &ncolumn, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("數據庫操作失敗:%s\n", errmsg);
		return;
	}
	//判斷表是否爲空
	if(nrow == 0)
	{
		display_line();
		printf("\t\t沒有聊天記錄\n");
	}
	else
	{
		int i;
		system("clear");
		printf ("nrow = %d, ncolumn = %d\n", nrow, ncolumn);
		printf("\t\t%s",resultp[0]);
		printf("\t%12s",resultp[1]);
		printf("\t%8s",resultp[2]);
		printf("\t%s",resultp[3]);
		for (i = 4; i < (nrow+1)*ncolumn; i++)
		{
			if (i % ncolumn == 0)
				printf ("\n");
			printf ("%12s", resultp[i]);
		}
		printf ("\n");	
		
	}
		
	
	printf("\t\t按ENTER鍵返回\n");
	while(getchar() != '\n');
	
	sqlite3_free_table(resultp);
	
	sqlite3_close(database);
	return;
	
}

//獲取文件大小
int file_size(char* filename)  
{  
    struct stat statbuf;  
    stat(filename,&statbuf);  
    int size=statbuf.st_size;  
  
    return size;  
}
//上傳文件
void send_file(int socketfd, struct Msg *msg)
{
	msg->cmd = 6;
	
	strcpy(msg->fromname, msg->name);
	
	printf("\t\t請輸入要發送的文件名:\n");
	fgets(msg->msg, 1024, stdin);
	int len = strlen(msg->msg);
	msg->msg[len-1] = '\0';
	
	int size = file_size(msg->msg);
	msg->filesize = size;
	write(socketfd, msg, sizeof(struct Msg));
	printf("\t\t正在上傳,請稍後...\n");
	sleep(1);
	
	int fd = open(msg->msg, O_RDONLY, 0777);
	if(fd == -1)
	{
		perror("open");
		printf("文件傳輸失敗\n");
	}
	
	char buf[65535];
	memset(buf, 0, 65535);
	
	int ret = read(fd, buf, size);
	if(ret == -1)
	{
		perror("read");
		return;
	}	
	write(socketfd, buf, ret);
	close(fd);
	printf("\t\t上傳文件成功\n");
	sleep(1);

}

//註銷當前用戶
void logout(int socketfd, struct Msg *msg)
{
	msg->cmd = 7;
	write(socketfd, msg, sizeof(struct Msg));
	display_line();
	printf("\t\t正在註銷...\n");
}

//設置禁言
void forbid_speak(int socketfd, struct Msg *msg)
{
	msg->cmd = 6;
	printf("\t\t請輸入要禁言的對象:\n");
	fgets(msg->msg, SIZE, stdin);
	int len = strlen(msg->msg);
	msg->msg[len-1] = '\0';
	
	write(socketfd, msg, sizeof(struct Msg));
	
}

//解除禁言
void relieve_speak(int socketfd, struct Msg *msg)
{
	msg->cmd = 7;
	printf("\t\t請輸入要解除禁言的對象:\n");
	fgets(msg->msg, SIZE, stdin);
	int len = strlen(msg->msg);
	msg->msg[len-1] = '\0';
	
	write(socketfd, msg, sizeof(struct Msg));
}

//踢出聊天室
void kickout_room(int socketfd, struct Msg *msg)
{
	msg->cmd = 9;
	printf("\t\t請輸入要踢除的對象:\n");
	fgets(msg->msg, SIZE, stdin);
	int len = strlen(msg->msg);
	msg->msg[len-1] = '\0';
	
	write(socketfd, msg, sizeof(struct Msg));
}

//下線
void off_line(int socketfd, struct Msg *msg)
{
	msg->cmd = 8;
	write(socketfd, msg, sizeof(struct Msg));
	display_line();
	printf("\t\t正在退出...\n");
}

//超級用戶
void superusr(int socketfd, struct Msg *msg)
{
	char ch[SIZE];
	int x;
	while (1)
	{
		supinterface(msg);
		scanf("%s",ch);
		while(getchar() != '\n');
		
		switch(ch[0])
		{
			case '1':                   // 查看在線用戶
				see_online(socketfd, msg);
				break;
			case '2':                   // 羣發消息
				chat_group(socketfd, msg);
				break;
			case '3':                   // 發送悄悄話
				chat_private(socketfd, msg);
				break;
			case '4':                   // 刪除聊天記錄
				del_personsql();
				break;
			case '5':                   // 查看聊天記錄
				see_record();
				break;
			case '6':                   // 設置禁言
				forbid_speak(socketfd, msg);
				break;
			case '7':                   // 解除禁言
				relieve_speak(socketfd, msg);
				break;
			case '8':                   // 退出當前賬號
				off_line(socketfd, msg);
				return;			
			case '9':                   // 踢出聊天室
				kickout_room(socketfd, msg);
				break;
			default: 
				display_line();
				printf("錯誤指令,請重新輸入\n");
				sleep(1);
				break;
		}	
		
		
	}
}

//下載文件
void download_file(int socketfd, struct Msg *msg)
{
	msg->cmd = 9;

	printf("\t\t請輸入要下載的文件名:\n");
	fgets(msg->msg, 1024, stdin);
	int len = strlen(msg->msg);
	msg->msg[len-1] = '\0';

	write(socketfd, msg, sizeof(struct Msg));

}
void fun(int socketfd, struct Msg *msg)
{
	printf("\n\t\t正在下載,請稍後...\n");
	int fd = open(msg->msg, O_RDWR | O_CREAT, 0777);
	if(fd == -1)
	{
		perror("open");
		printf("文件下載失敗了\n");
	}
	
	int size = msg->filesize;
	char buf[65535];
	memset(buf, 0, 65535);
	
	int ret = read(socketfd, buf, size);
	if(ret == -1)
	{
		perror("read");
		return;
	}
	write(fd, buf, ret);
	close(fd);
	sleep(1);
	printf("\t\t文件下載完成\n");
}
//普通用戶
void commonusr(int socketfd, struct Msg *msg)
{
	char ch[SIZE];
	int x;
	while (1)
	{
		cominterface(msg);
		//scanf("%s",ch);
		//while(getchar() != '\n');
		fgets(ch, SIZE, stdin);
		if(offline == 1)
		{
			display_line();
			printf("\n\t\t請稍後...\n");
			off_line(socketfd, msg);
			return;
		}
		else
		{
			switch(ch[0])
			{
				case '1':                   // 查看在線用戶
					see_online(socketfd, msg);
					break;
				case '2':                   // 羣發消息
					chat_group(socketfd, msg);
					break;
				case '3':                   // 發送悄悄話
					chat_private(socketfd, msg);
					break;
				case '4':                   // 刪除聊天記錄
					del_personsql();
					break;
				case '5':                   // 查看聊天記錄
					see_record();
					break;
				case '6':                   // 上傳文件
					send_file(socketfd, msg);
					break;
				case '7':                   // 註銷當前用戶
					logout(socketfd, msg);
					return;
				case '8':                   // 退出當前賬號
					off_line(socketfd, msg);
					return;
				case '9':
					download_file(socketfd, msg); // 下載文件
					break;
				default: 
					display_line();
					printf("錯誤指令,請重新輸入\n");
					sleep(1);
					break;
			}
		}			
	}
}

//創建聊天記錄數據庫
void set_mysql()
{
	sqlite3 * database;
	
	int ret = sqlite3_open("person.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打開數據庫失敗\n");
		return;
	}

	char *errmsg = NULL;
	char *sql = "create table if not exists person(time TEXT,fromname TEXT,toname TEXT,word TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("聊天記錄表創建失敗:%s\n", errmsg);
		return;
	}
	sqlite3_close(database);
	return;
}

//註冊函數
void reg(int socketfd)
{
	struct Msg msg;
	memset(&msg, 0, sizeof(msg));
	msg.cmd = 1;
	
	display_line();
	printf("\t\t請輸入姓名:");
	scanf("%s",msg.name);
	while(getchar() != '\n');

	printf("\n");
	printf("\t\t請輸入密碼:");
	scanf("%s",msg.password);
	while(getchar() != '\n');

	write(socketfd, &msg, sizeof(msg));
	
	read(socketfd, &msg, sizeof(msg));
	if(msg.cmd == 0)
	{
		display_line();
		printf("\t\t註冊失敗,該用戶已被註冊\n");
	}
	if(msg.cmd == 1)
	{
		display_line();
		
		set_mysql();      //註冊成功就建立一個用戶自己的數據庫存放在本地,存放聊天記錄
		printf("\t\t註冊成功\n");
	}
	sleep(2);
	return;
}

//專門用來讀取服務器
void * readMsg(void *v)
 {
	 int socketfd = (int)v;
	 struct Msg msg;
	 while (1)
	 {
		 int i;
		 
		 int ret = read (socketfd, &msg, sizeof(msg));
		 switch (msg.cmd)
		 {
			 case 1:  //顯示在線用戶
				
				printf("\n\t\t當前在線戶:\n");
				printf("\t\t");
				for(i=0; i<SIZE; i++)
				{
					printf("%-5s",msg.usr[i].name);
				}
				printf("\n");	
				break;
			 case 2:   // 接受一條羣聊信息
				printf ("\n%s給大家發了一條消息: %s\n", msg.name, msg.msg);
				insert_mysql(&msg);       //保存聊天信息
				break;
			 case 3:   // 接受一條私聊信息
				printf ("\n%s給你發一條消息:%s\n", msg.fromname, msg.msg);
				insert_mysql(&msg);
				break;
			 case 6:   // 收到有人上傳文件的消息			
				printf("\n用戶%s上傳了一份文件%s\n", msg.fromname, msg.msg);	
				break;
			 case 7:
				return;
			 case 8:
				return;
			 case 9:
				fun(socketfd, &msg);
				break;
			 case 1003:
				//display_line();
				printf("\n\t\t您已被管理員禁言\n");
				//sleep(1);
			    break;
			 case 1004:
				printf("\n您已被管理員解除禁言\n");
			    break;
			 case 1005:
				printf("\n用戶%s已被管理員踢出聊天室\n",msg.msg);
			    break;
			 case 1006:
			    display_line();
				printf("\n\t\t您已被管理員踢出聊天室\n");
				printf("\t\t按ENTER鍵返回主菜單\n");
				offline = 1;
				//off_line(socketfd, msg);
			    break;
		 }	 
	 }
 }
 
//登陸函數
void login(int socketfd)
{
	struct Msg msg;
	memset(&msg, 0, sizeof(msg));
	msg.cmd = 2;
	
	display_line();
	printf("\t\t請輸入姓名:");
	scanf("%s",msg.name);
	while(getchar() != '\n');
	printf("\n");
	printf("\t\t請輸入密碼:");
	scanf("%s",msg.password);
	while(getchar() != '\n');
	write(socketfd, &msg, sizeof(msg));
	
	read(socketfd, &msg, sizeof(msg));
	
	printf("%d\n",msg.cmd);
	if(msg.cmd == 0)
	{
		
		display_line();
		printf("\t\t正在登陸...\n");
		sleep(2);
		display_line();
		printf("\t\t登陸成功\n");
		
		//啓動一個線程專門用來接受聊天信息
		pthread_t id;
		pthread_create(&id, NULL, readMsg,  (void *)socketfd);
		pthread_detach(id);                 
		
		offline = 0;
		if(msg.flag == 1)                   //超級用戶
			superusr(socketfd, &msg);
		if(msg.flag == 0)                   //普通用戶
			commonusr(socketfd, &msg);		
	}
	if(msg.cmd == 1)
	{
		display_line();
		printf("\t\t正在登陸...\n");
		sleep(2);
		display_line();
		printf("\t\t密碼錯誤\n");
	}
	if(msg.cmd == 2)
	{
		display_line();
		printf("\t\t正在登陸...\n");
		sleep(2);
		display_line();
		printf("\t\t用戶不存在,請先註冊\n");
	}
	if(msg.cmd == 3)
	{
		display_line();
		printf("\t\t正在登陸...\n");
		sleep(2);
		display_line();
		printf("\t\t該用戶已經登陸,請勿重複登陸\n");
	}
		
	sleep(1);
	return;
}

// 客戶端向服務器發送數據
void ask_server(int socketfd)
{
	int x;
	while (1)
	{
		maininterface();
		scanf("%d",&x);
		while(getchar() != '\n');
		switch(x)
		{
			case 1:     // 註冊
				reg(socketfd);
				break;
			case 2:     // 登陸
				login(socketfd);
				break;
			case 3:     // 退出
				system("clear");
				return;
			default: 
				display_line();
				printf("\t\t錯誤指令,請重新輸入\n");
				sleep(1);
				break;
		}	
	}
}

int main()
{	
	// 創建與服務器通信的套接字
	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); 
	inet_aton("127.0.0.1",&(addr.sin_addr));
	
	// 成功的情況下,可以通過socketfd與服務器進行通信
	int ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));
	if (ret == -1)
	{
		perror ("connect");
		return -1;
	}

	printf ("成功連上服務器\n");
	
	ask_server(socketfd);
	
	close(socketfd);
	
	return 0;
}


六、項目演示效果









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章