Ubuntu上Qt+Tcp網絡編程之簡單聊天對話框


首先看一下實現結果:

  >>功能

     (1)服務器和客戶端之間進行聊天通信;

     (2)一個服務器可同時給多個客戶端發送消息;(全部連接時)

             也可以只給特定的客戶端發送消息;(連接特定IP)

     (3)可發送任意字符,包括中文;(原來參考的程序不能發中文)

 

  >>后續拓展

     (1)不同客戶端之間能否進行通信?

     (2)發完消息之后如何清空發送區?

     (3)如何完善登錄注冊功能?

     (4)如何更換界面背景及顏色?

      …………

 

 程序:

注意:首先需要在每個工程.pro文件里加上一句

QT       += network

1.mytcpclient.h

#ifndef MYTCPCLIENT_H
#define MYTCPCLIENT_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>

namespace Ui {
class MyTcpClient;
}

class MyTcpClient : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MyTcpClient *ui;
    QTcpSocket *tcpClient;

private slots:
    //客戶端槽函數
    void ReadData();
    void ReadError(QAbstractSocket::SocketError);

    void on_btnConnect_clicked();
    void on_btnSend_clicked();
    void on_btnClear_clicked();
};

#endif // MYTCPCLIENT_H
mytcpclient.h

2.mytcpclient.cpp

#include "mytcpclient.h"
#include "ui_mytcpclient.h"

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

    //初始化TCP客戶端
    tcpClient = new QTcpSocket(this);   //實例化tcpClient
    tcpClient->abort();                 //取消原有連接
    ui->btnConnect->setEnabled(true);
    ui->btnSend->setEnabled(false);

    connect(tcpClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
    connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), \
            this, SLOT(ReadError(QAbstractSocket::SocketError)));
}

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

void MyTcpClient::ReadData()
{
    //QByteArray buffer = tcpClient->readAll();   //by me
    QByteArray buffer;  //add by me
    buffer.resize(tcpClient->bytesAvailable());//add by me
    tcpClient->read(buffer.data(),buffer.size());//add by me
    if(!buffer.isEmpty())
    {
        QString msg = QString::fromLocal8Bit(buffer.data());//add by me
        ui->edtRecv->append(msg);   //buffer  ->   msg  ;   by me
    }
}

void MyTcpClient::ReadError(QAbstractSocket::SocketError)
{
    tcpClient->disconnectFromHost();
    ui->btnConnect->setText(tr("連接"));
    QMessageBox msgBox;
    msgBox.setText(tr("failed to connect server because %1").arg(tcpClient->errorString()));
    msgBox.exec();
}

void MyTcpClient::on_btnConnect_clicked()
{
    if(ui->btnConnect->text()=="連接")
    {
        tcpClient->connectToHost(ui->edtIP->text(), ui->edtPort->text().toInt());
        if (tcpClient->waitForConnected(1000))  // 連接成功則進入if{}
        {
            ui->btnConnect->setText("斷開");
            ui->btnSend->setEnabled(true);
        }
    }
    else
    {
        tcpClient->disconnectFromHost();
        if (tcpClient->state() == QAbstractSocket::UnconnectedState \
                || tcpClient->waitForDisconnected(1000))  //已斷開連接則進入if{}
        {
            ui->btnConnect->setText("連接");
            ui->btnSend->setEnabled(false);
        }
    }
}

void MyTcpClient::on_btnSend_clicked()
{
    QString data = ui->edtSend->toPlainText();
    QByteArray text = data.toLocal8Bit();        //add by me
    if(data != "")
    {
        //tcpClient->write(data.toLatin1()); //qt5出去了.toAscii()     //by me
        tcpClient->write(text,text.length());         //add by me
    }
}

void MyTcpClient::on_btnClear_clicked()
{
    ui->edtRecv->clear();
}
mytcpclient.cpp

3.mytcpserver.h

#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QMessageBox>
namespace Ui {
class MyTcpServer;
}

class MyTcpServer : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MyTcpServer *ui;
    QTcpServer *tcpServer;
    QList<QTcpSocket*> tcpClient;
    QTcpSocket *currentClient;

private slots:
    void NewConnectionSlot();
    void disconnectedSlot();
    void ReadData();

    void on_btnConnect_clicked();
    void on_btnSend_clicked();
    void on_btnClear_clicked();
};

#endif // MYTCPSERVER_H
mytcpserver.h

4.mytcpserver.cpp

#include "mytcpserver.h"
#include "ui_mytcpserver.h"

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

    tcpServer = new QTcpServer(this);
    ui->edtIP->setText(QNetworkInterface().allAddresses().at(1).toString());   //獲取本地IP
    ui->btnConnect->setEnabled(true);
    ui->btnSend->setEnabled(false);

    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(NewConnectionSlot()));
}

MyTcpServer::~MyTcpServer()
{
    delete ui;
}
// newConnection -> newConnectionSlot 新連接建立的槽函數
void MyTcpServer::NewConnectionSlot()
{
    currentClient = tcpServer->nextPendingConnection();
    tcpClient.append(currentClient);
    ui->cbxConnection->addItem(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1])\
                                          .arg(currentClient->peerPort()));
    connect(currentClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
    connect(currentClient, SIGNAL(disconnected()), this, SLOT(disconnectedSlot()));
}

// 客戶端數據可讀信號,對應的讀數據槽函數
void MyTcpServer::ReadData()
{
    // 由於readyRead信號並未提供SocketDecriptor,所以需要遍歷所有客戶端
    for(int i=0; i<tcpClient.length(); i++)
    {
        QByteArray buffer;  //add by me
        buffer.resize(tcpClient[i]->bytesAvailable());//add by me
        tcpClient[i]->read(buffer.data(),buffer.size());//add by me
        //QByteArray buffer = tcpClient[i]->readAll();  //by me
        if(buffer.isEmpty())    continue;

        static QString IP_Port, IP_Port_Pre;
        IP_Port = tr("[%1:%2]:").arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1])\
                                     .arg(tcpClient[i]->peerPort());

        // 若此次消息的地址與上次不同,則需顯示此次消息的客戶端地址
        if(IP_Port != IP_Port_Pre)
            ui->edtRecv->append(IP_Port);

        QString msg = QString::fromLocal8Bit(buffer.data());//add by me
        ui->edtRecv->append(msg);  //buffer ->  msg ; by me

        //更新ip_port
        IP_Port_Pre = IP_Port;
    }
}
// disconnected -> disconnectedSlot 客戶端斷開連接的槽函數
void MyTcpServer::disconnectedSlot()
{
    //由於disconnected信號並未提供SocketDescriptor,所以需要遍歷尋找
    for(int i=0; i<tcpClient.length(); i++)
    {
        if(tcpClient[i]->state() == QAbstractSocket::UnconnectedState)
        {
            // 刪除存儲在combox中的客戶端信息
            ui->cbxConnection->removeItem(ui->cbxConnection->findText(tr("%1:%2")\
                                  .arg(tcpClient[i]->peerAddress().toString().split("::ffff:")[1])\
                                  .arg(tcpClient[i]->peerPort())));
            // 刪除存儲在tcpClient列表中的客戶端信息
             tcpClient[i]->destroyed();
             tcpClient.removeAt(i);
        }
    }
}
// 監聽--斷開
void MyTcpServer::on_btnConnect_clicked()
{
    if(ui->btnConnect->text()=="監聽")
    {
        bool ok = tcpServer->listen(QHostAddress::Any, ui->edtPort->text().toInt());
        if(ok)
        {
            ui->btnConnect->setText("斷開");
            ui->btnSend->setEnabled(true);
        }
    }
    else
    {
        for(int i=0; i<tcpClient.length(); i++)//斷開所有連接
        {
            tcpClient[i]->disconnectFromHost();
            bool ok = tcpClient[i]->waitForDisconnected(1000);
            if(!ok)
            {
                // 處理異常
            }
            tcpClient.removeAt(i);  //從保存的客戶端列表中取去除
        }
        tcpServer->close();     //不再監聽端口
        ui->btnConnect->setText("監聽");
        ui->btnSend->setEnabled(false);
    }
}
// 發送數據
void MyTcpServer::on_btnSend_clicked()
{
    QString data = ui->edtSend->toPlainText();
    QByteArray text = data.toLocal8Bit();    //add by me
    if(data == "")  return;    // 文本輸入框為空時
    //全部連接
    if(ui->cbxConnection->currentIndex() == 0)
    {
        for(int i=0; i<tcpClient.length(); i++)
            //tcpClient[i]->write(data.toLatin1()); //qt5除去了.toAscii()    //by me
            tcpClient[i]->write(text,text.length());         //add by me
    }
    //指定連接
    else
    {
        QString clientIP = ui->cbxConnection->currentText().split(":")[0];
        int clientPort = ui->cbxConnection->currentText().split(":")[1].toInt();
//        qDebug() << clientIP;
//        qDebug() << clientPort;
        for(int i=0; i<tcpClient.length(); i++)
        {
            if(tcpClient[i]->peerAddress().toString().split("::ffff:")[1]==clientIP\
                            && tcpClient[i]->peerPort()==clientPort)
            {
                //tcpClient[i]->write(data.toLatin1());    //by me
                tcpClient[i]->write(text,text.length());   //add by me
                return; //ip:port唯一,無需繼續檢索
            }
        }
    }
}

void MyTcpServer::on_btnClear_clicked()
{
    ui->edtRecv->clear();
}
mytcpserver.cpp

5.mytcpclient.ui

6.mytcpserver.ui

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM