本节以QTcpServer与QTcpSocket为主,联合其它知识(sql与json),编写TCP服务器与客户端,以让读者更好理解Qt的TCP部分各函数功能。程序结构参考自qtcn上的liudianwu的TCP调试工具,原程序链接:点击打开链接。由于是业余爱好者,不知软件公司里这种程序结构,欢迎指点。
————————————————————————————————————————————————————————————
工程 结果图:
整个程序的思路如下:dialog中创建一个Server类(继承自QTcpServer),并调用它的listen函数开始监听。Server类重载了QTcpServer的 incomingConnection(qintptr socketDescriptor)函数,当有新连接时,在这个函数中不断new出新的ServerSocket(继承自QTcpSocket),并用ServerSocket的setSocketDescriptor(qintptr socketDescriptor)函数,将socketDescriptor从Server中传到new出来的socket中,在类Server中,用QMap
在Server类中,我们添加了两个自定义功能,即:
1,实现用户名登陆。函数为:bool login(QString userName,QString passWord);
2,查某用户名的信息(需先登陆),并以json格式返回数据到客户端。函数为:QString getInfoJson(QString userName);
————————————————————————————————————————————————————————————
先写服务器部分。
新建一个带ui的dialog工程。
从局部到整体的顺序,先编写我们的"电话“socket,类名为ServerSocket,继承自 QTcpSocket。向导中如图:
同理,其它类也这样
————————————————————————————————————————————————————————————
下面通过各个头文件,了解各个类及它们的功能
serversocket.h:
#ifndef SERVERSOCKET_H
#define SERVERSOCKET_H
#include
#include
#include
class ServerSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit ServerSocket(QObject *parent = 0,int serverSocketID=0);//在这里为它多加了serverSocketID,将TcpServer的SocketDescriptor传进来。
signals:
void serverSocketReadData(int serverSocketID,QString IP,int Port,QByteArray data);//自建的server类,通过这个信号,得到传输来的数据的来源及具体内容
void serverSocketDisConnect(int serverSocketID,QString IP,int Port);//断开连接时,通过这个信号,Server类将容器idSocketMap内对应的套接字删除
private:
int serverSocketID;
private slots:
void ReadData();//具体实现读取数据
void DisConnect();
void outPutError(QAbstractSocket::SocketError);//qDebug输出套接字出错信息
};
#endif // SERVERSOCKET_H
#ifndef SERVER_H
#define SERVER_H
#include "serversocket.h"
#include
#include
#include
#include
#include
#include
#include
class Server : public QTcpServer
{
Q_OBJECT
public:
explicit Server(QObject *parent = 0);
bool login(QString userName,QString passWord);//通过查询sqlite数据库对比用户名进行登陆,登陆后,将登陆成功的用户添加到用户列表QMap idUserMap;
QString getInfoJson(QString userName);
int serverSocketCount;//用于统计连接的用户数,
private:
QSqlDatabase db;
QMap idSocketMap;
QMap idUserMap;
protected:
void incomingConnection(int serverSocketID);
signals:
void hasData(int serverSocketID,QString IP,int Port,QByteArray data);//这三个信号用于告诉dialog信息,用于dialog的UI的信息显示,比如更新显示用户连接数
void hasConnect(int serverSocketID,QString IP,int Port);
void hasDisConnect(int serverSocketID,QString IP,int Port);
private slots:
void ReadData(int serverSocketID,QString IP,int Port,QByteArray data);//连接到ServerSocket的void serverSocketReadData……信号
void DisConnect(int serverSocketID,QString IP,int Port);
};
#endif // SERVER_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include
#include "server.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
Server *server;
public slots:
void updateCount();
void updateData(int a,QString b,int c,QByteArray d);
};
#endif // DIALOG_H
三个类的具体实现:
serversocket.cpp
#include "serversocket.h"
#include "myhelper.h"
ServerSocket::ServerSocket(QObject *parent,int serverSocketID) :
QTcpSocket(parent)
{
this->serverSocketID=serverSocketID;
connect(this,SIGNAL(readyRead()),this,SLOT(ReadData()));//挂接读取数据信号
connect(this,SIGNAL(disconnected()),this,SLOT(DisConnect()));//关闭连接时,发送断开连接信号
connect(this,SIGNAL(disconnected()),this,SLOT(deleteLater()));//关闭连接时,对象自动删除
connect(this,SIGNAL(error(QAbstractSocket::SocketError)),
this,SLOT(outPutError(QAbstractSocket::SocketError)));
}
void ServerSocket::ReadData()
{
myHelper::Sleep(100);
//读取完整一条数据并发送信号
QByteArray data=this->readAll();
emit serverSocketReadData(this->serverSocketID,this->peerAddress().toString(),this->peerPort(),data);
}
void ServerSocket::DisConnect()
{
//断开连接时,发送断开信号
emit serverSocketDisConnect(this->serverSocketID,this->peerAddress().toString(),this->peerPort());
}
void ServerSocket::outPutError(QAbstractSocket::SocketError)
{
this->disconnectFromHost();
qDebug()<< this->errorString();
}
server.cpp
#include "server.h"
#include "serversocket.h"
#include
#include
Server::Server(QObject *parent) :
QTcpServer(parent)
{
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("myDB.db");
if(!db.open())
{
qDebug()<< "打开数据库出错";
}
}
bool Server::login(QString userName, QString passWord)
{
QSqlQuery query_select(QString("SELECT * FROM userInfo where userName=\"%1\" and passWord=\"%2\" ").arg(userName).arg(passWord));
while(query_select.next())
{
if(query_select.value("userName").toString()=="" )
{
return false;
}else
{
return true;
}
}
return false;
}
QString Server::getInfoJson(QString userName)
{
qDebug()<setSocketDescriptor(serverSocketID);
connect(serverSocket,SIGNAL(serverSocketReadData(int,QString,int,QByteArray)),this,SLOT(ReadData(int,QString,int,QByteArray)));
connect(serverSocket,SIGNAL(serverSocketDisConnect(int,QString,int)),this,SLOT(DisConnect(int,QString,int)));
connect(serverSocket,SIGNAL(aboutToClose()),this,SLOT(deleteLater()));//关闭监听时,对象自动删除
idSocketMap.insert(serverSocketID,serverSocket);
serverSocketCount++;
emit hasConnect(serverSocketID, serverSocket->peerAddress().toString(),serverSocket->peerPort());
qDebug()<error==QJsonParseError::NoError)
{
qDebug()<< "接收到的数据不完整或出错";
return;
}
if(jsonAnalyse.isObject())
{
QJsonObject obj=jsonAnalyse.object();//取得最外层这个大对象
QString str=obj["type"].toString();
QByteArray arrSend;
if(str=="login")
{
QString userName = obj["userName"].toString();
QString passWord = obj["passWord"].toString();
if(login(userName,passWord))
{
qDebug()<< userName << "登陆成功";
idUserMap.insert(serverSocketID,userName);
arrSend.append("{\"type\":\"login result\",\"result\":\"success\"}");
}else
{
qDebug()<< userName << "登陆失败";
arrSend.append("{\"type\":\"login result\",\"result\":\"failed\"}");
}
}else if(str=="getInfo")
{
if(idUserMap.contains(serverSocketID))
{
QString a=getInfoJson(idUserMap.value(serverSocketID));
arrSend=a.toLatin1();
}else
{
arrSend.append("{\"type\":\"login result\",\"result\":\"do not logined\"}");
}
}
ServerSocket *Socket = idSocketMap.value(serverSocketID);
Socket->write(arrSend);
}
}
void Server::DisConnect(int serverSocketID,QString IP,int Port)
{
idSocketMap.remove(serverSocketID);
idUserMap.remove(serverSocketID);
serverSocketCount--;
emit hasDisConnect(serverSocketID,IP,Port);
}
#include "dialog.h"
#include "ui_dialog.h"
#include
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
server = new Server(this);
server->listen(QHostAddress::LocalHost,1992);
connect(server,SIGNAL(hasConnect(int,QString,int)),this,SLOT(updateCount()));
connect(server,SIGNAL(hasDisConnect(int,QString,int)),this,SLOT(updateCount()));
connect(server,SIGNAL(hasData(int,QString,int,QByteArray)),this,SLOT(updateData(int,QString,int,QByteArray)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::updateCount()
{
ui->label_2->setText(QString::number(server->serverSocketCount));
}
void Dialog::updateData(int a, QString b, int c, QByteArray d)
{
QString str=QString("来自:%1;ip:%2;port:%3;data:%4").arg(a).arg(b).arg(c).arg(QString(d));
ui->plainTextEdit->appendPlainText(str);
}