说明:
使用qt的network的TCP/IP 网络通信API
与 linux下简单的sqlite3 数据库进行数据互传,实现登录系统
实现原理为:
1.linux服务器:
若使用云服务器请见此说明:使用云服务器的区别在于连接的ip不同而已,本代码已验证在阿里云Ubuntu16.04 ECS服务器下验证可行,从而实现简易的远程注册登录,若使用云服务器,且在安全组策略中添加相应的端口入和处规则,详细操作请自行百度
**为实现TCP/IP数据传输,请参考如下c语言所定义的数据交换的数据包以及所使用的所有的头文件,其中在建立或者打开数据库db文件时,将会使用sql查询语句查询admin是否存在,否则则会插入一个用户:admin, 密码为:123456,并使用自增主键,开始的位置为10000。 **
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define portnumber 6666 //所定义的端口
typedef struct socket_head
{
int fun; //功能码 1 登陆 2 注册
}HEAD_T;
typedef struct socket_login
{
char username[32];
char userpwd[32];
}LOGIN_T;
typedef struct loginret //应答包
{
int flag;// 0 fail 1 success
char loginuser[32];
}LOGINRET_T;
服务器主函数如下方代码所示
int main(int argc, char *argv[])
{
sqlite3 *db;
char *errmsg=NULL; //用来存储错误信息字符串
char ret=0;
char **dbResult;
int nRow=0, nColumn=0; //nRow 查找出的总行数,nColumn 存储列
int rc;
int temp_flag=0;
char sql[128];
int sockfd,new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size;
int nbytes;
char buffer[1024];
char Backdata[1024];
char *ptr;
HEAD_T reverse_head;
LOGIN_T reverse_login;
LOGINRET_T reverse_data;
ptr =(char*)malloc(sizeof(char));
memset(ptr,0,sizeof(char));
memset(sql,0,sizeof(char));
rc = sqlite3_open("linuxdb.db",&db); //打开数据库,如果不存在,创造数据库db
if(rc!=SQLITE_OK)
{
sqlite3_close(db);
fprintf(stderr,"can't open database:%s\n",sqlite3_errmsg(db));
exit(0);
return 0;
}
else
{
printf("creat db success\n");
}
ret=sqlite3_get_table(db,"create table if not exists user ( ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME VARCHAR(32) , PASSWORD VARCHAR(32) );",&dbResult,&nRow,&nColumn,&errmsg);
if(NULL!=errmsg)
{
printf("create fail -- user table\n");
sqlite3_free_table(dbResult);
errmsg=NULL;
return -1;
}
else
{
printf("creat user table success\n");
}
nRow=0;
nColumn=0;
errmsg=NULL;
ret=sqlite3_get_table(db,"select * from user where NAME = 'admin'; ",&dbResult,&nRow,&nColumn,&errmsg);
if(NULL!=errmsg)
{
sqlite3_free_table(dbResult);
errmsg=NULL;
}
printf("nRow=%d",nRow);
if(nRow==0)
{
nRow=0;
nColumn=0;
errmsg=NULL;
printf("admin is not exsit\n");
ret=sqlite3_get_table(db,"insert into user values(10000,'admin','123456');",&dbResult,&nRow,&nColumn,&errmsg);
if(NULL!=errmsg)
{
printf("insert admin error\n");
sqlite3_free_table(dbResult);
errmsg=NULL;
}
}
/* 服务器端开始建立sockfd描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4; SOCK_STREAM:TCP
{
fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
exit(1);
}
//设置Socket属性:SO_REUSEADDR:允许在bind过程中本地地址重复使用
int iSockopt = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(const char *)&iSockopt, sizeof(int)) < 0 )
{
close(sockfd);
return -1;
}
/* 服务器端填充 sockaddr结构 */
bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0
server_addr.sin_family=AF_INET; // Internet
server_addr.sin_addr.s_addr=INADDR_ANY; //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
//用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
server_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号
/* 捆绑sockfd描述符到IP地址 */
if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
exit(1);
}
/* 设置允许连接的最大客户端数 */
if(listen(sockfd,5)==-1)
{
fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
exit(1);
}
while(1)
{
/* 服务器阻塞,直到客户程序建立连接 */
sin_size=sizeof(struct sockaddr_in);
if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)
{
fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
exit(1);
}
fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); // 将网络地址转换成.字符串
memset(buffer,0,sizeof(buffer));
memset(reverse_login.username,0,sizeof(reverse_login.username));
memset(reverse_login.userpwd,0,sizeof(reverse_login.userpwd));//给reverse_login进行清空操作
memset(Backdata,0,sizeof(Backdata));
memset(reverse_data.loginuser,0,sizeof(reverse_data.loginuser));
reverse_data.flag=0;
nbytes=0;
if((nbytes=read(new_fd,buffer,1024))==-1)
{
recv( new_fd, buffer, 1024, 0 );
fprintf(stderr,"Read Error:%s\n",strerror(errno));
exit(1);
}
buffer[ nbytes]='\0';
ptr=buffer;
printf("nbytes= %d buffer=%d\n",nbytes,sizeof(buffer));
printf("Server received %s\n",buffer); //这个通讯已经结束
memcpy(&reverse_head, ptr, sizeof(HEAD_T));
printf("head=%d\n ",reverse_head.fun);
switch(reverse_head.fun)
{
case 0:
{
printf("connect\n");
memcpy(Backdata,&reverse_head,sizeof(HEAD_T));
write(new_fd,Backdata,sizeof(Backdata));
break;
}
case 1:
{
printf("login\n");
memcpy(&reverse_login, ptr+ sizeof(HEAD_T), sizeof(LOGIN_T));
printf("name=%s pwd=%s\n ",reverse_login.username,reverse_login.userpwd);
nRow=0;
nColumn=0;
errmsg=NULL;
sprintf(sql,"select * from user where name='%s' and password='%s';",reverse_login.username,reverse_login.userpwd);
printf("%s\n",sql);
rc=sqlite3_get_table(db,sql,&dbResult,&nRow,&nColumn,&errmsg);
printf("nRow=%d nColumn=%d\n",nRow,nColumn);
if(rc==SQLITE_OK)
{
if(nRow==1)
{
printf("login success\n");
}
else if(nRow==0)
{
printf("no account\n");
}
else if(nRow>1)
{
printf("many account\n");
}
}
reverse_data.flag=nRow;
strcpy(reverse_data.loginuser,reverse_login.username);
memcpy(Backdata,&reverse_head,sizeof(HEAD_T));
memcpy(Backdata+sizeof(HEAD_T),&reverse_data,sizeof(LOGINRET_T));
write(new_fd,Backdata,sizeof(Backdata));
break;
}
case 2:
{
printf("register\n");
errmsg=NULL;
memcpy(&reverse_login, ptr+ sizeof(HEAD_T), sizeof(LOGIN_T));
printf("name=%s pwd=%s\n ",reverse_login.username,reverse_login.userpwd);
memset(sql,0,sizeof(sql));
sprintf(sql,"select * from user where name='%s';",reverse_login.username);
printf("%s\n",sql);
rc=sqlite3_get_table(db,sql,&dbResult,&nRow,&nColumn,&errmsg);
printf("nRow=%d nColumn=%d\n",nRow,nColumn);
if(rc==SQLITE_OK)
{
if(nRow>=1)
{
printf("error! account is exist\n");
temp_flag=1;
}
else if(nRow==0)
{
printf("search success!\n");
memset(sql,0,sizeof(sql));
sprintf(sql,"INSERT INTO user (NAME,PASSWORD) VALUES('%s','%s');",reverse_login.username,reverse_login.userpwd);
printf("%s\n",sql);
rc=sqlite3_get_table(db,sql,&dbResult,&nRow,&nColumn,&errmsg);
if(rc==SQLITE_OK)
{
printf("register success");
}
temp_flag=2;
}
else
{
printf("unknown error\n");
temp_flag=3;
}
}
reverse_data.flag=temp_flag;
strcpy(reverse_data.loginuser,reverse_login.username);
memcpy(Backdata,&reverse_head,sizeof(HEAD_T));
memcpy(Backdata+sizeof(HEAD_T),&reverse_data,sizeof(LOGINRET_T));
write(new_fd,Backdata,sizeof(Backdata));
temp_flag=0;
break;
}
}
ptr =NULL;
close(new_fd); /* 循环下一个 */
} /* 结束通讯 */
sqlite3_close(db);
close(sockfd);
exit(0);
}
2.qt5.14 所建的登录注册客户端:
.pro文件中需要加入如下语句使用TCP/IP
QT += network
主函数为
#include "widget.h"
#include "myconnect.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
myconnect w;
w.show();
return a.exec();
}
使用简单的单例模式,放在allwidgetpoint中
其中allwidgetpoint.h为
#ifndef ALLWIDGETPOINT_H
#define ALLWIDGETPOINT_H
class allwidgetpoint
{
public:
static QWidget *ptcp;
static QWidget *plogin;
static QWidget *pregister;
static QString ip;
static QString port;
};
#endif // ALLWIDGETPOINT_H
其中allwidgetpoint.cpp为,主要进行了单例的初始化操作
QWidget* allwidgetpoint::ptcp=NULL;
QWidget* allwidgetpoint::plogin=NULL;
QWidget* allwidgetpoint::pregister=NULL;
QString allwidgetpoint::ip="";
QString allwidgetpoint::port="";
在socket.h中放置简单的与服务器的数据包格式的定义
#ifndef SOCKET_H
#define SOCKET_H
typedef struct socket_head
{
int fun; //功能码 0连接检测 1 登陆 2 注册
}HEAD_T;
typedef struct socket_login
{
char username[32];
char userpwd[32];
}LOGIN_T;
typedef struct socket_register
{
char username[32];
char userpwd[32];
}REGISTER_T;
typedef struct loginret //应答包
{
int flag;// 0 fail 1 success
char loginuser[32];
}LOGINRET_T;
#endif // SOCKET_H
在connect中所实现的效果为
在点击connect按钮并且接收到数据库的数据包后
则如下图显示
在connect.h中建立一个新的QWidget以实现端口连接
class myconnect : public QWidget
{
Q_OBJECT
public:
myconnect(QWidget *parent = nullptr);
~myconnect();
char Date[128];
public slots:
void Button_connect_click();
void ReadBuffer();
private:
Mylogin *mylogin;
QLabel *Label_ip;
QLabel *Label_port;
QLineEdit *Edit_ip;
QLineEdit *Edit_port;
QTcpSocket *tcpclient;
QPushButton *Button_connect;
};
在connect.cpp中为
myconnect::myconnect(QWidget *parent)
{
allwidgetpoint::ptcp=this;
this->setFixedSize(500,300);
QDesktopWidget *deskdop = QApplication::desktop();
move((deskdop->width() - this->width())/2, (deskdop->height() - this->height())/2);
mylogin =new Mylogin();
tcpclient=new QTcpSocket();
this->Label_ip =new QLabel("Ip",this,0x00000000);
this->Label_ip->setGeometry(QRect(150,80,30,20));
this->Label_port =new QLabel("Port",this,0x00000000);
this->Label_port->setGeometry(QRect(150,110,30,20));
this->Edit_ip=new QLineEdit("192.168.1.109",this);
this->Edit_ip->setGeometry(QRect(190,80,150,20));
allwidgetpoint::ip="192.168.1.109";
this->Edit_port=new QLineEdit("6666",this);
this->Edit_port->setGeometry(QRect(190,110,150,20));
allwidgetpoint::port="6666";
this->Button_connect= new QPushButton("Connect",this);
this->Button_connect->setGeometry(QRect(230,170,50,20));
connect(Button_connect,SIGNAL(clicked(bool)),this,SLOT(Button_connect_click()));
connect(this->tcpclient,SIGNAL(readyRead()),this,SLOT(ReadBuffer()));
}
myconnect::~myconnect()
{
}
void myconnect::Button_connect_click()
{
allwidgetpoint::ip=this->Edit_ip->text();
allwidgetpoint::port=this->Edit_port->text();
qDebug()<<"ip="<<allwidgetpoint::ip<<"port="<<allwidgetpoint::port;
HEAD_T *connect_head = (HEAD_T*)malloc(sizeof(HEAD_T));
memset(connect_head,0,sizeof(HEAD_T));
connect_head->fun=0;
tcpclient->connectToHost(allwidgetpoint::ip,allwidgetpoint::port.toInt());
memcpy(Date,connect_head,sizeof(HEAD_T));
tcpclient->write(Date,sizeof(Date));
}
void myconnect::ReadBuffer()
{
HEAD_T head;
qDebug()<<"read connect";
QByteArray arry = tcpclient->readAll();
memcpy(&head,arry.data(),sizeof(HEAD_T));
qDebug()<<head.fun;
if(head.fun==0)
{
QMessageBox::information(NULL, "Tips", "已连接!");
qDebug()<<"ready OK";
this->hide();
allwidgetpoint::plogin->show();
}
}
class Mylogin: public QWidget
{
Q_OBJECT
public:
Mylogin(QWidget *parent = nullptr);
~Mylogin();
bool checklogin(QString name,QString password);
char Date[128];
public slots:
void Button_register_click();
void Button_login_click();
void ReadBuffer();
private:
QLabel *Label_name;
QLabel *Label_pwd;
QLineEdit *Edit_name;
QLineEdit *Edit_pwd;
myregister *login_register;
QTcpSocket *tcpclient;
QPushButton *Button_login;
QPushButton *Button_register;
QTimer* time;
// QSqlDatabase database;
};
login.cpp代码
Mylogin::Mylogin(QWidget *parent)
: QWidget(parent)
{
allwidgetpoint::plogin=this;
login_register=new myregister();
this->setFixedSize(500,300);
QDesktopWidget *deskdop = QApplication::desktop();
move((deskdop->width() - this->width())/2, (deskdop->height() - this->height())/2);
tcpclient=new QTcpSocket();
this->Label_name =new QLabel("帐号",this,0x00000000);
this->Label_name->setGeometry(QRect(150,80,30,20));
this->Label_pwd =new QLabel("密码",this,0x00000000);
this->Label_pwd->setGeometry(QRect(150,110,30,20));
this->Edit_name=new QLineEdit("",this);
this->Edit_name->setGeometry(QRect(190,80,150,20));
this->Edit_pwd=new QLineEdit("",this);
this->Edit_pwd->setGeometry(QRect(190,110,150,20));
this->Edit_pwd->setEchoMode(QLineEdit::Password);
this->Button_login= new QPushButton("登陆",this);
this->Button_login->setGeometry(QRect(170,170,50,20));
this->Button_register= new QPushButton("注册",this);
this->Button_register->setGeometry(QRect(280,170,50,20));
this->time = new QTimer;
connect(this->tcpclient,SIGNAL(readyRead()),this,SLOT(ReadBuffer()));
connect(((Button_register)),SIGNAL(clicked(bool)),this,SLOT(Button_register_click()));
connect(((Button_login)),SIGNAL(clicked(bool)),this,SLOT(Button_login_click()));
}
Mylogin::~Mylogin()
{
}
bool Mylogin::checklogin(QString name, QString password)
{
QSqlQuery SqlQuery;
QString login_check =QString("select * from user where NAME = '%1' and PASSWORD = '%2'").arg(name).arg(password);
if(!SqlQuery.exec(login_check))//==!QSqlQuery.exec(create_sql)
{
return false;
}
else
{
QString show_name ;
QString show_pwd ;
while(SqlQuery.next())
{
show_name = SqlQuery.value(1).toString();
show_pwd = SqlQuery.value(2).toString();
}
if(show_name==NULL || show_pwd==NULL)
return false;
}
return true;
}
void Mylogin::Button_register_click()
{
qDebug() <<"进入注册";
allwidgetpoint::pregister->show();
this->hide();
}
void Mylogin::Button_login_click()
{
QByteArray ba1 = Edit_name->text().toLatin1();
QByteArray ba2 = Edit_pwd->text().toLatin1();
HEAD_T *user_head = (HEAD_T*)malloc(sizeof(HEAD_T));
memset(user_head,0,sizeof(HEAD_T));
user_head->fun=1;
LOGIN_T *user = (LOGIN_T*)malloc(sizeof(LOGIN_T));
memset(user,0,sizeof(LOGIN_T));
strcpy(user->username,ba1.data());
strcpy(user->userpwd,ba2.data());
memcpy(Date,user_head,sizeof(HEAD_T));
memcpy(Date+sizeof(HEAD_T),user,sizeof(LOGIN_T));
tcpclient->connectToHost(allwidgetpoint::ip,allwidgetpoint::port.toInt());
tcpclient->write(Date,sizeof(Date));
}
void Mylogin::ReadBuffer()
{
HEAD_T head;
LOGINRET_T recevdata ;
qDebug()<<"in";
QByteArray arry = tcpclient->readAll();
memcpy(&head,arry.data(),sizeof(HEAD_T));
memcpy(&recevdata,arry.data()+sizeof(HEAD_T),sizeof(LOGINRET_T));
qDebug()<<head.fun<<recevdata.flag<<recevdata.loginuser;
if(recevdata.flag == 1)
//if((name_check,pwd_check))
{
this->Edit_name->clear();
this->Edit_pwd->clear();
QMessageBox::information(NULL, "Tips", "登陆成功");
}
else
{
this->Edit_pwd->clear();
QMessageBox::information(NULL, "Tips", "账户名/密码错误/账户不存在");
}
}
注册系统如下图所示
class myregister:public QWidget
{
Q_OBJECT
public:
myregister(QWidget *parent = nullptr);
~myregister();
char Date[128];
public slots:
void back_login();
void btn_yes_click();
void ReadBuffer();
private:
QLabel *Label_name;
QLabel *Label_pwd;
QLabel *Label_confirm;
QLineEdit *Edit_name;
QLineEdit *Edit_pwd;
QLineEdit *Edit_confirm;
QPushButton *Button_yes;
QPushButton *Button_back;
QTcpSocket *tcpclient;
// QSqlDatabase database;
};
register.cpp中为
myregister::myregister(QWidget *parent)
: QWidget(parent)
{
allwidgetpoint::pregister=this;
tcpclient=new QTcpSocket();
this->setFixedSize(500,300);
QDesktopWidget *deskdop = QApplication::desktop();
move((deskdop->width() - this->width())/2, (deskdop->height() - this->height())/2);
this->Label_name =new QLabel("帐号",this,0x00000000);
this->Label_name->setGeometry(QRect(150,80,30,20));
this->Label_pwd =new QLabel("密码",this,0x00000000);
this->Label_pwd->setGeometry(QRect(150,110,30,20));
this->Label_confirm=new QLabel("密码确认",this,0x00000000);
this->Label_confirm->setGeometry(QRect(124,140,50,20));
this->Edit_name=new QLineEdit("",this);
this->Edit_name->setGeometry(QRect(190,80,150,20));
this->Edit_pwd=new QLineEdit("",this);
this->Edit_pwd->setGeometry(QRect(190,110,150,20));
this->Edit_pwd->setEchoMode(QLineEdit::Password);
this->Edit_confirm=new QLineEdit("",this);
this->Edit_confirm->setGeometry(QRect(190,140,150,20));
this->Edit_confirm->setEchoMode(QLineEdit::Password);
this->Button_yes= new QPushButton("确认",this);
this->Button_yes->setGeometry(QRect(170,170,50,20));
this->Button_back= new QPushButton("返回",this);
this->Button_back->setGeometry(QRect(280,170,50,20));
connect(this->tcpclient,SIGNAL(readyRead()),this,SLOT(ReadBuffer()));
connect(Button_back,SIGNAL(clicked(bool)),this,SLOT(back_login()));
connect(Button_yes,SIGNAL(clicked(bool)),this,SLOT(btn_yes_click()));
}
myregister::~myregister()
{
}
void myregister::back_login()
{
allwidgetpoint::plogin->show();
this->hide();
}
void myregister::btn_yes_click()
{
QString name_check=this->Edit_name->text();
QString pwd_check= this->Edit_pwd->text();
QString confirm_check= this->Edit_confirm->text();
if(pwd_check.compare(confirm_check)!=0)
{
name_check="";
pwd_check="";
confirm_check="";
QMessageBox::information(NULL, "Tips", "密码不一致,注册失败,请重试");
this->Edit_pwd->clear();
this->Edit_confirm->clear();
return ;
}
else
{
QByteArray ba1 = Edit_name->text().toLatin1();
QByteArray ba2 = Edit_pwd->text().toLatin1();
HEAD_T *register_head = (HEAD_T*)malloc(sizeof(HEAD_T));
memset(register_head,0,sizeof(HEAD_T));
register_head->fun=2;
REGISTER_T *myregister = (REGISTER_T*)malloc(sizeof(REGISTER_T));
memset(myregister,0,sizeof(REGISTER_T));
strcpy(myregister->username,ba1.data());
strcpy(myregister->userpwd,ba2.data());
memcpy(Date,register_head,sizeof(HEAD_T));
memcpy(Date+sizeof(HEAD_T),myregister,sizeof(REGISTER_T));
tcpclient->connectToHost(allwidgetpoint::ip,allwidgetpoint::port.toInt());
tcpclient->write(Date,sizeof(Date));
}
}
void myregister::ReadBuffer()
{
HEAD_T head;
LOGINRET_T recevdata ;
qDebug()<<"in";
QByteArray arry = tcpclient->readAll();
memcpy(&head,arry.data(),sizeof(HEAD_T));
memcpy(&recevdata,arry.data()+sizeof(HEAD_T),sizeof(LOGINRET_T));
qDebug()<<head.fun<<recevdata.flag<<recevdata.loginuser;
switch(recevdata.flag)
{
case 0://服务器端未进入判断
{
QMessageBox::information(NULL, "Tips", "数据库错误 Code:0");
this->Edit_name->clear();
this->Edit_pwd->clear();
this->Edit_confirm->clear();
allwidgetpoint::plogin->show();
this->hide();
break;
}
case 1://服务器端显示用户存在
{
QMessageBox::information(NULL, "Tips", "账户名已存在");
this->Edit_name->clear();
this->Edit_pwd->clear();
this->Edit_confirm->clear();
break;
}
case 2://服务器端显示注册成功
{
QMessageBox::information(NULL, "Tips", "注册成功,即将返回");
this->Edit_name->clear();
this->Edit_pwd->clear();
this->Edit_confirm->clear();
allwidgetpoint::plogin->show();
this->hide();
break;
}
case 3://服务器端显示未知错误
{
QMessageBox::information(NULL, "Tips", "数据库错误 Code:3");
this->Edit_name->clear();
this->Edit_pwd->clear();
this->Edit_confirm->clear();
allwidgetpoint::plogin->show();
this->hide();
break;
}
}
}
可以在此做些改进,如在输入上做些限制,如正则表达式 ip 用户名等进行限制,
故大家可以自行改进,上述版本为初级的版本。