结课项目:聊天室

有一阵子没写博客了,这个项目写到这,我觉得有必要记录一下了.中间有很多无用的备注,思路和格式也写的不是很好,没办法,水平有限.

在关于传输文件方面,还有一些不可预知的错误,暂时我还没有发现原因,只能减缓传输速度来降低这种错误出现的概率.超级用户还没有写,不过功能也不算少了.

优先头文件

/***************************************************************
* 文件名  	: 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

目前就是这样,以后肯定会更新.

  • 10
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值