对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)

在汇文培训老师给讲了这个例子。讲的挺好的

Qt编写聊天服务器与客户端主要用到下面两个类:

  • QTcpSocket --- 处理连接的
  • QTcpServer --- 处理服务器,对接入进行响应,创建每个链接的QTcpSocket实例

编写网络程序需要在 .pro 文件中加上 network,如下

QT       += core gui network

1.客户端的编写

客户端需要做的事:

  • 获取服务器的主机ip和端口(port)
  • 链接主机(connectToHost)
  • 链接状态下等待一些信号(signal)的产生并作出相应的回应(slot)

主要等待的信号由一下几个:

void connected()
void disconnected()
void error(QAbstractSocket::SocketError socketError)
void readyRead()

并为这几个信号写相应的槽函数对该情况作出处理

以下为我的具体实现代码:

我的ui界面是这样的:

对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)

 #ifndef TCPCLIENT_H
#define TCPCLIENT_H #include <QWidget>
#include <QMessageBox>
#include <QTcpSocket>
#include <QDebug>
#include <QHostAddress>
#include <QTextStream> namespace Ui {
class TcpClient;
} class TcpClient : public QWidget
{
Q_OBJECT public:
explicit TcpClient(QWidget *parent = );
~TcpClient(); private slots:
void on_connectPushButton_clicked(); //connect按键槽函数 void slotConnect();
void slotErr(QAbstractSocket::SocketError err);
void slotDisconnect();
void slotReadData(); void on_sendPushButton_clicked(); //send按键槽函数
void on_sendLineEdit_returnPressed(); //sendLineEdit的回车槽 private:
Ui::TcpClient *ui;
QTcpSocket *socket; //客户端的文件描述符
bool isConnect; //connect按键的状态变换值
}; #endif // TCPCLIENT_H

tcpclient.h

 #include "tcpclient.h"
#include "ui_tcpclient.h" TcpClient::TcpClient(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpClient)
{
ui->setupUi(this);
isConnect = true;
} TcpClient::~TcpClient()
{
delete ui;
} void TcpClient::on_connectPushButton_clicked()
{
QString hostip;
quint16 port;
if(isConnect)
{
/*判断ip和端口是否填写,并获取该ip和端口*/
if(ui->ipLineEdit->text().isEmpty())
{
QMessageBox::information(this,"ip","The ip is empty,please input a ip");
return ;
}
hostip = ui->ipLineEdit->text();
if(ui->portLineEdit->text().isEmpty())
{
QMessageBox::information(this,"port","The port is empty,please input a port");
return ;
}
port = ui->portLineEdit->text().toShort(); /*连接服务器,并在这之前连接几个必要的信号*/
socket = new QTcpSocket;
//已连接信号
connect(socket,SIGNAL(connected()),this,SLOT(slotConnect()));
//连接出现错误信号
connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(slotErr(QAbstractSocket::SocketError)));
//连接断开信号
connect(socket,SIGNAL(disconnected()),this,SLOT(slotDisconnect()));
//准备读取数据信号
connect(socket,SIGNAL(readyRead()),this,SLOT(slotReadData()));
socket->connectToHost(QHostAddress(hostip),port);
}
else
{
socket->disconnectFromHost();
ui->connectPushButton->setText("connect");
isConnect = true;
}
} //槽函数定义
void TcpClient::slotConnect()
{
QMessageBox::information(this,"connect","host connect sucess");
ui->ipLineEdit->setEnabled(false);
ui->portLineEdit->setEnabled(false);
ui->connectPushButton->setText("disconnect");
isConnect = false;
} void TcpClient::slotErr(QAbstractSocket::SocketError err)
{
qDebug("error is %d",err);
ui->connectPushButton->setText("connect");
} void TcpClient::slotDisconnect()
{
QMessageBox::information(this,"disconnect","host disconnect sucess");
ui->ipLineEdit->setEnabled(true);
ui->portLineEdit->setEnabled(true);
ui->connectPushButton->setText("connect");
isConnect = true;
} void TcpClient::slotReadData()
{
qDebug()<<"have data";
if(socket->bytesAvailable()>)
{
QString msg;
QByteArray ba;
ba.resize(socket->bytesAvailable());
socket->read(ba.data(),ba.size());
msg = QString(ba); // QTextStream out(socket);
// QString msg;
// out >> msg;
ui->msgListWidget->addItem(msg);
}
} void TcpClient::on_sendPushButton_clicked()
{
QTextStream in(socket);
in << ui->sendLineEdit->text();
ui->sendLineEdit->clear();
} void TcpClient::on_sendLineEdit_returnPressed()
{
QTextStream in(socket);
in << ui->sendLineEdit->text();
ui->sendLineEdit->clear();
}

tcpcilent.cpp

#include "tcpclient.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TcpClient w;
w.show(); return a.exec();
}

main.cpp

总结:客户端的编写比较容易,只要连接服务器,发送信息给服务器就可以


2.服务器的编写

服务器需要做的事:

  • Qt对于服务器有专门的类 QTcpServer封装,所以不需要去自己配置服务器(socket,bind),可以直接侦听(listen)
  • 当有客户端连接的时候分配文件描述符和地址标示客户端
  • 然后等待客户端发送信息(这里会得到一些信号,通过信号来处理),接收信号,转发(发送给链接在服务器的其他人)或者回馈信息。
  • 服务器需要支持多客户端,所以要有保存和删除客户端信息的处理。

因为需要实现支持多客户端接入的服务器,所以需要将显示层(ui),服务器层,socket层分开。可以看下面这个图来理解

对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)

简述下这里的流程,首先,客户端发送请求,服务器分配(创建)一个socket,然后incomingConnection()函数捕捉到这个socket,并在此响应客户端(接收消息,转发消息)。然后在这里创建一个信息转发消息到ui界面

通过这样的模型,我们就可以创建一个多客户端的服务器。

接下来的一张图我来解释下我的程序中信号与槽函数之间连接的关系(人类总是对视觉的感官来的更直接点)

对Qt下对话服务器客户端的总结(MyTcpServer与MyTcpClient)

结合上面的图和我的代码就很清晰的能看出信号与槽在这三层之间的传递关系

(1)由MyTcpSocket socket产生两个信号,readyread()和disconnected(),然后通过connect连接到两个槽函数readDataSlot()与disconnectSlot()

(2)在上面的两个槽函数中发送emit两个信号sockReadDataSignal(qintptr,QString),disconnectSignal(qintptr),附带信息的信号,使得MyTcpServer能够访问到信息

(3)在MyTcpServer类中通过socket接受这两个信号,并连接到相应的槽函数,把信息返回给其他客户端。

(4)并且我把信号sockReadDataSignal()连接到MyTcpServer内显示到ui界面

下面是的我的代码区

 #ifndef MYTCPSOCKET_H
#define MYTCPSOCKET_H #include <QObject>
#include <QTcpSocket>
#include <QTextStream>
#include <QHostAddress> class MyTcpSocket : public QTcpSocket
{
Q_OBJECT
public: MyTcpSocket();
~MyTcpSocket(); signals:
void disconnectSignal(qintptr);
void sockReadDataSignal(qintptr,QString); private slots:
void readDataSlot();
void disconnectSlot();
}; #endif // MYTCPSOCKET_H

mytcpsocket.h

 #include "mytcpsocket.h"

 MyTcpSocket::MyTcpSocket()
{
QObject::connect(this,SIGNAL(readyRead()),this,SLOT(readDataSlot()));
QObject::connect(this,SIGNAL(disconnected()),this,SLOT(disconnectSlot()));
} MyTcpSocket::~MyTcpSocket()
{ } void MyTcpSocket::readDataSlot()
{
//读取数据的两种方式
qDebug()<<"read data";
QString msg;
QByteArray ba;
ba.resize(this->bytesAvailable());
this->read(ba.data(),ba.size());
msg = QString(ba);
qDebug()<<"ip:"<<this->peerAddress().toString(); // QTextStream out(this),in(this);
// QString msg;
// out >> msg;
emit sockReadDataSignal(this->socketDescriptor(),msg);
} void MyTcpSocket::disconnectSlot()
{
qDebug()<<"disconnect";
emit disconnectSignal(this->socketDescriptor());
}

mytcpsocket.cpp

 #ifndef MYTCPSERVER_H
#define MYTCPSERVER_H #include <QObject>
#include <QHostAddress>
#include <QTcpServer>
#include <QDebug>
#include <QList>
#include "mytcpsocket.h" class MyTcpServer : public QTcpServer
{
Q_OBJECT
public:
MyTcpServer();
~MyTcpServer(); protected:
void incomingConnection(qintptr socketDescriptor); private slots:
void disconnectSlot(qintptr);
void answerMsgSlot(qintptr,QString); signals:
void serverReadDataSignal(qintptr,QString); private:
QList<MyTcpSocket *> clients; }; #endif // MYTCPSERVER_H

mytcpserver.h

 #include "mytcpserver.h"

 MyTcpServer::MyTcpServer()
{
listen(QHostAddress::Any,);
} MyTcpServer::~MyTcpServer()
{ } void MyTcpServer::incomingConnection(qintptr socketDescriptor)//when a new connection is available.
{
qDebug()<<"connect success";
MyTcpSocket *sock = new MyTcpSocket;
sock->setSocketDescriptor(socketDescriptor);
QObject::connect(sock,SIGNAL(disconnectSignal(qintptr)),this,SLOT(disconnectSlot(qintptr)));
QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SIGNAL(serverReadDataSignal(qintptr,QString)));
QObject::connect(sock,SIGNAL(sockReadDataSignal(qintptr,QString)),this,SLOT(answerMsgSlot(qintptr,QString)));
clients << sock;
} void MyTcpServer::disconnectSlot(qintptr sockfd)
{
// MyTcpSocket *sock = new MyTcpSocket;
// sock->setSocketDescriptor(socketDescriptor);
qDebug()<<"delete client "<<sockfd;
for (int i = ; i < clients.size(); ++i)
{
if (clients.at(i)->socketDescriptor() == sockfd)
clients.removeAt(i);
} // delete sock;
} void MyTcpServer::answerMsgSlot(qintptr sockfd, QString msg)
{
for (int i = ; i < clients.size(); ++i)
{
if(clients.at(i)->socketDescriptor() != sockfd)
{
QTextStream in(clients.at(i));
in << sockfd << ":" << msg << endl;
}
}
}

mytcpserver.cpp

 #ifndef MYTCPSERVERWIDGET_H
#define MYTCPSERVERWIDGET_H #include <QWidget>
#include "mytcpserver.h"
#include <QObject> namespace Ui {
class MyTcpServerWidget;
} class MyTcpServerWidget : public QWidget
{
Q_OBJECT public:
explicit MyTcpServerWidget(QWidget *parent = );
~MyTcpServerWidget(); private slots:
void widgetReadDataSlot(qintptr,QString); private:
Ui::MyTcpServerWidget *ui;
MyTcpServer *tcpserver;
}; #endif // MYTCPSERVERWIDGET_H

mytcpserverwidget.h

 #include "mytcpserverwidget.h"
#include "ui_mytcpserverwidget.h" MyTcpServerWidget::MyTcpServerWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyTcpServerWidget)
{
ui->setupUi(this);
tcpserver = new MyTcpServer; //connect to the server
QObject::connect(tcpserver,SIGNAL(serverReadDataSignal(qintptr,QString)),this,SLOT(widgetReadDataSlot(qintptr,QString))); } MyTcpServerWidget::~MyTcpServerWidget()
{
delete ui;
} void MyTcpServerWidget::widgetReadDataSlot(qintptr sock, QString msg)
{
QString id;
id = QString::number(sock);
ui->listWidget->addItem(id+":"+msg);
}

mytcpserverwidget.cpp

 #include "mytcpserverwidget.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyTcpServerWidget w;
w.show(); return a.exec();
}

main.cpp

视频教程在汇文的网校上也是有的:www.huiwen.com

最后附带一个写的很好的blog

Qt浅谈之十六:TCP和UDP(之一)

上一篇:Cesium原理篇:3最长的一帧之地形(3:STK)


下一篇:Cesium原理篇:3最长的一帧之地形(1)