[Qt] tcp服务器连接多个客户端的实现

[Qt]tcp服务器连接多个客户端的实现

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

要求:数据按字节接收,以1~255个字节循环发送
编译环境:Qt 5.9.5

客户端的实现:

代码如下:
TcpClient.h:
#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include 
#include 
#include 
#include 
using namespace std;


namespace Ui {
class TcpClient;
}

class TcpClient : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_pushButtonEnter_clicked();
    void slotconnectedsuccess();  //处理连接成功的信号
    void slotreceive();   //接收服务器传过来的信息
    void on_pushButtonSend_clicked();
    void slotdisconnected();  //处理离开聊天室的信号


private:
    Ui::TcpClient *ui;
    bool status;     //用来判断是否进入了服务器
    size_t strSize;
    int port;
    QHostAddress *serverIP;
    QString userName;
    QTcpSocket *tcpsocket;
};

#endif // TCPCLIENT_H

TcpClient.cpp:
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include 
#include 

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

    //将进入服务器的标志位置为false
    status = false;
    port = 8888;
    ui->lineEditServerPort->setText(QString::number(port));

    serverIP = new QHostAddress();

    //未连接内不能发送信息
    ui->pushButtonSend->setEnabled(false);
}

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

//进入服务器
void TcpClient::on_pushButtonEnter_clicked()
{
    //首先判断这个用户是不是在服务器中
    if(status == false) {  //不在服务器中就和服务器进行连接
        QString ip = ui->lineEditServerIp->text();
        if(!serverIP->setAddress(ip)) {   //判断IP地址是否可以被正确解析
            QMessageBox::warning(this, "错误", "IP地址不正确");
            return;
        }
        if(ui->lineEditUserName->text() == "") {
            QMessageBox::warning(this, "错误", "用户名不能为空");
            return;
        }
        userName = ui->lineEditUserName->text();

        tcpsocket = new QTcpSocket(this);
        tcpsocket->connectToHost(*serverIP, port);

        //和服务器连接成功能会触发connected信号
        connect(tcpsocket, &QTcpSocket::connected, this, &TcpClient::slotconnectedsuccess);

        //接收到服务器的信息就会触发readyRead信号
        connect(tcpsocket, &QTcpSocket::readyRead, this, &TcpClient::slotreceive);

        //将进入服务器的标志位置为true;
        status = true;
    }
    else {   //已经进入了服务器
        //int length = 0;
        QString msg = userName + ":disconnected";
        tcpsocket->write(msg.toUtf8().data());
        tcpsocket->disconnectFromHost();
        status = false;
        //离开服务器就会触发disconnected信号
        connect(tcpsocket, &QTcpSocket::disconnected, this, &TcpClient::slotdisconnected);
    }
}

//用来处理连接成功的信号
void TcpClient::slotconnectedsuccess()
{
    ui->textEdit->append("连接成功");

    //进入服务器可以发送信息了
    ui->pushButtonSend->setEnabled(true);
    //将连接服务器的按钮改为离开服务器
    ui->pushButtonEnter->setText("离开服务器");

    QString msg = userName + ": connected";
    tcpsocket->write(msg.toUtf8().data());
}


void TcpClient::slotreceive()
{
    QByteArray array = tcpsocket->readAll();

    ui->textEdit->append(array.data());
}

void TcpClient::on_pushButtonSend_clicked()
{
    if(ui->lineEditSend->text() == "") {
        return;
    }

    //获取编辑区内容
    QString str = ui->lineEditSend->text();

    //加头信息
    qint64 textSize = str.size();

    //userName = userName + ": ";

    QString strHead = QString("%1##%2##%3").arg(userName + ": ").arg(textSize).arg(str);

    tcpsocket->write(strHead.toUtf8().data());

    ui->lineEditSend->clear();
}

void TcpClient::slotdisconnected()
{
    ui->pushButtonSend->setEnabled(false);
    ui->pushButtonEnter->setText("连接服务器");
}

[Qt] tcp服务器连接多个客户端的实现_第1张图片

服务端的实现:

tcpserver.h:
#ifndef TCPSERVER_H
#define TCPSERVER_H

#include 
#include "server.h"

namespace Ui {
class TcpServer;
}

class TcpServer : public QWidget
{
    Q_OBJECT

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

protected slots:
    void slotupdateserver(QString, int);  //接收server发过来的信号就更新界面信息

private slots:
    void on_Button_waitConnect_clicked();

private:
    Ui::TcpServer *ui;

    int port;
    Server *server;

};

#endif // TCPSERVER_H

tcpserver.cpp:
#include "tcpserver.h"
#include "server.h"
#include "ui_tcpserver.h"
#include 

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

    port = 8888;
}

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


void TcpServer::on_Button_waitConnect_clicked()
{
    server  = new Server(this, port);

    connect(server, &Server::updateserver, this, &TcpServer::slotupdateserver);


    ui->textEdit->append("创建成功,可以连接");

    ui->Button_waitConnect->setEnabled(false);
}


void TcpServer::slotupdateserver(QString msg, int length)
{
    ui->textEdit->append(msg);
}

重写incomingconnected() , 监听
server.h
#ifndef SERVER_H
#define SERVER_H

#include 
#include 
#include 
#include "tcpclientsocket.h"


class Server : public QTcpServer
{
    Q_OBJECT
public:
    explicit Server(QObject *parent = 0, int port = 0);
    QList tcpclientsocketlist;

protected:
    virtual void incomingConnection(qintptr socketDescriptor);  //只要出现一个新的连接,就会自动调用这个函数

protected slots:
    void slotupdateserver(QString, int);    //用来处理tcpclient发过来的信号
    void slotclientdisconnect(int);

signals:
    void updateserver(QString, int);    //发送信号给界面, 让界面更新信息

};

#endif // SERVER_H

server.cpp:
#include "server.h"
#include "tcpclientsocket.h"
#include 
#include 

Server::Server(QObject *parent, int port) : QTcpServer(parent)
{
    listen(QHostAddress::Any, port);   //监听

}

void Server::incomingConnection(qintptr socketDescriptor)
{
    //只要有新连接就生成一个新的套接字
    TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);
    tcpclientsocket->setSocketDescriptor(socketDescriptor);

    //将新创建的套接字加入到客户端套接字列表中
    tcpclientsocketlist.append(tcpclientsocket);

    //接收到tcpclientsocket发过来的更新界面的信号
    connect(tcpclientsocket, &TcpClientSocket::updateserver, this, &Server::slotupdateserver);
    connect(tcpclientsocket, &TcpClientSocket::clientDisconnected, this, &Server::slotclientdisconnect);

    //return emit  QTcpServer::newConnection();
}

void Server::slotupdateserver(QString msg, int length)
{
    //将这个信号发送给界面
    emit updateserver(msg, length);

    QString userName = msg.section("##", 0, 0);
    QString text = msg.section("##", 1, 1);

    //将收到的信号发送给每个客户端,从套接字列表中找到需要接收的套接字
    for(int i = 0; i < tcpclientsocketlist.count(); i++) {
        QTcpSocket *item = tcpclientsocketlist.at(i);

        QString sendInfo = userName + " ";
        item->write(sendInfo.toUtf8().data());
        //处理数据,按格式发送
        QString str = text;
        string buf = str.toLatin1().data();
        string sendText;   //存放要发送的数据
        size_t j = 0;
        while(j < buf.size()) {
            for(size_t i = 1; i <= 11; i++) {
                if((j + i) <= buf.size()) {

                    if(i == 11) {
                        i = 1;
                    }

                    sendText = buf.substr(j, i);
                    //发送数据
                    sendText =  sendText + "~~~";
                    //tcpsocket->write(sendText.data());

                    item->write(sendText.data());
                }
                else {
                    sendText = buf.substr(j, (buf.size() - j));
                    //发送数据
                    sendText = sendText + "~~~";
                    item->write(sendText.data());
                    break;
                }
                j += i;
//                if(i == 255) {
//                    i = 1;
//                }
                sendText.clear();
            }
            break;
        }




    }
}

void Server::slotclientdisconnect(int descriptor)
{
    for(int i = 0; i < tcpclientsocketlist.count(); i++) {
        QTcpSocket *item = tcpclientsocketlist.at(i);
        //如果有客户端断开连接,将列表中的套接字删除
        if(item->socketDescriptor() == descriptor) {
            tcpclientsocketlist.removeAt(i);
            return;
        }
    }
    return;
}

通信
tcpsocket.h:
#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H

#include 
#include 
#include 
using namespace std;


class TcpClientSocket : public QTcpSocket
{
    Q_OBJECT
public:
    explicit TcpClientSocket(QObject *parent = nullptr);

protected slots:
    void recvdata();  //处理readyRead信号读取数据
    void slotclientdisconneceted();  //客户端断开触发disconnected,处理这个信号

signals:
    void updateserver(QString, int);  //用来告诉tcpserver需要更新界面
    void clientDisconnected(int);   //告诉server有客户端断开

public slots:

private:
    size_t strSize;
};

#endif // TCPCLIENTSOCKET_H

tcpsocket.cpp:
#include "tcpclientsocket.h"
#include 

TcpClientSocket::TcpClientSocket(QObject *parent) : QTcpSocket(parent)
{
    //客户端发过来的数据触发readyRead信号
    connect(this, &TcpClientSocket::readyRead, this, &TcpClientSocket::recvdata);
    connect(this, &TcpClientSocket::disconnected, this, &TcpClientSocket::slotclientdisconneceted);
}

void TcpClientSocket::recvdata()
{
    QByteArray array = readAll();
    int length = array.size();

    QString userName = QString(array).section("##", 0, 0);

    strSize = QString(array).section("##", 1, 1).toInt();
    //正文内容
    string strText = QString(array).section("##", 2, 2).toLatin1().data();
    string buf;
    for(size_t i = 0; i < strText.size(); i++) {
        buf.append(strText.substr(i, 1));
    }
    if(strSize != buf.size()) {
        qDebug() << "读取出错";
        return;
    }

    if(buf.size() == 0) {
        QString msg = userName;
        emit updateserver(msg, length);
    }
    else {
        QString msg = userName + "##" + buf.data();
        emit updateserver(msg, length);
    }

}

void TcpClientSocket::slotclientdisconneceted()
{
    emit clientDisconnected(this->socketDescriptor());
}

界面布局

[Qt] tcp服务器连接多个客户端的实现_第2张图片

你可能感兴趣的:([Qt] tcp服务器连接多个客户端的实现)