【QT】使用TCP实现服务器端越客户端通信
1,窗口设计
为了便于代码部分阅读,现将窗口各部分objectName名称展示如下
1.1 服务器端窗口设计
[1],listWidget
[2], portEdit
[3],createButton
1.2 客户端窗口设计
[1], listWidget
[2], messageEdit
[3], sendButton
[4], serveripEdit
[5], serverPortEdit
[6], usernameEdit
[7], connectButton
2, 实现效果
3,实现原理
3.1, 服务端主要使用QTcpSever
和QTcpSocket
类,QTcpSever
用于创建TCP服务,QTcpSocket
用于控制建立的socket连接。
3.1.1 创建TCPserver对象
3.1.2 使用listen()
监听IP端口
3.1.3 当有新的连接时,会发出newConnection
信号,触发槽函数接受并产生连接的套接字QTcpSocket
3.1.4 使用readyRead信号
表示Socket缓存接收到新的数据
3.2 客户端只使用 QTcpSocket
类
3.2.1 创建QTcpSocket对象
3.2.2 使用connectToHost()
连接服务器IP和端口号
3.2.3 绑定connected
和disconnected
信号
3.2.4 发送数据给服务端
4,具体代码部分
4.1 服务器端
4.1.1 头文件部分.h,主要一些声明,不做过多解释
#ifndef SERVERDIALOG_H #define SERVERDIALOG_H #include <QDialog> #include <QTcpServer> #include <QTcpSocket> #include <QDebug> #include <QTimer> namespace Ui { class ServerDialog; } class ServerDialog : public QDialog { Q_OBJECT public: explicit ServerDialog(QWidget *parent = 0); ~ServerDialog(); private slots: void on_createButton_clicked(); void onNewConnection(); void onReadyRead(); void sendMessage(const QByteArray& buf); void onTimeout(void); private: Ui::ServerDialog *ui; QTcpServer tcpserver; quint16 port; QList<QTcpSocket*> tcpClientList; QTimer timer; }; #endif // SERVERDIALOG_H
4.1.2 具体.cpp部分实现#include "serverdialog.h"
#include "ui_serverdialog.h" ServerDialog::ServerDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ServerDialog) { ui->setupUi(this);
//创建TcpServer连接 connect(&tcpserver,&QTcpServer::newConnection,this,&ServerDialog::onNewConnection);
//处理超时连接 connect(&timer,&QTimer::timeout,this,&ServerDialog::onTimeout); } ServerDialog::~ServerDialog() { delete ui; }
//创建服务按钮槽函数实现 void ServerDialog::on_createButton_clicked() {
//获取端口
port = ui->portEdit->text().toShort();
//创建监听 if(tcpserver.listen(QHostAddress::Any,port)==true) { qDebug() << "create server success!"; //创建按钮置灰,端口编辑置灰 ui->createButton->setEnabled(false); ui->portEdit->setEnabled(false);
//设置超时时间 timer.start(3000); }else { qDebug() << "create server false!"; } }
//实现新连接槽函数 void ServerDialog::onNewConnection() {
//创建QTcpSocket连接套接字 QTcpSocket* tcpClient = tcpserver.nextPendingConnection();
//将套接字指针添加进链表中,实现多客户端访问 tcpClientList.append(tcpClient);
//读取客户端数据 connect(tcpClient,&QTcpSocket::readyRead,this,&ServerDialog::onReadyRead); }
//实现读取客户端数据 void ServerDialog::onReadyRead() {
//遍历所有客户端 for(int i=0;i<tcpClientList.size();i++) { if(tcpClientList.at(i)->bytesAvailable()) {
//读取并显示数据 QByteArray buf = tcpClientList.at(i)->readAll(); ui->listWidget->addItem(buf); ui->listWidget->scrollToBottom(); sendMessage(buf); } } }
//将信息分发给所有客户端 void ServerDialog::sendMessage(const QByteArray& buf) { for(int i=0;i<tcpClientList.size();i++) { tcpClientList.at(i)->write(buf); } }
//超时报错 void ServerDialog::onTimeout(void) { for(int i=0;i<tcpClientList.size();i++) { if(tcpClientList.at(i)->state() == QAbstractSocket::UnconnectedState) { tcpClientList.removeAt(i); --i; } } }
4.2 客户端
4.2.1 头文件 .h
#ifndef CLIENTDIALOG_H #define CLIENTDIALOG_H #include <QDialog> #include <QTcpSocket> #include <QHostAddress> #include <QMessageBox> #include <QDebug> namespace Ui { class ClientDialog; } class ClientDialog : public QDialog { Q_OBJECT public: explicit ClientDialog(QWidget *parent = 0); ~ClientDialog(); private slots: void on_sendButton_clicked(); void on_connectButton_clicked(); void onConnected(); void onDisconnected(); void onReadyRead(); void onError(); private: Ui::ClientDialog *ui; bool status; QTcpSocket tcpSocket; QHostAddress serverIP; quint16 serverPort; QString username; }; #endif // CLIENTDIALOG_H
4.2.1 具体.cpp实现#include "clientdialog.h"#include "ui_clientdialog.h"
ClientDialog::ClientDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ClientDialog) { ui->setupUi(this); status = false; connect(&tcpSocket,&QTcpSocket::connected,this,&ClientDialog::onConnected); connect(&tcpSocket,&QTcpSocket::disconnected,this,&ClientDialog::onDisconnected); connect(&tcpSocket,&QTcpSocket::readyRead,this,&ClientDialog::onReadyRead); connect(&tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this,&ClientDialog::onError); } ClientDialog::~ClientDialog() { delete ui; }
//发送信息按钮槽函数实现 void ClientDialog::on_sendButton_clicked() { QString msg = ui->messageEdit->text(); if(msg == "") { return; } msg = username + ":" + msg; tcpSocket.write(msg.toUtf8()); ui->messageEdit->clear(); }
//连接服务器端代码实现 void ClientDialog::on_connectButton_clicked() { if(status == false) {
serverIP.setAddress(ui->serveripEdit->text()); serverPort = ui->serverPortEdit->text().toShort(); username = ui->usernameEdit->text();
//连接地址与端口号,连接服务器 tcpSocket.connectToHost(serverIP,serverPort); } else { QString msg = username + "leave the talk room!"; tcpSocket.write(msg.toUtf8()); tcpSocket.disconnectFromHost(); } }
//实现连接槽函数 void ClientDialog::onConnected() { status = true; ui->sendButton->setEnabled(true); ui->serveripEdit->setEnabled(false); ui->serverPortEdit->setEnabled(false); ui->usernameEdit->setEnabled(false); ui->connectButton->setText("leave talk room"); QString msg = username + ":enter the talk room"; tcpSocket.write(msg.toUtf8()); } void ClientDialog::onDisconnected() { status = false; ui->sendButton->setEnabled(true); ui->serveripEdit->setEnabled(false); ui->serverPortEdit->setEnabled(false); ui->usernameEdit->setEnabled(false); ui->connectButton->setText("connect the server"); }
//读取数据内容 void ClientDialog::onReadyRead() { if(tcpSocket.bytesAvailable()) { QByteArray buf = tcpSocket.readAll(); ui->listWidget->addItem(buf); ui->listWidget->scrollToBottom(); } } //显示报错信息 void ClientDialog::onError() { QMessageBox::critical(this,"ERROR",tcpSocket.errorString()); }