UDP网络编程实战:基于MYSQL数据库的聊天服务器的搭建

  新手上路,自己学得一知半解,按照自己的理解,码了一个基于UDP的聊天服务器,后续会写与其配套的客户端。

  此服务器,主要有以下几点的功能:

  1.登录功能

  2.注册功能

  3.查询功能

  4.退出功能

  其具体介绍如下:

  1.登录功能

     传输数据格式:char recvBuf[100]

     客户端向服务器传输时格式为:

     当首位recvBuf[0]为'L'时,进入登录功能。

扫描二维码关注公众号,回复: 2519084 查看本文章

     [1]~[10]为用户名user;

     [11]~[20]为密码password;

     [21]~[35]为IP地址ip;(每次登录时要进行更新)

     服务端向客户端返回格式为:

     [0]为'L'

     [1]若为'1'则成功,若为'0'则失败(客户端需要对其返回消息进行相应操作)

     登录功能将用户名和密码于数据库中信息进行比对,若正确,则返回"L1"并且更新该用户的IP地址,否则则返回"L0"即登录失败。

  2.注册功能

    传输数据格式:char recvBuf[100]

     客户端向服务器传输时格式为:

     当首位recvBuf[0]为'R'时,进入注册功能。

     [1]为'1'时,进入注册查重功能,为'0'时,进入实际注册功能 

     2.1 查重功能

          客户端向服务器传输时格式为:

          'R'+'1'+user,其中user占10个长度

          服务器向客户端返回格式为:

          "R11"重复,"R10"不重复

          其主要功能为检测用户名是否重复(在数据库中)。

      2.2 实际注册功能

           客户端向服务器传输时格式为:

           'R'+'0'+user+password+IP,其中user,password,IP占用长度分别为10,10,15

            服务器向客户端返回格式为:

            "R01"成功,"R00"失败

            其主功能为向数据库中插入一条新的用户数据(包括用户名,密码,当前IP)

    3.查询功能

       客户端向服务器传输时格式为:

       'F'+user,其中user占10个长度

        服务器向客户端返回格式为:

        "F0"没用找到该用户;

        'F'+'1'+IP 有该用户,且IP为...,其中IP占15个长度

        主要功能为查询目标用户的IP。

    4.退出功能

       客户端向服务器传输时格式为:

       'q'+user,其中user占10个长度

       服务器向客户端返回格式为:

       "q1"成功退出;"q0"退出失败

       主要功能为告知服务器此用户已退出,并把此用户的IP改为"xxx.xxx.xxx.xxx"

其下是具体代码:

#include <Winsock2.h>
#include <stdio.h>
#include <iostream>
#include <winsock.h>
#include <mysql.h>
#pragma comment(lib,"libmySQL.lib")
using namespace std;

void menu(char recvBuf0,char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrSrv,int len);
void login(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len);
void _register(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len);
void findIP(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len);
void quit(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len);
BOOL find(MYSQL* mysql,string u,string p);

struct conn_info {
	char *host;
	char *user;
	char *password;
	char *db;
};

class Mysql
{
public:
/********************************连接数据库*****************************************************/
	MYSQL* mysql_connect(conn_info con){
		MYSQL* mysql=mysql_init(NULL);//mysql初始化函数,实例化一个新的对象(NULL为新的对象,否则为初始化以创建的对象)
		if(!mysql_real_connect(mysql,con.host,con.user,con.password,con.db,3306,NULL,0)){//mysql_real_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag)
			//上面的函数为,与mysql进行连接
			cout<<"connection error";
			exit(1);
		}
		return mysql;//返回实例化的对象
	}
 
 
/****************************************数据库操作函数*******************************************************/
	void mysql_operation(MYSQL* mysql,char *sql_op)
	{
		if(mysql_query(mysql,sql_op))//mysql_query()函数为对mysql进行操作,第一个参数为刚才实例化的对象,第二个位需要进行的操作
		{ 
			printf("MySQL query error : %s/n", mysql_error(mysql));  //需要加上这个,因为这个是操作失败时返回显示
			getchar();
		 	exit(1);
		}	
	}
 
 
/*******************************查询函数***********************************************************/
	MYSQL_RES* mysql_find(MYSQL* mysql,const char *table,MYSQL_RES* res){
		char cmd[100];
		strcpy(cmd,"select * from ");
		strcat(cmd,table);		//拼接函数
 
 
//		cout<<cmd<<endl;
 
 
		mysql_free_result(res);
		mysql_operation(mysql,cmd);
		return mysql_store_result(mysql);//查询的结果,用mysql_store_result函数后接mysql_affected_rows()可以返回查询次数,而用mysql_use_result()则不行
	}
 
 
/*****************************************插入数据*********************************************************************/
	void mysql_insert(MYSQL* mysql,string u0,string p0,string ip0)//需要输入数据为:已经创建好的对象mysql和结构体的指针头部*p
	{
		char cmd[100];
 
 
		strcpy(cmd,"insert zkw_user values(\"");
		strcat(cmd,u0.c_str());
		strcat(cmd,"\",\"");
		strcat(cmd,p0.c_str());
		strcat(cmd,"\",\"");
		strcat(cmd,ip0.c_str());
		strcat(cmd,"\")");
 
 
//		cout<<cmd<<endl;
 
 
		mysql_operation(mysql,cmd);
	}
 
 
/*****************************************显示函数*********************************************************************/
	void mysql_show(MYSQL_RES* res)//用于存储查询返回的数据,但这个数据还不能显示,需要进行转换
	{
		MYSQL_ROW row;
		while (( row = mysql_fetch_row(res) ) != NULL)//	row = mysql_fetch_row(res);进行数据的转换,需要循环转换,具体看程序最后的解释
		{
			cout<<row[0]<<"  "<<row[1]<<"  "<<endl;//显示的行数,注意此处表格中只有两列,故row也只有2列,若写多列会出错
		 
		}
	}
 
 
/******************************************精确查询函数************************************************************************/
	BOOL mysql_found(MYSQL* mysql,const char *table,MYSQL_RES* res,int i,string str1)//因为row为const char *类型,故需要str1也为此类型,因为要两者进行比较,i为需要比较哪列,
	{
		MYSQL_ROW row;
		res = mysql_find(mysql,"zkw_user",res);
 
 
		while (( row = mysql_fetch_row(res) ) != NULL)//	row = mysql_fetch_row(res);进行数据的转换,需要循环转换,具体看程序最后的解释
		{
			if( strcmp(str1.c_str(),row[i]) == 0 ) //判断是否相等
			{
				return TRUE;
				break;
			}
		}
		if(row == NULL)
		{
			return FALSE;
		}
	}

/*********************************************查找并返回数据******************************************************************/
	string mysql_found_data(MYSQL* mysql,const char *table,MYSQL_RES* res,int i,int j,string str1)//因为row为const char *类型,故需要str1也为此类型,因为要两者进行比较,i为需要比较哪列,j为需要返回哪列
	{
		MYSQL_ROW row;
		string str2;
		string str3;
		res = mysql_find(mysql,"zkw_user",res);
 
 
		while (( row = mysql_fetch_row(res) ) != NULL)//	row = mysql_fetch_row(res);进行数据的转换,需要循环转换,具体看程序最后的解释
		{
			str2 = row[i];
			str3 = row[j];
			if( strcmp(row[i],str1.c_str()) == 0 ) //判断是否相等
			{
				return str3;
				break;
			}
		}
	}
 
 
/***************************************************判断重复****************************************************************/
 
	int mysql_repeat(MYSQL* mysql,const char *table,MYSQL_RES* res,int i,const char *str1)//因为row为const char *类型,故需要str1也为此类型,因为要两者进行比较,i为需要比较哪列
	{
		int flag=0;//返回为1为重复,0为不重复
		MYSQL_ROW row;
		res = mysql_find(mysql,"zkw_user",res);
 
 
		while (( row = mysql_fetch_row(res) ) != NULL)//	row = mysql_fetch_row(res);进行数据的转换,需要循环转换,具体看程序最后的解释
		{
			if( strcmp(row[i],str1) == 0) //判断是否相等
			{
				flag=1;
				break;
			}
		}
		return flag;
	}
 
/***************************************************删除函数(删除某一行)***************************************************************/
	void mysql_del(MYSQL* mysql,const char *table,const char *row,const char *str0)
	{
		char cmd[100];
 
 
		strcpy(cmd,"delete from ");//Mysql 删除语句为 delete from 表格名 where 列名 like 检索条件
		strcat(cmd,table);
		strcat(cmd," where ");
		strcat(cmd,row);
		strcat(cmd," like ");
		strcat(cmd,"\"");
		strcat(cmd,str0);
		strcat(cmd,"\"");
	//	cout<<cmd<<endl;
 
 
		mysql_operation(mysql,cmd);
	}
 
 
/**************************************************更改函数****************************************************************************/
	void mysql_change(MYSQL* mysql,const char *table,const char *row1,const char *row1_data,const char *row2,const char *row2_data)
	{					//(对象,表格名,列名(用于修改的列),该列数据(用于修改的数据),列名(用于查找),该列数据(用于查找))
		char cmd[100];
 
 
		strcpy(cmd,"update ");
		strcat(cmd,table);
		strcat(cmd," set ");
		strcat(cmd,row1);
		strcat(cmd,"=\"");
		strcat(cmd,row1_data);
		strcat(cmd,"\" where ");
		strcat(cmd,row2);
		strcat(cmd,"=\"");
		strcat(cmd,row2_data);
		strcat(cmd,"\"");
 
 
//		cout<<cmd<<endl;
 
 
		mysql_operation(mysql,cmd);
	}
 
 
/***************************************上一次查询、删除、添加的操作次数*****************************************************/
	const mysql_number(MYSQL* mysql)
	{
		const length = mysql_affected_rows(mysql);//记录刚才进行查询的次数
		return length;
	}
};



void menu(char recvBuf0,char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len)
{
	switch(recvBuf0)
	{
	case 'q':
	/*	sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len);
		printf("Chat end!\n");*/
		quit(recvBuf,sockSrv,addrClient,len);
		break;
	case 'L':
		login(recvBuf,sockSrv,addrClient,len);
		break;
	case 'R':
		_register(recvBuf,sockSrv,addrClient,len);
		break;
	case 'F':
		findIP(recvBuf,sockSrv,addrClient,len);
		break;
	default :
		break;
	}
}

//查找函数
BOOL find(MYSQL* mysql,string u,string p)
{
	Mysql _mysql;

	if(_mysql.mysql_found(mysql,"zkw_user",NULL,0,u) &&
		_mysql.mysql_found(mysql,"zkw_user",NULL,1,p))
		return TRUE;
	else
		return FALSE;
}

//登录程序
void login(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len)
{
	struct conn_info info;

	info.host="localhost";
	info.user="root";
	info.password="123456";
	info.db="mysql";

	Mysql mysql;
	MYSQL* conn;
	conn = mysql.mysql_connect(info); 

	string _recvBuf = recvBuf;
	string u = _recvBuf.substr(1,10);
	string p = _recvBuf.substr(11,10);
	string ip = _recvBuf.substr(21,15);



	if(find(conn,u,p))
	{
		sendto(sockSrv,"L1",strlen("L1")+1,0,(SOCKADDR*)&addrClient,len);//返回"L1"即登陆成功
		mysql.mysql_change(conn,"zkw_user","ip",ip.c_str(),"user",u.c_str());
	}
	else
		sendto(sockSrv,"L0",strlen("L0")+1,0,(SOCKADDR*)&addrClient,len);//返回"L0"即登录失败

	mysql_close(conn);
}

//注册函数
void _register(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len)
{
	struct conn_info info;

	info.host="localhost";
	info.user="root";
	info.password="123456";
	info.db="mysql";

	Mysql mysql;
	MYSQL* conn;
	MYSQL_RES* res;
	res = NULL;
	conn = mysql.mysql_connect(info); 

	string _recvBuf = recvBuf;
	string u0 = _recvBuf.substr(2,10);
	string p0;
	string ip0;

	if(_recvBuf[1] == '0')
	{
		p0 = _recvBuf.substr(12,10);
		ip0 = _recvBuf.substr(22,15);
	}

	if(_recvBuf[1] == '1')
	{
		//进行查重
		if(mysql.mysql_found(conn,"zkw_user",NULL,0,u0))
			sendto(sockSrv,"R11",strlen("R11")+1,0,(SOCKADDR*)&addrClient,len);		//R11重复
		else
			sendto(sockSrv,"R10",strlen("R10")+1,0,(SOCKADDR*)&addrClient,len);		//R10不重复	
	}
	if(recvBuf[1] == '0')
	{
		//进行注册
		
		mysql.mysql_find(conn,"zkw_user",res);
		const length0 = mysql.mysql_number(conn);		//判断注册之前的人数

		mysql.mysql_insert(conn,u0,p0,ip0);
			
		mysql.mysql_find(conn,"zkw_user",res);
		const length1 = mysql.mysql_number(conn);		//判断注册之后的人数

		if(length1>length0)
			sendto(sockSrv,"R01",strlen("R01")+1,0,(SOCKADDR*)&addrClient,len);
		else
			sendto(sockSrv,"R00",strlen("R00")+1,0,(SOCKADDR*)&addrClient,len);
	}
	
	mysql_close(conn);
}

//查询IP函数
void findIP(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len)
{
	struct conn_info info;

	info.host="localhost";
	info.user="root";
	info.password="123456";
	info.db="mysql";

	Mysql mysql;
	MYSQL* conn;
	MYSQL_RES* res;
	res = NULL;
	conn = mysql.mysql_connect(info); 

	string _recvBuf = recvBuf;
	string u = _recvBuf.substr(1,10);
	string re = "F1";
	string ip;

	if(mysql.mysql_found(conn,"zkw_user",NULL,0,u))
	{
		ip = mysql.mysql_found_data(conn,"zkw_user",NULL,0,2,u);
		re += ip;
		sendto(sockSrv,re.c_str(),strlen(re.c_str())+1,0,(SOCKADDR*)&addrClient,len);
	}else
	{
		sendto(sockSrv,"F0",strlen("F0")+1,0,(SOCKADDR*)&addrClient,len);
	}
	
	mysql_close(conn);
	
}

//退出函数
void quit(char *recvBuf,SOCKET sockSrv,SOCKADDR_IN addrClient,int len)
{
	struct conn_info info;

	info.host="localhost";
	info.user="root";
	info.password="123456";
	info.db="mysql";

	Mysql mysql;
	MYSQL* conn;
	MYSQL_RES* res;
	res = NULL;
	conn = mysql.mysql_connect(info); 

	string _recvBuf = recvBuf;
	string u = _recvBuf.substr(1,10);

	if(mysql.mysql_found(conn,"zkw_user",NULL,0,u))
	{
		mysql.mysql_change(conn,"zkw_user","ip","xxx.xxx.xxx.xxx","user",u.c_str());
		sendto(sockSrv,"q1",strlen("q1")+1,0,(SOCKADDR*)&addrClient,len);
	}else
	{
		sendto(sockSrv,"q0",strlen("q0")+1,0,(SOCKADDR*)&addrClient,len);
	}

	mysql_close(conn);
}

void main()
{
		//加载套接字库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD ( 1, 1);

	err = WSAStartup( wVersionRequested, &wsaData);
	if( err != 0 )
	{
		return;
	}

	if( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1)
	{
		WSACleanup();
		return;
	}
	//创建套接字
	SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);		

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	//绑定套接字
	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	char recvBuf[100];
	char sendBuf[100];
	char tempBuf[200];

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	while(1)
	{
		//等待并接收数据
		recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
		menu(recvBuf[0],recvBuf,sockSrv,addrClient,len);
	}
	//关闭套接字
	closesocket(sockSrv);
	WSACleanup();
}

其下是我客户端的程序链接

https://download.csdn.net/download/qq_37358422/10573696

注意需要在第一个头文件修改服务器的IP地址

猜你喜欢

转载自blog.csdn.net/qq_37358422/article/details/81229028
今日推荐