利用QT写一个简单的TCP服务器和客户端进行聊天

TCP与UDP基本区别
  1.基于连接与无连接
  2.TCP要求系统资源较多,UDP较少; 
  3.UDP程序结构较简单 
  4.流模式(TCP)与数据报模式(UDP); 
  5.TCP保证数据正确性,UDP可能丢包 
  6.TCP保证数据顺序,UDP不保证 


《案例》网络聊天室
1 服务器
1)使用QTcpServer创建并发服务器
2)保存所有客户端socket套接字
3)接收客户端消息(read)
4)转发消息给所有的客户端(write)

2 客户端:
1)QTcpSocket建立基于TCP的客户端
2)连接到指定的服务器(IP/端口)
3)发送消息到服务器(write)
4)接收服务器返回聊天信息并显示

 

服务器端

ui界面布局

1.添加pro文件  因为用到了网络传输,所以需要添加network模块

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

2.添加头文件

#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>

3.添加变量函数声明

private slots:
    //创建聊天室对应对应槽函数
    void on_createButton_clicked();
    //当客户端和服务器建立链接发送信号newConnection
    void onNewConnection();
    //连接readyRead信号
    void slotDataReceived();
private:
    //转发消息给所有的客户端
    void sendMessage(const QByteArray&);
private:
    Ui::ServerDialog *ui;
private:
    QTcpServer tcpServer;//服务器对象
    qint16 port;//服务器端口
    //保存所有和客户端通信的套接字
    QList <QTcpSocket*> tcpClientList;

4.添加函数代码

ServerDialog::ServerDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ServerDialog)
{
    ui->setupUi(this);
    connect(&tcpServer,SIGNAL(newConnection()),
            this,SLOT(onNewConnection()));
}
ServerDialog::~ServerDialog()
{
    delete ui;
}
//创建TCP服务器
void ServerDialog::on_createButton_clicked()
{
    port = ui->portEdit->text().toShort();
    //开启服务器
    if(tcpServer.listen(QHostAddress::Any,port)){
        qDebug("TCP服务器创建成功");
    }
    else{
         qDebug("TCP服务器创建失败");
    }
    //禁用创建按钮和端口输入
    ui->createButton->setEnabled(false);
    ui->portEdit->setEnabled(false);
}
//当客户端和服务器建立链接发送信号newConnection
void ServerDialog::onNewConnection()
{
    //获取和客户端通信的套接字
    QTcpSocket* tcpClientSocket =
            tcpServer.nextPendingConnection();
    //保存套接字到容器
    tcpClientList.append(tcpClientSocket);

    //收到数据时,将会发送readyRead
    connect(tcpClientSocket,SIGNAL(readyRead()),
                this,SLOT(slotDataReceived()));
}
//连接readyRead信号
void ServerDialog::slotDataReceived()
{
    //遍历所有的客户端
    for(int i=0; i<tcpClientList.count(); i++){
        //检查当前客户端是否有数据到来
        if(tcpClientList.at(i)->bytesAvailable()){
            //接收客户端发送来的消息
            QByteArray readbuf =
                tcpClientList.at(i)->readAll();
            //显示到服务器Ui
            ui->listWidget->addItem(readbuf);
            //转发消息给所有的客户端
            sendMessage(readbuf);
        }
    }
}
//转发消息给所有的客户端
void ServerDialog::sendMessage(const QByteArray& msg)
{
    for(int i=0;i<tcpClientList.count();i++){
        tcpClientList.at(i)->write(msg);
    }
}

客户端

ui界面布局

1.同样pro文件需要添加network模块

2.添加头文件

#include <QDialog>
#include <QHostAddress>
#include <QTcpSocket>
#include <QMessageBox>

3.添加变量函数声明

private slots:
    //发送消息,按钮的槽函数
    void on_sendButton_clicked();
    //连接服务器,按钮的槽函数
    void on_connectButton_clicked();

    //和服务器连接时调用的槽函数
    void onConnected(void);
    //和服务器断开连接调用的槽函数
    void onDisconnected(void);
    //接收聊天消息的槽函数
    void slotDataReceived(void);
    //网络异常处理的槽函数
    void slotSockError(QAbstractSocket::SocketError);

private:
    Ui::ClientDialog *ui;
    bool status;//标记客户端的连接状态,true在线,false离线
    QTcpSocket tcpSocket;//和服务器通信的套接字
    qint16 port;//服务器通信的端口
    QHostAddress serverIP;//服务器通信的IP地址
    QString userName;//聊天室的昵称

4.添加函数代码

ClientDialog::ClientDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ClientDialog)
{
    ui->setupUi(this);
    status = false;//初始化连接状态
    //和服务器连接时发送connected信号
    connect(&tcpSocket,SIGNAL(connected()),
            this,SLOT(onConnected()));
    //断开连接发送disconnected信号
    connect(&tcpSocket,SIGNAL(disconnected()),
            this,SLOT(onDisconnected()));
    //当服务器发送数据过来时发送readyRead信号
    connect(&tcpSocket,SIGNAL(readyRead()),
            this,SLOT(slotDataReceived()));
    //网络异常,发送error
    connect(&tcpSocket,
        SIGNAL(error(QAbstractSocket::SocketError)),
        this,
        SLOT(slotSockError(QAbstractSocket::SocketError)));
}

ClientDialog::~ClientDialog()
{
    delete ui;
}

void ClientDialog::on_sendButton_clicked()
{
    //获取用户输入的消息
    if(ui->sendEdit->text() == ""){
        return;
    }
    QString msg = userName + ":" + ui->sendEdit->text();
    //发送消息到服务器
    tcpSocket.write(msg.toLocal8Bit());
    //清空消息
    ui->sendEdit->clear();
}
void ClientDialog::on_connectButton_clicked()
{
    if(status == false){//当前没有连接,建立连接
        //从界面获取IP、用户名、PORT
        QString ip = ui->serverIpEdit->text();
        if(serverIP.setAddress(ip) == false){
            QMessageBox::information(
                        this,"错误","请输入正确的IP地址");
            return;
        }
        if(ui->userNameEdit->text() == ""){
            QMessageBox::information(
                        this,"错误","请输入聊天室昵称");
            return;
        }
        userName = ui->userNameEdit->text();

        if(ui->portEdit->text() == ""){
            QMessageBox::information(
                        this,"错误","请输入正确的端口");
            return;
        }
        port = ui->portEdit->text().toShort();
        //连接服务器
        tcpSocket.connectToHost(serverIP,port);
        status = true;//标记为在线状态
    }
    else{
        //发送下线消息
        QString msg = userName+":离开聊天室";
        tcpSocket.write(msg.toLocal8Bit());
        //关闭和服务器的连接
        tcpSocket.disconnectFromHost();
        status = false;//标记为离线状态
    }
}
//和服务器连接时调用的槽函数
void ClientDialog::onConnected(void)
{
    //使能发送消息按钮
    ui->sendButton->setEnabled(true);
    //连接服务器 按钮文本改为 离开服务器
    ui->connectButton->setText(tr("离开服务器"));
    //禁用:ip port 用户名输入
    ui->serverIpEdit->setEnabled(false);
    ui->portEdit->setEnabled(false);
    ui->userNameEdit->setEnabled(false);
    //向服务器发送一条进入聊天室消息
    QString msg = userName+":进入聊天室";
//    tcpSocket.write(msg.toLocal8Bit());
    tcpSocket.write(msg.toLatin1());
}

//和服务器断开连接调用的槽函数
void ClientDialog::onDisconnected(void)
{
    //禁用发送消息按钮
    ui->sendButton->setEnabled(true);
    //使能ip port 用户 输入
    ui->serverIpEdit->setEnabled(true);
    ui->portEdit->setEnabled(true);
    ui->userNameEdit->setEnabled(true);

    ui->connectButton->setText("连接服务器");
}

//接收聊天消息的槽函数
void ClientDialog::slotDataReceived(void)
{
    if(tcpSocket.bytesAvailable()){
        QByteArray buf;
        buf.resize(tcpSocket.bytesAvailable());
        tcpSocket.read(buf.data(),buf.size());
        //显示消息到界面
        QString msg = buf.data();
        ui->listWidget->addItem(msg);
    }
}
//网络异常处理的槽函数
void ClientDialog::slotSockError(
        QAbstractSocket::SocketError){
    QMessageBox::critical(this,"网络异常",
                          tcpSocket.errorString());
}

运行演示

可能有点乱码,后续再优化

 

Logo

致力于链接即构和开发者,提供实时互动和元宇宙领域的前沿洞察、技术分享和丰富的开发者活动,共建实时互动世界。

更多推荐