基于C/S架构的网络聊天室

服务器端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "LinkList.h"

#define PORT 9999

typedef struct _msg
{
	int cmd;
	int flag;
	char username[20];
	char password[20];
	char msg[1024];
	char frname[20];
	char toname[20];
	char time[50];
	char filename[20];
}Msg;

sqlite3 *database;

Node *head;

//初始化套接字,返回监听套接字
int Init_Socket()
{
	//创建套接字
	int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
	if(listen_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);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	int ret = bind(listen_socket, (struct sockaddr *)&addr, sizeof(addr));
	if(ret == -1)
	{
		perror("bind");
		return -1;
	}
	
	//监听套接字
	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;
}

//注册
void reg(int client_socket, Msg *msg)
{
	char *errmsg = NULL;
	char **resultp = NULL;
	int nrow, ncolumn;
	char *sql = "select * from login";
	int ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return;
	}
	printf("查找成功\n");
	
	int i;
	for(i = 0;i < (nrow+1) * ncolumn;i++)
	{
		if(strcmp(resultp[i],msg->username) == 0)
		{
			printf("用户已存在\n");
			msg->cmd = -1;
			write(client_socket,msg,sizeof(Msg));
			return;
		}
	}
	
	char buf[100];
	sprintf (buf, "insert into login values('%s', '%s')", msg->username,msg->password);
	ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return;
	}
	printf("%s用户注册成功\n",msg->username);
	
	msg->cmd = 1001;

	write(client_socket,msg,sizeof(Msg));
}

//用户登录
void login(int client_socket, Msg *msg)
{
	char *errmsg = NULL;
	char **resultp = NULL;
	int nrow, ncolumn;
	char *sql = "select * from login";
	int ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return;
	}
	printf("查找成功\n");
	
	int i;
	for(i = 0;i < (nrow+1) * ncolumn;i++)
	{
		if(strcmp(resultp[i],msg->username) == 0 && strcmp(resultp[i+1],msg->password) == 0)
		{
			printf("%s登录成功\n",msg->username);
			User user;
			strcpy(user.username,msg->username);
			user.client_socket = client_socket;
			Insert_Last(head,user);
			Display(head);
			msg->cmd = 1002;
			write(client_socket,msg,sizeof(Msg));
			return;
		}
	}
		
	msg->cmd = -2;
	printf("用户名或密码错误,%s登录失败\n",msg->username);
	write(client_socket,msg,sizeof(Msg));
}

//显示在线好友列表
void Display_Friend(int client_socket,Msg *msg)
{
	Node* tmp = head->next;
	while(tmp)
	{
		strcpy(msg->username,tmp->data.username);
		write(client_socket,msg,sizeof(Msg));
		tmp = tmp->next;
	}
}

//群聊
void chat_all(int client_socket,Msg *msg)
{	
	Node* tmp = head->next;
	Node* tmp1 = head->next;
	
	while(tmp1)
	{
		if(tmp1->data.client_socket == client_socket)
		{
			strcpy(msg->frname,tmp1->data.username);
			break;
		}
		tmp1 = tmp1->next;
	}
	strcpy(msg->toname,"all");
	printf("%s发了一次群消息\n",msg->frname);
	
	while(tmp)
	{
		write(tmp->data.client_socket,msg,sizeof(Msg));
		tmp = tmp->next;
	}
}

//私聊
void chat_along(int client_socket,Msg *msg)
{
	Node* tmp1 = head->next;
	Node* tmp2 = head->next;
	
	while(tmp1)
	{
		if(tmp1->data.client_socket == client_socket)
		{
			strcpy(msg->frname,tmp1->data.username);
			break;
		}
		tmp1 = tmp1->next;
	}
	printf("%s发给%s一条消息\n",msg->frname,msg->toname);
	
	while(tmp2)
	{
		if(strcmp(tmp2->data.username,msg->toname) == 0)
		{
			msg->flag = 101;		
			write(tmp2->data.client_socket,msg,sizeof(Msg));
			return;
		}
		tmp2 = tmp2->next;
	}
	
	if(tmp2 == NULL)
	{
		msg->flag = -101;
		write(client_socket,msg,sizeof(Msg));
		printf("您要发送的好友不存在\n");
		return;
	}
}

//注销
void logout(int client_socket,Msg *msg)
{
	Node *tmp = head;
	while(tmp->next)
	{
		if(tmp->next->data.client_socket == client_socket)
		{
			strcpy(msg->frname,tmp->next->data.username);
			Node *p = tmp->next;
			tmp->next = p->next;
			free(p);
			break;
		}
	}
	
	char buf[100];
	char *errmsg = NULL;
	sprintf (buf, "delete from login where username = '%s'",msg->frname);
	int ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return;
	}
	
	write(client_socket,msg,sizeof(Msg));
}

//文件传输
void file_transfer(int client_socket,Msg *msg)
{
	Node *tmp1 = head->next;
	Node *tmp2 = head->next;
	
	while(tmp1)
	{
		if(tmp1->data.client_socket == client_socket)
		{
			strcpy(msg->frname,tmp1->data.username);
			break;
		}
		tmp1 = tmp1->next;
	}
	
	while(tmp2)
	{
		if(strcmp(tmp2->data.username,msg->toname) == 0)
		{
			if(strcmp(msg->msg,"over") != 0)
			{
				msg->flag = 101;
				usleep(15);
				write(tmp2->data.client_socket,msg,sizeof(Msg));
				return;
			}
			else
			{
				msg->flag = 102;
				printf("%s给%s发了一个文件\n",msg->frname,msg->toname);
				write(tmp2->data.client_socket,msg,sizeof(Msg));
				return;
			}
		}
		tmp2 = tmp2->next;
	}
	
	if(tmp2 == NULL)
	{
		msg->flag = -101;
		write(client_socket,msg,sizeof(Msg));
		printf("您要发送的好友不存在\n");
		return;
	}
}

//退出
void user_quit(int client_socket,Msg *msg)
{
	Node *tmp = head;
	while(tmp->next)
	{
		if(tmp->next->data.client_socket == client_socket)
		{
			strcpy(msg->frname,tmp->next->data.username);
			Node *p = tmp->next;
			tmp->next = p->next;
			free(p);
			break;
		}
	}
	printf("%s退出登录\n",msg->frname);
	write(client_socket,msg,sizeof(Msg));
}

//处理客户端通信的线程工作函数
void *handle_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:// 客户端进行注册
				reg(client_socket, &msg);
				break;
			case 2:// 登录 
				login(client_socket,&msg);
				break;
			case 3://在线好友显示
				Display_Friend(client_socket,&msg);
				break;
			case 4://群聊
				chat_all(client_socket,&msg);
				break;
			case 5://私聊
				chat_along(client_socket,&msg);
				break;
			case 6://注销
				logout(client_socket,&msg);
				break;
			case 7://退出
				user_quit(client_socket,&msg);
				break;
			case 8://文件传输
				file_transfer(client_socket,&msg);
				break;
		}
	}
	
	close(client_socket);
}

int main()
{
	//打开数据库
	int ret = sqlite3_open("chatroom.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打开数据库失败\n");
		return -1;
	}
	printf("数据库打开成功\n");
	
	//创建用户登录表
	char *errmsg = NULL;
	char *sql = "create table if not exists login(username TEXT,password TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return -1;
	}
	printf("用户登录表创建成功\n");
	
	//创建好友链表
	head = Create_List();
	if(head == NULL)
	{
		printf("链表创建失败\n");
		return -1;
	}
	printf("链表创建成功\n");
	
	//初始化套接字
	int listen_socket = Init_Socket();
	
	while(1)
	{
		//获取与客户端连接的套接字
		int client_socket = MyAccept(listen_socket);
		
		pthread_t pthread_id;
		int ret = pthread_create(&pthread_id, NULL, handle_client,(void*)client_socket);
		if(ret != 0)
		{
			perror("pthread_create");
			return -1;
		}
		
		pthread_detach(pthread_id);
	}
	
	Clean_List(head);
	Destroy(head);
	close(listen_socket);
	sqlite3_close(database);

    return 0;
}


客户端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "LinkList.h"

typedef struct _msg
{
	int cmd;
	int flag;
	char username[20];
	char password[20];
	char msg[1024];
	char frname[20];
	char toname[20];
	char time[50];
	char filename[20];
}Msg;

sqlite3 *database;

//与服务器连接,返回与服务器通信的套接字
int MyConnect(char *argv[])
{
	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(atoi(argv[2])); 
	inet_aton(argv[1],&(addr.sin_addr));
	
	int ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));
	if (ret == -1)
	{
		perror ("connect");
		return -1;
	}

	printf ("成功连上服务器\n");
	
	return socketfd;
}

void Ask_UserServer(int socketfd);

//登录界面
void interface()
{
	time_t Time = time(NULL);
	char *buf = ctime(&Time);
	
	system("clear");
	printf("%s",buf);
	printf("|****************************|\n");
	printf("|          欢迎使用          |\n");
	printf("|****************************|\n");
	printf("|       1、用户注册          |\n");
	printf("|       2、用户登录          |\n");
	printf("|       3、退出              |\n");
	printf("|****************************|\n");
	printf("请输入您要进行的操作:\n");
}

//用户界面
void userface()
{
	time_t Time = time(NULL);
	char *buf = ctime(&Time);
	
	system("clear");
	printf("%s",buf);
	printf("|****************************|\n");
	printf("|           主菜单           |\n");
	printf("|****************************|\n");
	printf("|      1、显示在线好友       |\n");
	printf("|      2、群聊               |\n");
	printf("|      3、私聊               |\n");
	printf("|      4、查看聊天记录       |\n");
	printf("|      5、用户注销           |\n");
	printf("|      6、文件传输           |\n");
	printf("|      7、退出               |\n");
	printf("|****************************|\n");
	printf("请输入您要进行的操作:\n");
}

//在线好友显示
void Display_Friend(int socketfd)
{
	Msg msg;
	msg.cmd = 3;
	write (socketfd, &msg, sizeof(msg));
}

//群聊
void chat_all(int socketfd)
{
	Msg msg;
	msg.cmd = 4;
	printf ("请输入要发送的内容: \n");
	fgets(msg.msg, 1024, stdin);
	write (socketfd, &msg, sizeof(msg));
}

//私聊
void chat_along(int socketfd)
{
	Msg msg;
	msg.cmd = 5;
	printf("请输入您要发送的对象:\n");
	scanf("%s",msg.toname);
	while(getchar() != '\n');
	
	printf ("请输入要发送的内容: \n");
	fgets(msg.msg, 1024, stdin);
	
	write (socketfd, &msg, sizeof(msg));
}

//将数据插入本地数据库
void insert_records(sqlite3 *database,Msg msg)
{
	char buf[100];
	char *errmsg = NULL;
	sprintf (buf, "insert into records values('%s', '%s', '%s','%s')",msg.time,msg.frname,msg.toname,msg.msg);
	int ret = sqlite3_exec(database, buf, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return;
	}
}

//查看聊天记录
void get_records(sqlite3 *database)
{
	char *errmsg = NULL;
	char **resultp = NULL;
	int nrow, ncolumn;
	char *sql = "select * from records";
	int ret = sqlite3_get_table(database, sql, &resultp, &nrow, &ncolumn, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return;
	}	
		

	int i;
	for (i = 0; i < (nrow+1)*ncolumn; i++)
	{
		if (i % ncolumn == 0)
			printf ("\n");
		printf ("%-26s", resultp[i]);
	}
	printf ("\n");
}

//用户注销
void logout(int socketfd)
{
	Msg msg;
	msg.cmd = 6;
	write (socketfd, &msg, sizeof(msg));
}

//文件传输
void file_transfer(int socketfd)
{
	Msg msg;
	msg.cmd = 8;
	printf("请输入您要发送的对象:\n");
	scanf("%s",msg.toname);
	while(getchar() != '\n');
	
	printf("当前文件夹下的文件:\n");
	system("ls");
	printf ("请输入要发送的文件: \n");
	fgets(msg.filename, 20, stdin);
	
	char *tmp = msg.filename;
	while(tmp)
	{
		if(*tmp == '\n')
		{
			*tmp = '\0';
			break;
		}
		tmp++;
	}
	
	//printf("%s",msg.filename);
	FILE *fp1 = fopen(msg.filename, "r");
	if (fp1 == NULL)
	{
		perror ("fopen fp1");
		return;
	}
	
	int ret;
	while (ret = fread(msg.msg, sizeof(char), 1023, fp1))
	{	
		msg.msg[1023] = '\0';
		//sleep(2);
		//printf("%s",msg.msg);
		usleep(15);
		write(socketfd, &msg, sizeof(msg));
		memset(msg.msg,0,1024);
	}
	
	if(ret == 0 && !feof(fp1))
	{
		perror ("fread");
		return;
	}
	
	strcpy(msg.msg,"over");
	write(socketfd, &msg, sizeof(msg));
	fclose(fp1);
}

void receive_file(Msg *msg)
{
	FILE *fp2 = fopen("2.ppt", "a");
	if (fp2 == NULL)
	{
		perror ("fopen fp2");
		return;
	}
	
	fwrite(msg->msg,sizeof(char),1023,fp2);
	
	fclose(fp2);
}

//退出登录
void user_quit(int socketfd)
{
	Msg msg;
	msg.cmd = 7;
	write (socketfd, &msg, sizeof(msg));
}

//读写分离
void * readMsg(void *v)
{
	int socketfd = (int)v;
	Msg msg;
	while (1)
	{
		read (socketfd, &msg, sizeof(msg));
		
		time_t Time = time(NULL);
		char *buf = ctime(&Time);
		*(buf+24) = '\0';
		strcpy(msg.time,buf);
		
		switch(msg.cmd)
		{
			case 3:
				printf("在线好友:%s\n",msg.username);
				break;
			case 4:
				printf("%s  %s发了一条群消息: %s\n",msg.time,msg.frname,msg.msg);
				insert_records(database,msg);
				break;
			case 5:
				if(msg.flag == 101)
				{
					printf("%s  %s给您发了一条消息:%s",msg.time,msg.frname,msg.msg);
					insert_records(database,msg);
				}
				else if(msg.flag == -101)
					printf("您要发送的好友不存在\n");
				break;
			case 6:
				printf("注销成功\n");
				printf("按回车键返回......\n");
				return;
			case 7:
				printf("退出成功\n");
				printf("按回车键返回......\n");
				return;
			case 8:
				if(msg.flag == 101)
				{
					receive_file(&msg);
				}
				else if(msg.flag == 102)
				{
					printf("%s 给您发送了一个文件\n",msg.frname);
				}
				else if(msg.flag == -101)
					printf("您要发送的好友不存在\n");
				break;
		}
	}
}

//注册
void reg(int socketfd)
{
	Msg msg;
	char username[20];
	char password[20];
	msg.cmd = 1;
	printf("请输入用户名:");
	//getchar();
	//fgets(username,20,stdin);
	scanf("%s",username);
	strcpy(msg.username,username);
	printf("请输入密码:");
	//getchar();
	//fgets(password,20,stdin);
	scanf("%s",password);
	strcpy(msg.password,password);
	
	write(socketfd,&msg,sizeof(Msg));
	
	read(socketfd,&msg,sizeof(Msg));
	
	if(msg.cmd == -1)
	{
		printf("用户已存在,注册失败\n");
		return;
	}
	else if(msg.cmd == 1001)
		printf("注册成功\n");
}

//登录
void login(int socketfd)
{
	Msg msg;
	char username[20];
	char password[20];
	msg.cmd = 2;
	printf("请输入用户名:");
	scanf("%s",username);
	strcpy(msg.username,username);
	printf("请输入密码:");
	scanf("%s",password);
	strcpy(msg.password,password);
	
	write(socketfd,&msg,sizeof(Msg));
	
	read(socketfd,&msg,sizeof(Msg));
	
	if(msg.cmd == -2)
	{
		printf("用户名或密码错误,登陆失败\n");
		return;
	}
	
	else if(msg.cmd == 1002)
		printf("登录成功\n");
	
	pthread_t id;
	pthread_create(&id, NULL, readMsg,  (void *)socketfd);
	
	pthread_detach(id);
	
	sleep(2);
	
	Ask_UserServer(socketfd);
}

//注册、登录服务
void Ask_Server(int socketfd)
{
	int choice;
	while(1)
	{
		interface();
		scanf("%d",&choice);
		
		switch(choice)
		{
			case 1:
				reg(socketfd);
				getchar();
				sleep(2);
				break;
			case 2:
				login(socketfd);
				getchar();
				sleep(2);
				break;
			case 3:
				return;
			default:
				break;
		}
	}
}

//用户服务
void Ask_UserServer(int socketfd)
{
	int choice;
	while(1)
	{
		userface();
		scanf("%d",&choice);
		while(getchar()!= '\n');
		
		switch(choice)
		{
			case 1:
				Display_Friend(socketfd);
				getchar();
				break;
			case 2:
				chat_all(socketfd);
				getchar();
				break;
			case 3:
				chat_along(socketfd);
				getchar();
				break;
			case 4:
				get_records(database);
				getchar();
				break;
			case 5:
				logout(socketfd);
				return;
			case 6:
				file_transfer(socketfd);
				break;
			case 7:
				user_quit(socketfd);
				return;
		}
	}
}

int main(int argc, char *argv[])
{
	//打开数据库
	int ret = sqlite3_open("chatroom.db", &database);
	if (ret != SQLITE_OK)
	{
		printf ("打开数据库失败\n");
		return -1;
	}
	
	//创建本地聊天记录表
	char *errmsg = NULL;
	char *sql = "create table if not exists records(time TEXT,frname TEXT,toname TEXT,msg TEXT)";
	ret = sqlite3_exec(database, sql, NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
	{
		printf ("数据库操作失败:%s\n", errmsg);
		return -1;
	}
	
	int socketfd = MyConnect(argv);
	
	Ask_Server(socketfd);
	
	close(socketfd);
	sqlite3_close(database);

    return 0;
}



你可能感兴趣的:(基于C/S架构的网络聊天室)