結課項目:聊天室

有一陣子沒寫博客了,這個項目寫到這,我覺得有必要記錄一下了.中間有很多無用的備註,思路和格式也寫的不是很好,沒辦法,水平有限.

在關於傳輸文件方面,還有一些不可預知的錯誤,暫時我還沒有發現原因,只能減緩傳輸速度來降低這種錯誤出現的概率.超級用戶還沒有寫,不過功能也不算少了.

優先頭文件

/***************************************************************
* 文件名  	: network.h
* 創建時間  : 2017年8月20日15:28:04
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中關於網絡參數傳遞的頭文件
***************************************************************/
#ifndef __NETWORK_H__
#define __NETWORK_H__

#include <sqlite3.h>
#include <stdio.h>
#include <time.h>
#include <string.h>

#define SIZE 1024

typedef struct msg          //表示聊天時的信息
{
	char msg[SIZE];   		//消息內容
	char localname[20];		//消息目的名字
	char fromname[20];      //消息來源名字
	char password[20];      //用戶密碼   被借用與文件傳輸文件名
	char signname[40];      //個性簽名   
	int cmd;                //工作模式
	int num;                //用於保存文件複製的字節數,這個我想了好久,沒有別的可以取代
}Msg;
//做到一半,我發現只要有來源,目的名字,消息內容和工作模式就可以解決一切問題
//前提是不做文件傳輸


//***************************服務器數據庫**************************************

//創建數據庫用於保存賬號密碼
sqlite3 * Create_Sqlite(void);

//初測時保存用戶賬號和密碼
int Save_User(Msg *msg, sqlite3 *datebase);

//登錄時檢查用戶賬號和密碼
int Entry_User(Msg *msg, sqlite3 *datebase);

//修改數據庫 (個性簽名)
int revise_sign_sqlite(Msg * msg);

//修改密碼(數據庫)
int revise_password_sqlite(Msg * msg);

//***************************客戶端操作****************************************

//創建數據庫用於保存聊天記錄
sqlite3 * Create_user_sqlite(Msg * msg);

//保存聊天記錄到數據庫中
void save_Chat(Msg *msg);

//查看聊天記錄(調用數據庫)
void see_chat(Msg * msg);

#endif


/***************************************************************
* 文件名  	: server.c/server.h
* 創建時間  : 2017年8月20日13:30:38
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中關於服務器的總頭文件
***************************************************************/
#ifndef __SERVER_H__
#define __SERVER_H__

//socket bind accept函數使用
#include <sys/types.h>
#include <sys/socket.h>  //listen 使用
//memset 等函數使用
#include <string.h>
//htonl htons 函數使用
#include <arpa/inet.h>
//線程管理
#include <pthread.h>
//標準IO
#include <stdio.h>
//自制錯誤顯示
#include "error.h"
//關於保存數據和數據庫的頭文件
#include "network.h"

#define PORT 9999        //表示服務器端口
#define NOW_MAX 20 	 	 //表示最大在線用戶個數

typedef struct now_client   //用於保存在線的客戶端
{
	char name[20];
	int socket;
}NowClient;

//創建監聽套接字
int Socket_init (void);

//鏈接客戶端
int Socket_Accept (int listen_socket);

//創建服務器
int Make_Server (void);

//線程操作客戶端
void * hanld_client (void* v);

//客戶但進行註冊
int regis(int client_socket, Msg *msg);

//登錄賬號
int entry(int client_socket, Msg *msg);

//用戶界面
void User_server(int client_socket, Msg *msg);

//羣聊
void server_chatall(int client_socket, Msg * msg);

//私聊
void server_chatone(int client_socket, Msg * msg);

//退出當前登錄
void server_entryout(int client_socket, Msg * msg);

//顯示當前在下人數
void see_nowuser(int client_socket, Msg * msg);

//修改個性簽名
void server_revise_sign(int client_socket, Msg * msg);

//修改密碼
void server_revise_password(int client_socket, Msg * msg);

//收到開始傳輸文件命令
void server_transfer_file(int client_socket, Msg * msg);

//接受文件
void server_transfer_file_y(Msg * msg);

//拒絕文件
void server_transfer_file_n(Msg * msg);

//一切條件都已成立,直接開始傳輸
void server_start_transfer_file(Msg * msg);
#endif


/***************************************************************
* 文件名  	: client.c/client.h
* 創建時間  : 2017年8月20日19:24:59
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中關於客戶端的總頭文件
***************************************************************/
#ifndef __CLIENT_H__
#define __CLIENT_H__

#define PORT 9999 

//socket 函數使用
#include <sys/types.h>
#include <sys/socket.h>  
//memset 等函數使用
#include <string.h>
//htonl htons 函數使用
#include <arpa/inet.h>
//標準IO
#include <stdio.h>
//系統調用
#include <sys/stat.h>
#include <fcntl.h>
//exit 函數使用
#include <unistd.h>
#include <stdlib.h>
//關於保存數據和數據庫的頭文件
#include "network.h"
#include <strings.h>


//連接服務器
int Connect_Server(void);

//訪問服務器 主界面
int Ask_server(int client_socket);

//客戶端主界面
void main_menu(void);

//用戶登錄界面
void user_menu(void);

//註冊賬號
void regis(int client_socket);

//登錄賬號
int entry(int client_socket);

//用戶界面
void User_used(int client_socket);

//讀寫分離
void * readMsg (void *v);

//羣聊
void chat_all(int client_socket);

//私聊
void chat_one(int client_socket);

//退出登錄
void entry_out(int client_socket);

//查看聊天記錄
void look_chat(void);

//顯示當前在線人數 
void see_now_time(int client_socket);

//修改個性簽名
void revise_sign(int client_socket);

//修改密碼
void revise_password(int client_socket);

//傳輸文件
void transfer_file(int client_socket);

//表示願意接受文件
void transfer_file_y(int client_socket);

//表示不願意接受文件
void transfer_file_n(int client_socket);

//傳出文件來源開始傳輸文件
void start_transfer_file(int client_socket);

//接受文件
void save_transfer_file(Msg * buf);



#endif



/***************************************************************
* 文件名  	: error.c/error.h
* 創建時間  : 2017年8月20日13:44:51
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中報錯系統的頭文件
***************************************************************/

#ifndef __ERROR_H__
#define __ERROR_H__

#include<stdio.h>

#define SOCKET_INIT			 -1     //初始化套接字失敗
#define SOCKET_ACCEPT		 -2		//鏈接客戶端
#define CREATE_SQLITE        -3     //打開數據庫失敗
#define SAVE_SQLITE          -4     //數據庫插入數據失敗

int errno ;

//向屏幕輸出錯誤原因
void myerror (char *str);

//向字符串傳送錯誤原因
char * myStrError (void);

#endif

然後是各函數
/***************************************************************
* 文件名  	: server.c/server.h
* 創建時間  : 2017年8月20日13:30:38
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中關於服務器的函數
***************************************************************/

#include "server.h"


NowClient user[NOW_MAX];    //表示當前在線人的資料

//****************************服務器製作*******************************

//創建監聽套接字
int Socket_init (void)
{
	//創建
	int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
	if(listen_socket == -1)
	{
		printf("監聽套接字創建失敗.\n");
		perror("socket");
		return -1;
	}
	//綁定
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));  			//清空內存
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);      			//端口和ip要改成大端模式
	addr.sin_addr.s_addr = htonl(INADDR_ANY);   //INADDR_ALL表示任意ip
	
	int ret = bind(listen_socket, (struct sockaddr*)&(addr), sizeof(addr));
	if(ret == -1)
	{
		printf("綁定套接字失敗.\n");
		perror("bind");
		return -1;
	}
	//監聽
	ret = listen(listen_socket, 5);      //用戶最多20個
	if(ret == -1)
	{
		printf("監聽套接字失敗.\n");
		perror("listen");
		return -1;
	}
	
	printf("等待用戶連接.....\n");
	
	return listen_socket;
}

//鏈接客戶端
int Socket_Accept (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)
	{
		printf("鏈接客戶端失敗\n");
		perror("accept");
		return -1;
	}
	
	printf("成功鏈接了一個客戶端 : %s\n"), inet_ntoa(client_addr.sin_addr);
	
	return client_socket;
}

//創建服務器
int Make_Server (void)
{
	//初始化套接字
	int listen_socket = Socket_init();
	if(listen_socket == -1)
	{
		errno = SOCKET_INIT;
		myerror("Socket_init");
		return -1;
	}
	
	while(1)
	{
		//鏈接客戶端
		int client_socket = Socket_Accept(listen_socket);
		if(listen_socket == -1)
		{
		errno = SOCKET_ACCEPT;
		myerror("Socket_Accept");
		return -1;
		}
		
		//創建進程處理客戶端
		pthread_t client_id;
		int ret = pthread_create(&client_id, NULL, hanld_client, (void*)client_socket);
		if(ret != 0)
		{
			perror("pthread_create");
			return -1;
		}
		pthread_detach(client_id); // 線程分離
	}
	
	close (listen_socket);
	return 0;
}

//****************************主界面操作*******************************

//線程操作客戶端
void * hanld_client (void* v)
{
	int client_socket = (int)v;
	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 :    // 客戶但進行註冊
				regis(client_socket, &msg);
				break;
			case 2 :    //客戶端登錄
				ret = entry(client_socket, &msg);
				write(client_socket , &msg ,sizeof(Msg));
				if (ret == 1)
				{
					//在線人數加1
					int i;
					for (i=0; i<NOW_MAX; i++)
					{
						if(user[i].socket == 0)
						{
							strcpy(user[i].name, msg.fromname);
							user[i].socket = client_socket;
							//printf("%d  %s\n",user[i].socket,user[i].name);
							printf("客戶端在線人數加一\n");
							break;
						}
					}
					//用戶界面
					User_server(client_socket, &msg);  
				}	
				break;
		}
	}
}

// 客戶端進行註冊
int regis(int client_socket, Msg *msg)
{
	printf(" %s 進行註冊.\n",msg->fromname);
	
	//用戶賬號和密碼保存在數據庫中
	sqlite3 *datebase = Create_Sqlite();
	if(datebase == NULL)
	{
		errno = CREATE_SQLITE;
		myerror("Create_Sqlite");
	}
	int flag = Save_User(msg, datebase);
	if (flag == -1)
	{
		errno = SAVE_SQLITE;
		myerror("Save_User");
		msg->cmd = -1;
	}
	/*
	else if (flag == -2)   //表示用戶名已經存在
	{
		printf("賬號創建失敗,用戶民已存在.\n");
		msg->cmd = -1;
	}
	*/
	else 
	{
		msg->cmd += 1000;
	}
	sqlite3_close(datebase);
	
	write(client_socket , msg ,sizeof(Msg));
}

//登錄賬號
int entry(int client_socket, Msg *msg)
{
	printf(" %s 進行登錄.\n",msg->fromname);
	
	//用戶登錄
	sqlite3 *datebase = Create_Sqlite();
	if(datebase == NULL)
	{
		errno = CREATE_SQLITE;
		myerror("Create_Sqlite");
		return -1;
	}
	int flag = Entry_User(msg, datebase);
	//printf("2222\n");   段錯誤沒有輸出
	if (flag == -1)
	{
		errno = SAVE_SQLITE;
		myerror("Entry_User");
		return -1;
	}
	if (flag == -2)
	{
		printf("登錄失敗,用戶名不存在\n");
		msg->cmd = -1;
		return -1;
	}
	if (flag == -3)
	{
		printf("登錄失敗,密碼錯誤\n");
		msg->cmd = -2;
		return -1;
	}
	else 
	{
		printf("登錄成功\n");
		msg->cmd += 1000;
		return 1;
	}
}

//***************************用戶界面操作******************************

//用戶界面
void User_server(int client_socket, Msg *msg)
{
	int j = 1;      //表示循環退出條件
	while(j)
	{
		//從客戶端讀取數據
		int ret = read(client_socket, msg, sizeof(Msg));
		
		if (ret == -1)
		{
			perror ("read");
			break;
		}
		// 代表客戶端退出
		if (ret == 0)
		{
			//printf ("客戶端退出\n");
			break;
		}
		
		switch (msg->cmd)
		{
			case 3 :    //羣聊
				server_chatall(client_socket, msg);
				break;
			case 4 :    //私聊
				server_chatone(client_socket, msg);	
				break;
			case 5 :    //退出登錄
				server_entryout(client_socket, msg);
				j = 0;
				break;
			case 6 :   //顯示當前在線人數
				see_nowuser(client_socket, msg);
				break;
			case 7 :   //修改個性簽名
				server_revise_sign(client_socket, msg);
				break;
			case 8 :   //修改密碼
				server_revise_password(client_socket, msg);
				break;
			/*************錯誤思路
			case 9 :   //傳輸文件
				server_transfer_file(client_socket, msg);
				break;
			case 10 :  //表示一切處理成功,直接傳輸文件
				server_transfer_filenow(client_socket, msg);
			*/
			case 9 :   //請求傳輸文件
				server_transfer_file(client_socket, msg);
				break;
			case 10:   //接受傳輸文件
				server_transfer_file_y(msg);
				break;
			case -10 : //拒絕傳輸文件
				server_transfer_file_n(msg);
				break;
			case 11:   //一切條件都已成立,直接開始傳輸
				server_start_transfer_file(msg);
		}
	}
	
	//用戶下線
	int i ;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket == client_socket)
		{
			user[i].socket = 0;
			printf("客戶端在線人數減一\n");
			break;
		}
	}
}

//羣聊
void server_chatall(int client_socket, Msg * msg)
{
	printf (" %s 進行羣發.\n",msg->fromname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if (user[i].socket != 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
		}
	}
}

//私聊
void server_chatone(int client_socket, Msg * msg)
{
	printf ("私聊 %s發送信息給%s\n",msg->fromname,msg->localname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strncmp(user[i].name, msg->localname, strlen(msg->localname)) == 0)
		{
			//printf("%s\n",msg->localname);
			//printf("%d  %s\n",user[i].socket,user[i].name);
			write(user[i].socket, msg , sizeof(Msg));
			printf("私聊成功\n");
			break;
		}
	}
	//printf("%d\n",i);
	if (i == NOW_MAX)
	{
		msg->cmd = -3;    //表示私聊失敗
		write(client_socket, msg , sizeof(Msg));
		printf("私聊失敗\n");
	}
}

//退出當前登錄
void server_entryout(int client_socket, Msg * msg)
{
	write(client_socket, msg , sizeof(Msg));
	printf("%s 退出登錄\n",msg->fromname);
}

//顯示當前在下人數
void see_nowuser(int client_socket, Msg * msg)
{
	printf("%s 查看當前在線人員\n",msg->fromname);
	
	int i;
	int len;
	char buf[1024] = {0};
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0)
		{
			strcat(buf,user[i].name);
			len = strlen(buf);
			buf[len] = ' ';
		}
	}
	strcpy(msg->msg,buf);
	
	write(client_socket, msg, sizeof(Msg));
	printf("查看成功\n");
}

//修改個性簽名
void server_revise_sign(int client_socket, Msg * msg)
{
	printf("%s 修改個性簽名\n",msg->fromname);
	int ret = revise_sign_sqlite(msg);    //修改數據庫
	if (ret == -1)
	{
		msg->cmd = -7;
		printf("%s 修改個性簽名失敗\n",msg->fromname);
		write(client_socket, msg, sizeof(Msg));
	}
	printf("%s 修改個性簽名成功\n",msg->fromname);
	write(client_socket, msg, sizeof(Msg));
}

//修改密碼
void server_revise_password(int client_socket, Msg * msg)
{
	printf("%s 修改密碼\n",msg->fromname);
	int ret = revise_password_sqlite(msg);
	if (ret == -1)
	{
		msg->cmd = -8;
		printf("%s 修改密碼失敗\n",msg->fromname);
		write(client_socket, msg, sizeof(Msg));
	}
	printf("%s 修改密碼成功\n",msg->fromname);
	write(client_socket, msg, sizeof(Msg));
}

/*錯誤思路
//傳輸文件
void server_transfer_file(int client_socket, Msg * msg)
{
	printf ("%s 請求發送%s文件給 %s\n",msg->fromname,msg->signname,msg->localname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			printf("發送給%s信息進行判斷是否接受\n",msg->localname);
			break;
		}
	}
	if (i == NOW_MAX)
	{
		msg->cmd == -9;    //表示傳輸文件失敗
		write(client_socket, msg , sizeof(Msg));
		printf("發送文件失敗,好友不在線或不存在\n");
		return ;
	}
	
	read(user[i].socket, msg, sizeof(Msg));
	if (msg->cmd != 2000)
	{
		msg->cmd = -10;    //傳輸文件失敗
		write(client_socket, msg , sizeof(Msg));
		printf("發送文件失敗,好友拒絕\n");
		return ;
	}
	
	msg->cmd = 10;      //表示接受傳輸
	printf("開始傳輸文件\n");
	write(client_socket, msg, sizeof(Msg));
}

//表示一切處理成功,直接傳輸文件
void server_transfer_filenow(int client_socket, Msg * msg)
{
	printf ("接受到數據,進行傳輸\n");
	msg->cmd = 11;
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strncmp(user[i].name, msg->localname, strlen(msg->localname)) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			printf("傳輸成功\n");
			break;
		}
	}
}
*/

//傳輸文件
void server_transfer_file(int client_socket, Msg * msg)
{
	printf ("%s 請求發送%s文件給 %s\n",msg->fromname,msg->signname,msg->localname);
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			printf("發送給%s信息進行判斷是否接受\n",msg->localname);
			break;
		}
	}
	if (i == NOW_MAX)
	{
		msg->cmd == -9;    //表示傳輸文件失敗
		write(client_socket, msg , sizeof(Msg));
		printf("發送文件失敗,好友不在線或不存在\n");
	}
}

//接受文件
void server_transfer_file_y(Msg * msg)
{
	printf("%s 接受文件傳輸.\n",msg->fromname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			break;
		}
	}
}

//拒絕文件
void server_transfer_file_n(Msg * msg)
{
	printf("%s 不願意接受文件傳輸.\n",msg->fromname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			break;
		}
	}
}

//一切條件都已成立,直接開始傳輸
void server_start_transfer_file(Msg * msg)
{
	//printf("文件傳輸中\n");
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));    //寫文件數據
			break;
		}
	}
	if(msg->num != 1024)
	{
		printf("文件傳輸完成\n");
	}
}


int main()
{
	Make_Server ();
	return 0;
}



/***************************************************************
* 文件名  	: hanld_sqlite3/network.h
* 創建時間  : 2017年8月20日15:27:56
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中關於一些對數據庫操作的函數
***************************************************************/


#include "network.h"



//***************************服務器數據庫**************************************

//創建數據庫用於保存賬號密碼
sqlite3 * Create_Sqlite(void)
{
	sqlite3 * datebase;
	int ret = sqlite3_open("chat.db", &datebase);
	if(ret != SQLITE_OK)
	{
		printf("數據庫打開失敗\n");
		return NULL;
	}
	printf("數據庫打開成功\n");
	
	return datebase;
}
/*
create table user(
NAME  	   TEXT,
Password   TEXT,
);
*/

//註冊時保存用戶賬號和密碼
int Save_User(Msg *msg, sqlite3 *datebase)
{
	//打開表
	char *errmsg = NULL;
	char buf[100] = "create table if not exists user(NAME TEXT,Password TEXT,Sign_name TEXT,primary key(NAME))";
	int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("打開表失敗\n");
		printf("sqlite3_exec: %s\n",errmsg);
		return -1;
	}
	printf("打開表成功\n");
	
	//判斷表中是否已存在相同賬號名
	char **resultp = NULL;
	int nrow,ncolumn;
	char *sq1 = "select NAME from user";
	ret = sqlite3_get_table(datebase, sq1, &resultp, &nrow, &ncolumn, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("數據庫操作失敗\n");
		printf("sqlite3_get_table: %s\n",errmsg);
		return -1;
	}
	/*
	int i;
	for(i=1; i<(nrow+1)*ncolumn ;i++)
	{
		if(strcmp(resultp[i], msg->fromname) == 0)
		{
			break;
		}
	}
	printf("判斷時候存在同名賬號....\n");
	sqlite3_free_table(resultp); //釋放空間
	
	if ((nrow+1)*ncolumn != 0)
	{
		if(i == (nrow+1)*ncolumn)
			return -2;
	}
	*/    //用主鍵比較同名
	//插入數據
	sprintf(buf,"insert into user values('%s', '%s', '鎧源鎧源你最帥')",msg->fromname, msg->password);
	ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("插入數據失敗\n");
		printf("sqlite3_exec: %s\n",errmsg);
		return -1;
	}
	
	return 0;
}

//登錄時檢查用戶賬號和密碼
int Entry_User(Msg *msg, sqlite3 *datebase)
{
	//打開表
	char *errmsg = NULL;
	char buf[100] = "create table if not exists user(NAME TEXT,Password TEXT,Sign_name TEXT,primary key(NAME))";
	int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("打開表失敗\n");
		printf("sqlite3_exec: %s\n",errmsg);
		return -1;
	}
	printf("打開表成功\n");
	
	//判斷賬號密碼
	char **resultp = NULL;
	int nrow,ncolumn;
	char *sq1 = "select * from user";
	ret = sqlite3_get_table(datebase, sq1, &resultp, &nrow, &ncolumn, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("數據庫操作失敗\n");
		printf("sqlite3_get_table: %s\n",errmsg);
		return -1;
	}
	int i;
	int j = 0 ;     		 //表示密碼對不對的標記
	int flag = 0;			 //表示賬號存不存在
	for(i=3; i<(nrow+1)*ncolumn ;i+=3)
	{
		if(strcmp(resultp[i], msg->fromname) == 0)
		{
			if (strcmp(resultp[i+1], msg->password) != 0)
			{
				j = 1;
			}
			flag = 1;
			break;
		}
	}
	
	//strcpy(msg->signname,resultp[i+2]);    //保存個性簽名
	ret = 0;
	if (flag != 1)
	{
		ret = -2;     //表示名字不存在
	}
	else if (j == 1)
	{
		ret = -3;     //表示密碼不對
	}
	else
		strcpy(msg->signname,resultp[i+2]);    //保存個性簽名
	
	sqlite3_free_table(resultp); //釋放空間
	return ret;          //表示登錄成功
}

//修改數據庫 (個性簽名)
int revise_sign_sqlite(Msg * msg)
{
	sqlite3 * datebase = Create_Sqlite();

	char *errmsg = NULL;
	char buf[200];
	sprintf(buf,"update user set Sign_name = '%s' where Name = '%s'",msg->signname,msg->fromname);
	int ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("修改數據失敗\n");
		printf("sqlite3_exec: %s\n",errmsg);
		sqlite3_close(datebase);   //關閉表
		return -1;
	}
	
	printf("修改數據成功\n");
	sqlite3_close(datebase);
	
	return 0;
}

//修改密碼(數據庫)
int revise_password_sqlite(Msg * msg)
{
	sqlite3 * datebase = Create_Sqlite();
	
	char *errmsg = NULL;
	char buf[200];
	sprintf(buf,"update user set Password = '%s' where Name = '%s'",msg->password,msg->fromname);
	int ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("修改數據失敗\n");
		printf("sqlite3_exec: %s\n",errmsg);
		sqlite3_close(datebase);   //關閉表
		return -1;
	}
	
	printf("修改數據成功\n");
	sqlite3_close(datebase);
	
	return 0;
}

//***************************客戶端數據庫***************************************

//創建數據庫用於保存聊天記錄
sqlite3 * Create_user_sqlite(Msg * msg)
{
	sqlite3 * datebase;
	char name[23];
	sprintf(name ,"%s.db",msg->fromname);
	int ret = sqlite3_open(name, &datebase);
	
	return datebase;
}
    
//保存聊天記錄到數據庫中
void save_Chat(Msg *msg)
{
	sqlite3 * datebase = Create_user_sqlite(msg);
	//打開表
	char *errmsg = NULL;
	char buf[200] = "create table if not exists chat(時間 TEXT,發送者 TEXT,接收者 TEXT,內容 TEXT)";
	int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("打開表失敗\n");
		return ;
	}
	
	time_t t;
	t = time(&t);
	char time[100];
	strcpy(time,ctime(&t));
	int len = strlen(time);
	time[len-1] = '\0';      //去掉回車 
	sprintf(buf,"insert into chat values('%s','%s','%s','%s')",time,msg->fromname,msg->localname,msg->msg);
	ret = sqlite3_exec(datebase, buf, NULL, NULL,&errmsg);
	if(ret != SQLITE_OK)
	{
		printf("插入數據失敗\n");
		return ;
	}
	sqlite3_close(datebase);
}

//查看聊天記錄(調用數據庫)
void see_chat(Msg * msg)
{
	sqlite3 * datebase = Create_user_sqlite(msg);
	//打開表
	char *errmsg = NULL;
	char buf[200] = "create table if not exists chat(時間 TEXT,發送者 TEXT,接收者 TEXT,內容 TEXT)";
	int ret = sqlite3_exec(datebase, buf, NULL, NULL, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("打開表失敗\n");
		return ;
	}
	//查看聊天記錄
	char **resultp = NULL;
	int nrow,ncolumn;
	char *sq1 = "select * from chat";
	ret = sqlite3_get_table(datebase, sq1, &resultp, &nrow, &ncolumn, &errmsg);
	if(ret != SQLITE_OK)
	{
		printf("數據庫操作失敗\n");
		printf("sqlite3_get_table: %s\n",errmsg);
		return ;
	}
	int i;
	for(i=0; i<(nrow+1)*ncolumn ;i++)
	{
		if(i%4 == 0)
		{
			printf("\n");
			printf("%-25s",resultp[i]);
		}
		else 
			printf("%15s",resultp[i]);
	}
	printf("\n");
	
	sqlite3_free_table(resultp); //釋放空間
	
	sqlite3_close(datebase);
	
	sleep(5);
}


/***************************************************************
* 文件名  	: client.c/client.h
* 創建時間  : 2017年8月20日19:24:59
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中關於客戶端的函數
***************************************************************/

#include "client.h"
char myname[20];    //用於保存本地名字
char signname[40];  //用於保存個性簽名
char mylocalname[20]; //用於保存傳輸文件來源的名字
char fsignname[40]; //用於保存文件傳輸名字
//********************************網絡連接***************************
//連接服務器
int Connect_Server(void)
{
	//創建套接字
	int client_socket = socket(AF_INET, SOCK_STREAM, 0);
	if(client_socket == -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));
	
	int ret = connect(client_socket,(struct sockaddr *)&addr, sizeof(addr));
	if(ret == -1)
	{
		perror("connect");
		return -1;
	}
	printf("成功連接到服務器 : %s\n",inet_ntoa(addr.sin_addr));
	
	return client_socket;
}

//訪問服務器 主界面
int Ask_server(int client_socket)
{
	char ch;
	int ret;
	while(1)
	{
		main_menu();
		printf("請輸入您要做操作\n");
		scanf("%c",&ch);
		while(getchar() != '\n');
		switch(ch)
		{
			case '1':    //註冊
				regis(client_socket);
				break;
			case '2':    //登錄
				ret = entry(client_socket);
				if (ret == 1)
				{
					User_used(client_socket);    //調用函數表示用戶界面
				}
				break;
			case '3':    //退出
				exit(0);
				break;
		}
	}
}

//********************************界面*******************************

//客戶端主界面
void main_menu(void)
{
	system("clear");
	printf("\n\n\n\n\n\n\n\n");
	printf("\t\t1,註冊\n");
	printf("\t\t2,登錄\n");
	printf("\t\t3,退出\n");
}

//用戶登錄界面
void user_menu(void)
{
	system("clear");
	printf("%s: %s\n",myname,signname);
	printf("\n\n\n\n\n\n\n");
	printf("\t\t1,羣聊\n");
	printf("\t\t2,私聊\n");
	printf("\t\t3,退出登錄\n");
	printf("\t\t4,查看聊天記錄\n");
	printf("\t\t5,顯示當前在線人員\n");
	printf("\t\t6,修改個性簽名\n");
	printf("\t\t7,修改密碼\n");
	printf("\t\t8,傳輸文件\n");
}

//********************************主界面功能*************************
//註冊賬號
void regis(int client_socket)
{
	Msg msg;
	msg.cmd = 1;
	printf("註冊,請輸入賬號名: ");
	scanf("%s",msg.fromname);
	while(getchar() != '\n');
	//strcpy(msg.fromname,myname);
	printf("註冊,請輸入密碼: ");
	scanf("%s",msg.password);
	while(getchar() != '\n');
	write(client_socket, &msg, sizeof(msg));
	
	read(client_socket, &msg, sizeof(msg));
	if(msg.cmd == 1001)
	{
		printf("註冊成功\n");
	}
	else if (msg.cmd == -1)
	{
		printf("用戶名以存在,註冊失敗\n");
	}
	else
	{
		printf("系統繁忙,註冊失敗\n");
	}
	
	sleep(2);
}

//登錄賬號
int entry(int client_socket)
{
	Msg msg;
	msg.cmd = 2;
	printf("登錄,請輸入賬號名: ");
	scanf("%s",msg.fromname);
	while(getchar() != '\n');
	//strcpy(msg.fromname,myname);
	printf("登錄,請輸入密碼: ");
	scanf("%s",msg.password);
	while(getchar() != '\n');
	write(client_socket, &msg, sizeof(msg));
	
	read(client_socket, &msg, sizeof(msg));
	if(msg.cmd == -1)    //表示用戶不存在
	{
		printf("登錄失敗,用戶不存在.\n");
		sleep(2);
		return -1;
	}
	if(msg.cmd == -2)
	{
		printf("登錄失敗,密碼錯誤.\n");
		sleep(2);
		return -2;
	}	
	if(msg.cmd == 1002)
	{
		printf("登錄成功,登錄中...\n");
		strcpy(myname,msg.fromname);      //保存在線名字
		strcpy(signname, msg.signname);   //保存個性前面
		sleep(2);
		return 1;
	}
}

//********************************用戶界面功能***********************

//用戶界面
void User_used(int client_socket)
{
	//要進行讀寫分離
	pthread_t read_id;
	pthread_create(&read_id, NULL, readMsg, (void *)client_socket);
	pthread_detach(read_id);   //等待線程分離
	
	char ch;
	int i = 1;
	while(i)
	{
		user_menu();
		printf("請輸入您要做操作\n");
		scanf("%c",&ch);
		while(getchar() != '\n');
		switch(ch)
		{
			case '1':        //羣聊
				chat_all(client_socket);
				break;
			case '2':        //私聊
				chat_one(client_socket);
				break;
			case '3':        //退出登錄	
				entry_out(client_socket);
				i = 0;
				break;
			case '4':        //查看聊天記錄
				look_chat();  
				break;
			case '5':        //顯示當前在線人數 
				see_now_time(client_socket);
				break;
			case '6':        //修改個性簽名
				revise_sign(client_socket);
				break;
			case '7':        //修改密碼
				revise_password(client_socket);	
				break;
			case '8':        //傳輸文件
				transfer_file(client_socket);
				break;
			case 'y':        //表示願意接受文件
				transfer_file_y(client_socket);
				break;
			case 'n':        //表示不願意接受文件
				transfer_file_n(client_socket);
				break;		
		}
	}
}

//讀寫分離
void * readMsg (void *v)
{
	int client_socket = (int)v;
	Msg buf;
	int i = 0;
	while(1)
	{
		//Msg buf;
		bzero(&buf,sizeof(buf));
		int ret = read(client_socket, &buf, sizeof(Msg));
		if(ret == -1)
		{
			perror("read");
			break;
		}
		switch(buf.cmd)
		{
			case 3:   //羣聊
				printf("收到了一條消息: %s\n",buf.msg);
				save_Chat(&buf);    //保存聊天記錄
				break;
			case 4:   //私聊
				printf("%s 給你發了一條消息: %s\n",buf.fromname,buf.msg);
				save_Chat(&buf);    //保存聊天記錄
				break;
			case -3:  //私聊失敗
				printf("私聊失敗,用戶不存在或下線\n");
				break;
			case 5 :  //退出登錄
				printf("%s 退出登錄\n",buf.fromname);
				sleep(1);
				pthread_exit(NULL);    //線程退出
				break;
			case 6 :   //顯示當前在線人數
				printf("當前在線人員:\n");
				printf("%s\n",buf.msg);
				break;
			case 7 :  //修改個性簽名成功
				strcpy(signname,buf.signname);
				printf("修改個性簽名成功\n");
				break;
			case -7 :  //修改個性簽名失敗
				printf("修改個性簽名失敗\n");
				break;
			case 8 	:   //修改密碼成功
				printf("修改密碼成功\n");
				break;
			case -8 : 	//修改密碼失敗
				printf("修改密碼失敗\n");
				break;
			/*************錯誤思路
			//傳輸文件++++++++++++++++++++++++++++++++++++
			case 9  :   //調用函數確認是否接受文件
				receive1_file(client_socket, &buf);
				break;
			case -9 :   //發送文件失敗,找不到該好友
				printf("發送文件失敗,好友不在線或不存在\n");
				break;
			case -10 :  //發送文件失敗,對方拒絕
				printf("發送文件失敗,好友拒絕接受文件\n");
				break;
			case 10 :   //表示確認通過,直接傳輸文件
				receive2_file_now(client_socket, &buf);
				break;
			case 11 :   //表示直接接受
				save3_file_now(&buf);
				break;
			*/
			case 9  :   //調用函數確認是否接受文件
				system("clear");
				printf("請問你是否接受來自 %s 的文件 %s(y/n)\n",buf.fromname,buf.signname);
				strcpy(mylocalname,buf.fromname);   //保存傳輸文件來源名字
				sleep(1);
				break;
			case -9 :   //表示傳輸文件失敗,沒有找到該人
				printf("發送文件失敗,好友不在線或不存在\n");
				break;
			case 10 :   //表示願意接受文件,開始傳輸
				start_transfer_file(client_socket);
				break;
			case -10 :  //表示不願意接受文件
				printf("發送文件失敗,後又拒絕接受文件\n");
				break;
			case 11 :   //接受文件
				save_transfer_file(&buf);
				i++;
				break;
				//printf("%d\n",i);
			default:break;
		}

	}
}

//羣聊
void chat_all(int client_socket)
{
	Msg msg;
	msg.cmd = 3;
	strcpy(msg.fromname,myname);
	strcpy(msg.localname,"All");
	printf("親輸入你要羣發送的信息\n");
	scanf("%s",msg.msg);
	while(getchar() != '\n');
	write(client_socket, &msg, sizeof(Msg));

	//save_Chat(msg);    //保存聊天記錄
	
	sleep (2);
}

//私聊
void chat_one(int client_socket)
 {
	Msg msg;
	msg.cmd = 4;
	printf("請輸入你要聊天的對象:\n");
	scanf ("%s",msg.localname);
	while(getchar() != '\n');
	
	printf("請輸入要發送的內容: \n");
	scanf("%s",msg.msg);
	while(getchar() != '\n');
	
	strcpy (msg.fromname,myname);
	write(client_socket, &msg, sizeof(Msg));

	save_Chat(&msg);    //保存聊天記錄
	
	sleep(2);
}

//退出登錄
void entry_out(int client_socket)
{
	Msg msg;
	msg.cmd = 5;
	strcpy (msg.fromname,myname);
	write(client_socket, &msg, sizeof(Msg));
}

//查看聊天記錄
void look_chat(void)
{
	Msg msg;
	strcpy(msg.fromname,myname);
	see_chat(&msg);
}

//顯示當前在線人數 
void see_now_time(int client_socket)
{
	Msg msg;
	msg.cmd = 6;
	strcpy (msg.fromname,myname);
	write(client_socket, &msg, sizeof(Msg));
	
	sleep(2);
}

//修改個性簽名
void revise_sign(int client_socket)
{
	Msg msg;
	msg.cmd = 7;
	printf("請輸入新的個性簽名:");
	scanf("%s",msg.signname);
	while(getchar() != '\n');
	
	strcpy(msg.fromname,myname);   //需要保存名字
	write(client_socket, &msg, sizeof(Msg));

	sleep(2);
}

//修改密碼
void revise_password(int client_socket)
{
	Msg msg;
	msg.cmd = 8;
	printf("親輸入新的密碼: ");
	scanf("%s",msg.password);
	while(getchar() != '\n');
	
	strcpy(msg.fromname,myname);   //需要保存名字
	write(client_socket, &msg, sizeof(Msg));
	
	sleep(2);
}	

//+++++++++++++++++++++++++++傳輸文件我另起一節,這個真的不好做+++++++++++++++++

//傳輸文件
void transfer_file(int client_socket)
{
	Msg msg;
	msg.cmd = 9;
	printf("請輸入你要傳輸文件的對象:");
	scanf ("%s",msg.localname);
	while(getchar() != '\n');
	
	printf("請輸入你要傳輸的本地文件名:");
	scanf ("%s",msg.signname);
	while(getchar() != '\n');
	
	strcpy(msg.fromname,myname);   //需要保存名字
	write(client_socket, &msg, sizeof(Msg));
	
	printf("等待驗收中...\n");
	
	strcpy(fsignname,msg.signname);
	strcpy(mylocalname,msg.localname);
	
	sleep(2);
}

/*因爲讀寫分離,只能一端讀,一端寫,思路錯誤
//確認是否接受文件(讀線程函數)
void receive1_file(int client_socket, Msg * buf)
{
	//system("clear");
	printf("請問你是否接受來自 %s 的文件 %s(y/n)\n",buf->localname,buf->signname);
	char c;
	scanf("%s",&c);
	//while(getchar() != '\n');
	if (c == 'y')
	{
		buf->cmd =  2000;
	}
	write(client_socket, buf, sizeof(Msg));
}

//表示確認通過,直接傳輸文件
void receive2_file_now(int client_socket, Msg * buf)
{
	//printf("%s\n",buf->signname);
	int fd = open(buf->signname,O_RDONLY);    //沒有能判斷打開失敗
	if(fd == -1)
	{
		perror("open");
		printf("傳輸失敗\n");
		return ;
	}
	
	//buf->cmd = 11;
	int ret = 0;
	while(ret = read(fd,buf->msg,SIZE))
	{
		if(ret == -1)
		{
			perror("read");
			break;
		}
		if (ret == 0)
		{
			break;
		}
		buf->num = ret;
		write(client_socket, buf, sizeof(Msg));
	}
	printf("文件複製完成傳輸\n");
	close(fd);
}

//表示直接接受
void save3_file_now(Msg * buf)
{
	int fd = open(buf->signname,O_RDWR|O_CREAT|O_APPEND,0777);
	write(fd,buf->msg,buf->num);
	if(buf->num != SIZE)
	{
		printf ("文件接受完成\n");
	}
	close (fd);
}
*/

//表示願意接受文件
void transfer_file_y(int client_socket)
{
	Msg msg;
	msg.cmd = 10;
	strcpy(msg.fromname,myname); 
	strcpy(msg.localname,mylocalname); 
	strcpy(mylocalname,"\0");    //用完後至零
	write(client_socket, &msg, sizeof(Msg));
}

//表示不願意接受文件
void transfer_file_n(int client_socket)
{
	Msg msg;
	msg.cmd = -10;
	strcpy(msg.fromname,myname); 
	strcpy(msg.localname,mylocalname); 
	strcpy(mylocalname,"\0");    //用完後至零
	write(client_socket, &msg, sizeof(Msg));
}

//傳出文件來源開始傳輸文件
void start_transfer_file(int client_socket)
{
	Msg msg;
	msg.cmd = 11; 
	strcpy(msg.fromname,myname); 
	strcpy(msg.signname,fsignname);
	strcpy(msg.localname,mylocalname);
	
	int fd = open(msg.signname,O_RDONLY); 
	if(fd == -1)
	{
		perror("open");
		printf("傳輸失敗\n");
		return ;
	}
	
	int ret = 0;
	int i = 0;
	while(ret = read(fd,msg.msg,SIZE))
	{
		if(ret == -1)
		{
			perror("read");
			break;
		}
		if (ret == 0)
		{
			break;
		}
		msg.num = ret;
		write(client_socket, &msg, sizeof(Msg));
		usleep(10000);     //這個睡眠時間減緩傳輸速度,降低不可預祝錯誤出現
	}
	printf("文件複製完成傳輸\n");
	strcpy(fsignname,"\0");
	strcpy(mylocalname,"\0");
	
	close(fd);
}

//接受文件
void save_transfer_file(Msg * buf)
{
	int fd = open(buf->signname,O_WRONLY|O_CREAT|O_APPEND,0777);
	write(fd,buf->msg,buf->num);
	if(buf->num != SIZE)
	{
		printf ("文件接受完成\n");
	}
	close (fd);
}




int main()
{
	int client_socket = Connect_Server();
	Ask_server(client_socket);
	close(client_socket);
	return 0;
}














/***************************************************************
* 文件名  	: error.c/error.h
* 創建時間  : 2017年8月20日13:44:51
* 目錄      : D:\蘇嵌安裝環境\red hat share\leo\項目\聊天室
* 製作者  	: 劉鎧源
* 目的  	: 編寫Linux下聊天室中報錯系統的函數
***************************************************************/

#include"error.h"

//往屏幕輸出錯誤原因
void myerror (char *str)
{
	char *msg = myStrError ();
	
	printf("%s: %s\n",str,msg);
	

}

//往字符串輸入錯誤原因
char * myStrError (void)
{
	switch (errno)
	{
		case SOCKET_INIT :
			return "初始化套接字失敗.";	
		case SOCKET_ACCEPT :
			return "鏈接客戶端失敗.";
		case CREATE_SQLITE:
			return "打開數據庫失敗.";
		case SAVE_SQLITE:
			return "數據庫插入數據失敗,";
	}
	
}

最後自己寫了個Makefile
server:sqlite3.o server.o error.o
	gcc sqlite3.o server.o error.o -o server -lsqlite3 -lpthread
client:client.o error.o sqlite3.o
	gcc client.o error.o sqlite3.o -o client -lsqlite3 -lpthread
sqlite3.o:sqlite3.c
	gcc -c sqlite3.c
server.o:server.c
	gcc -c server.c
client.o:client.c
	gcc -c client.c

error.o:error.c
	gcc -c error.c	
	
.PHONY:clean
clean:
	rm -f *.o
	rm -f server
	rm -f client

clear:
	rm *.db

目前就是這樣,以後肯定會更新.

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