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

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

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

客户端的实现:

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

#include <QWidget>
#include <QTcpSocket>
#include <iostream>
#include <string>
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 <QHostAddress>
#include <QMessageBox>

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("连接服务器");
}

在这里插入图片描述

服务端的实现:

tcpserver.h:
#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QWidget>
#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 <QDebug>

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 <QTcpServer>
#include <QObject>
#include <QList>
#include "tcpclientsocket.h"


class Server : public QTcpServer
{
    Q_OBJECT
public:
    explicit Server(QObject *parent = 0, int port = 0);
    QList<TcpClientSocket*> 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 <QHostAddress>
#include <QDebug>

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 <QTcpSocket>
#include <iostream>
#include <string>
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 <QDebug>

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());
}

界面布局

在这里插入图片描述

  • 21
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
实现一个Qt TCP服务器连接多个客户端,可以使用Qt Network模块中的QTcpServer和QTcpSocket类。 首先要创建一个QTcpServer对象,并在其中的某个函数(如`incomingConnection()`)中监听新的客户端连接请求。当有新的客户端连接请求时,可以通过调用`nextPendingConnection()`函数来获得一个QTcpSocket对象,表示与该客户端连接。 接下来,可以将该QTcpSocket对象添加到一个存储所有客户端连接的容器中(如QList<QTcpSocket*>),并对其进行读写操作。可以使用`readyRead()`信号来接收客户端发送的数据,使用`write()`函数来向客户端发送数据。 当客户端连接断开时,可以使用`disconnected()`信号来处理该事件,并从存储所有客户端连接的容器中将该QTcpSocket对象移除。 以下是一个简单的示例代码: ```cpp #include <QTcpServer> #include <QTcpSocket> #include <QList> class MyServer : public QTcpServer { Q_OBJECT public: MyServer(QObject *parent = nullptr) : QTcpServer(parent) {} protected: void incomingConnection(qintptr socketDescriptor) override { QTcpSocket *clientSocket = new QTcpSocket(this); if (!clientSocket->setSocketDescriptor(socketDescriptor)) { delete clientSocket; return; } m_clients.append(clientSocket); connect(clientSocket, &QTcpSocket::readyRead, this, &MyServer::readData); connect(clientSocket, &QTcpSocket::disconnected, this, &MyServer::disconnected); } private slots: void readData() { QTcpSocket *clientSocket = static_cast<QTcpSocket*>(sender()); if (!clientSocket) { return; } QByteArray data = clientSocket->readAll(); // 处理接收到的数据 // 回复客户端 clientSocket->write("Hello, client!"); } void disconnected() { QTcpSocket *clientSocket = static_cast<QTcpSocket*>(sender()); if (!clientSocket) { return; } m_clients.removeOne(clientSocket); clientSocket->deleteLater(); } private: QList<QTcpSocket*> m_clients; }; ``` 在上面的代码中,`MyServer`继承自`QTcpServer`,并重写了`incomingConnection()`函数处理新的客户端连接请求。在该函数中,创建一个新的QTcpSocket对象表示与该客户端连接,并将其添加到存储所有客户端连接的容器中。 在`readData()`函数中,处理接收到的数据并向客户端回复数据。在`disconnected()`函数中,处理客户端断开连接的事件并将其从存储所有客户端连接的容器中移除。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值