Qt--a chat program based on TCP client and server (half-duplex communication)

Task: To implement a TCP-based chat program, the classes that need to be used are:

  • QTcpServer

Server management class: manages multiple connections of the server, directly inherits QObject, so it does not have IO capabilities.

The relevant functions are as follows:

// 构造函数
QTcpServer::QTcpServer(QObject * parent = 0)// 服务器开启监听,等待客户主动发起连接
// 参数1:监听来自于哪个IP地址的请求,默认值为不限制IP地址,QHostAddress类是IP地址的封装类。
// 参数2:服务器端口号
// 返回值:监听开启结果
bool QTcpServer::listen(
                const QHostAddress & address = QHostAddress::Any, 
                quint16 port = 0)
// 有新连接建立的通知信号
void QTcpServer::newConnection() [signal]// 服务器是否还在监听
bool QTcpServer::isListening() const// 关闭服务器
void QTcpServer::close()
// 返回一个就绪的连接对象,此对象用于跟某个客户端进行IO操作
QTcpSocket * QTcpServer::nextPendingConnection() [virtual]
  • QTcpSocket

TCP connection class: perform network IO operations, and indirectly inherit the QIODevice class.

The relevant functions are as follows:

// 构造函数
QTcpSocket::QTcpSocket(QObject * parent = 0)
// 连接到服务器
// 参数1:服务器的IP地址
// 参数2:服务器的端口号
// 参数3:读写模式,默认为可读可写
void QAbstractSocket::connectToHost(const QString & hostName,
                                    quint16 port, 
                                    OpenMode openMode = ReadWrite) [virtual]
// 连接是否处于打开状态
bool QIODevice::isOpen() const
// 关闭连接
void QIODevice::close() [virtual]
// 拿到对面的IP地址封装类对象,如果没有连接返回QHostAddress::Null
QHostAddress QAbstractSocket::peerAddress() const
// 连接断开发射的信号
void QAbstractSocket::disconnected() [signal]
  • QTextStream 

Text stream class: It is an auxiliary class for efficient text data IO.

Server: server (QTcpServer)

step:

① Create a QTcpServer object

②The parameters required to monitor the list are address and port number

③When a new client connects successfully, send a newConnect signal back

④In the newConnection signal slot function, call the nextPendingConnection function to obtain the new connection QTcpSocket object

⑤ Connect the readRead signal of the QTcpSocket object

⑥ Use read to receive data in the slot function of the readRead signal

⑦Call the write member function to send data

code:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include<QMessageBox>
//网络相关类
#include<QTcpServer>
#include<QTcpSocket>
#include<QTextStream>
#include<QDateTime>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    QTcpServer *server;//服务器对象
    QTcpSocket *socket=NULL;
private slots:
    void newConnSlot();//新连接的槽函数
    void disconnectedSlot(); //网络连接断开的槽函数
    void readReadSlot(); //读取客户端信息的槽函数
    void btnSendClickedSlot();//发送给客户端消息的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    //设置窗口标记为顶层
    setWindowFlags(Qt::WindowStaysOnTopHint);

    //创建管理类对象
    server=new QTcpServer(this);

    //连接服务器通知的信号槽
    connect(server,SIGNAL(newConnection()),this,SLOT(newConnSlot()));
    //向客户端发送信息的信号槽
    connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickedSlot()));

    // 服务器开启监听,等待客户主动发起连接
    // 参数1:监听来自哪个IP地址的请求,默认值为不限制IP地址,QHostAddress类是IP地址的封装类。
    // 参数2:服务器端口号
    server->listen(QHostAddress::Any,8887);
}

Dialog::~Dialog()
{
    if(server->isListening())//如果在监听
          server->close();//关闭服务器
    delete ui;
}

void Dialog::newConnSlot()
{
    //踢掉之前的连接
    if(socket!=NULL)
    {
        socket->close();
    }
    //拿到与客户端进行连接的QTcpSocket对象
    socket=server->nextPendingConnection();
    //建立断链通知的信号槽
    connect(socket,SIGNAL(disconnected()), this,SLOT(disconnectedSlot()));
    //建立读取消息的信号槽
    connect(socket,SIGNAL(readyRead()),this,SLOT(readReadSlot()));
    //拿到客户端的ip和端口号
    QString ip=socket->peerAddress().toString();
    quint16 port=socket->peerPort();
    QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("新连接来了!");
    ui->textBrowser->append("");
    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");
}

void Dialog::disconnectedSlot()
{
    //拿到客户端的ip和端口号
    QString ip=socket->peerAddress().toString();
    quint16 port=socket->peerPort();
    QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("连接结束了!");
    ui->textBrowser->append("");

    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");
}


void Dialog::readReadSlot()  //读取客户端信息
{
  QTextStream input(socket);
  QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
  //读取数据
  QString msg=input.readAll();
  //展示内容
  ui->textBrowser->append(time);
  ui->textBrowser->append(msg);
}

void Dialog::btnSendClickedSlot()  //向客户端发送消息
{
    //获取用户输入的内容
   QString info=ui->lineEdit->text();
   if(info=="")
   {
      QMessageBox::warning(this,"提示","请输入发送的内容");
      return;
   }
   //创建文本流对象
   QTextStream output(socket);
   //发送内容
   output<<info;
   ui->lineEdit->clear();
}

Client: client (QTcpSocket)

step:


① Create a QTcpSocket object

②When the object is successfully connected to the Server, it will send a connected signal

③Call the member function connectToHost to connect to the server, the required parameters are address and port number

④The slot function of the connected signal starts sending data

⑤ Use write to send data, read to receive data

code:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include<QMessageBox>
#include<QDateTime>
//连接类
#include<QTcpSocket>
#include<QTextStream>
namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    QTcpSocket *client;//连接对象
    QTcpSocket *socket=NULL;
private slots:
    void btnConnClickedSlot();//连接按钮的槽函数
    void btnSendClickedSlot();//发送按钮的槽函数
    void connectedSlot();  //连接的槽函数
    void disconnectedSlot(); //断开连接的槽函数
    void readInfoSlot();//接收信息的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButtonConn,SIGNAL(clicked()),this,SLOT(btnConnClickedSlot()));
    connect(ui->pushButtonSend,SIGNAL(clicked()), this,SLOT(btnSendClickedSlot()));
    //设置窗口标记为顶层
    setWindowFlags(Qt::WindowStaysOnTopHint);
    //创建连接对象
    client=new QTcpSocket(this);
    //连接状态检测的信号槽
    connect(client,SIGNAL(connected()),this,SLOT(connectedSlot()));
    connect(client,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));
    //读取服务器发来消息的信号槽
    connect(client,SIGNAL(readyRead()),this,SLOT(readInfoSlot()));
}

Dialog::~Dialog()
{
    if(client->isOpen())//如果连接处于打开状态
          client->close();//关闭连接
    delete ui;
}

void Dialog::btnConnClickedSlot()
{
    // 默认输入有效,连接到服务器
    // 参数1:服务器的IP地址
    // 参数2:服务器的端口号
    client->connectToHost(ui->lineEditIp->text(),8887);
}

void Dialog::btnSendClickedSlot()
{
   //获取用户输入的内容
    QString msg=ui->lineEditInfo->text();
    if(msg=="")
    {
        QMessageBox::warning(this,"提示","请输入要发送的内容!");
        return;
    }
    //创建文本流对象
    QTextStream output(client);
    //发送内容
    output<<msg;
    //清空输入框
    ui->lineEditInfo->clear();
}

void Dialog::connectedSlot()
{
    //屏蔽连接按钮
    ui->pushButtonConn->setEnabled(false);
    ui->pushButtonConn->setText("已连接");
    //释放发送按钮
    ui->pushButtonSend->setEnabled(true);
}
void Dialog::disconnectedSlot()
{
    //恢复连接按钮
    ui->pushButtonConn->setEnabled(true);
    ui->pushButtonConn->setText("连接");
    //屏蔽发送按钮
    ui->pushButtonSend->setEnabled(false);
}
void Dialog::readInfoSlot()
{
    QTextStream input(client);
    QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
    //读取数据
    QString info=input.readAll();
    ui->textBrowser->append(time);
    ui->textBrowser->append(info);
}

dialog.ui

 running result:

client

 

server 

Guess you like

Origin blog.csdn.net/m0_68672255/article/details/130637304