新手上路,自己学得一知半解,按照自己的理解,码了一个基于UDP的聊天服务器,后续会写与其配套的客户端。
此服务器,主要有以下几点的功能:
1.登录功能
2.注册功能
3.查询功能
4.退出功能
其具体介绍如下:
1.登录功能
传输数据格式:char recvBuf[100]
客户端向服务器传输时格式为:
当首位recvBuf[0]为'L'时,进入登录功能。
[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地址