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

 

posted @ 2018-05-30 17:36  愣娃娃  Views(1761)  Comments(0Edit  收藏  举报