Qt 网络通信

获取本机网络信息

(1)在 .pro 文件中加入

QT       += network

(2)

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QDebug>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QMessageBox>
#include <QGridLayout>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void getHostInfo();

private slots:
    void slot_Detail();

private:
    QLabel *HostLabel;
    QLineEdit *HostNameLineEdit;
    QLabel *IpLabel;
    QLineEdit *AddressLineEdit;
    QPushButton *DetailBtn;
    QGridLayout *mainLay;
};
#endif // WIDGET_H

(3)

#include "widget.h"
#include <QNetworkInterface>
#include <QHostInfo>
#include <QString>
#include <QHostAddress>
#include <QNetworkAddressEntry>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->resize(400, 300);

    HostLabel = new QLabel("主机名: ");
    HostNameLineEdit = new QLineEdit;
    IpLabel = new QLabel("IP 地址: ");
    AddressLineEdit = new QLineEdit;
    DetailBtn = new QPushButton("详细");

    mainLay = new QGridLayout(this);
    mainLay->addWidget(HostLabel, 0, 0, 1, 1);
    mainLay->addWidget(IpLabel, 1, 0, 1, 1);
    mainLay->addWidget(HostNameLineEdit, 0, 1, 1, 2);
    mainLay->addWidget(AddressLineEdit, 1, 1, 1, 2);
    mainLay->addWidget(DetailBtn, 2, 0, 1, 3);

    getHostInfo();
    connect(DetailBtn, &QPushButton::clicked, this, &Widget::slot_Detail);
}

Widget::~Widget()
{
}

void Widget::getHostInfo()
{
    // 获取本机主机名,QHostInfo 提供了一系列有关网络信息的静态函数
    // 即可以根据主机名获取分配的 IP 地址,也可以根据 IP 地址获取相应的主机名。
    QString localHostName = QHostInfo::localHostName();
    HostNameLineEdit->setText(localHostName);

    // 根据主机名获取相关主机信息,包括 IP 地址等
    // fromName() 函数通过主机名查找 IP 地址信息
    QHostInfo hostInfo = QHostInfo::fromName(localHostName);

    //  获取主机 Ip 地址列表
    QList<QHostAddress> listAddress = hostInfo.addresses();
    if(!listAddress.isEmpty()) {
        AddressLineEdit->setText(listAddress.at(2).toString());
    }
}

void Widget::slot_Detail()
{
    QString detail = "";
    // 获取主机IP地址和网络接口列表
    QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();

    for(int i = 0; i < list.count(); ++i) {
        QNetworkInterface interface = list.at(i);

        // 获取网络接口的名称
        detail = detail + "设备: " + interface.name() + "\n";

        // 获取网络接口的硬件地址
        detail = detail + "硬件地址: " + interface.hardwareAddress() + "\n";

        // 每个网络借口包括0个或多个IP地址,每个IP地址有选择性地与一个子网掩码或一个广播地址相关联
        // QNetworkAddressEntry类,存储了被网络接口支持的一个IP地址,同时还包括与之相关的子网掩码和广播地址
        QList<QNetworkAddressEntry> entryList = interface.addressEntries();

        for(int j = 1; j < entryList.count(); ++j) {
            QNetworkAddressEntry entry = entryList.at(j);
            detail = detail + "\t" + "IP 地址: " + entry.ip().toString() + "\n";
            detail = detail + "\t" + "子网掩码: " + entry.netmask().toString() + "\n";
            detail = detail + "\t" + "广播地址: " + entry.broadcast().toString() + "\n";
        }
    }
    QMessageBox::information(this, "Detail", detail);
}


基千 UDP 的网络广播程序

在这里插入图片描述
服务端定时发送数据,客户端接收

Client:

(1)

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTextEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDialog>
#include <QUdpSocket>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void CloseBtnClicked();
    void dataReceived();

private:
    QTextEdit *ReceiveTextEdit;
    QPushButton *CloseBtn;
    QVBoxLayout *mainLay;

    int port;
    QUdpSocket *UdpSocket;
};
#endif // WIDGET_H

(2)

#include "widget.h"
#include <QMessageBox>
#include <QHostAddress>
#include <QByteArray>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->setWindowTitle("UDP Client");
    this->resize(300, 300);

    ReceiveTextEdit = new QTextEdit(this);
    CloseBtn = new QPushButton("Close", this);

    mainLay = new QVBoxLayout(this);
    mainLay->addWidget(ReceiveTextEdit);
    mainLay->addWidget(CloseBtn);

    connect(CloseBtn, &QPushButton::clicked, this, &Widget::CloseBtnClicked);

    port = 5555;
    UdpSocket = new QUdpSocket(this);
    // 当有数据到达I/O设备时(在这里是UdpSocket),发出 readyRead() 信号
    connect(UdpSocket, &QUdpSocket::readyRead, this, &Widget::dataReceived);
	
	// 被动接收数据的一方必须绑定固定端口,UDP通信中并无严格的服务端与客户端的区别
    bool res = UdpSocket->bind(port); // 绑定到指定端口上
    if(!res) {
        QMessageBox::information(this, "error", "udp socket create error");
        return;
    }

}

Widget::~Widget()
{
}

void Widget::CloseBtnClicked()
{
    close();
}

void Widget::dataReceived()
{// 将数据读出并显示
    // 判断 UdpSocket 中是否有数据报可读
    // hasPendingDatagrams() 在至少有一个数据报可读时返回 true
    while(UdpSocket->hasPendingDatagrams()) {
        QByteArray datagram;

        // pendingDatagramSize()  Returns the size of the first pending UDP datagram.
        // If there is no datagram available, this function returns -1.
        datagram.resize(UdpSocket->pendingDatagramSize());

        // qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
        // Receives a datagram and stores it in data
        // datagram.data() : char * QByteArray::data()
        UdpSocket->readDatagram(datagram.data(), datagram.size());

        QString msg = datagram.data();
        ReceiveTextEdit->insertPlainText(msg);
    }
}


Server

(1)

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUdpSocket>
#include <QTimer>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void StartBtnClicked();
    void Timeout();
private:
    QLabel *TimerLabel;
    QLineEdit *TextLineEdit;
    QPushButton *StartBtn;
    QVBoxLayout *mainLay;

    int port;
    bool isStarted;
    QUdpSocket *UdpSocket;
    QTimer *Timer;
};
#endif // WIDGET_H

(2)

#include "widget.h"
#include <QHostAddress>
#include <QString>
#include <QMessageBox>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    setWindowTitle("UDP Server");
    this->resize(300, 100);
    TimerLabel = new QLabel("计时器", this);
    TextLineEdit = new QLineEdit(this); // 要传输的文本
    StartBtn = new QPushButton("开始", this);

    mainLay = new QVBoxLayout(this);
    mainLay->addWidget(TimerLabel);
    mainLay->addWidget(TextLineEdit);
    mainLay->addWidget(StartBtn);

    isStarted = false;
    Timer = new QTimer(this);
    // 按钮控制定时器开关
    connect(StartBtn, &QPushButton::clicked, this, &Widget::StartBtnClicked);

    port = 5555;
    UdpSocket = new QUdpSocket(this);
    //  定时发送广播信息
    connect(Timer, &QTimer::timeout, this, &Widget::Timeout);
}

Widget::~Widget()
{
}

void Widget::StartBtnClicked()
{
    if(!isStarted) {
        // 上一个状态没有被启动,那么点击之后应该启动
        StartBtn->setText("停止");
        Timer->start(1000);
        isStarted = true;
    }
    else {
        // 上一个状态没有被启动,那么点击之后应该停止
        StartBtn->setText("开始");
        isStarted = false;
        Timer->stop();
    }
}

void Widget::Timeout()// 定时向端口发送广播信息
{
    QString msg = TextLineEdit->text(); // 读取要传输的文本
    if(msg.isEmpty()) return;

    // qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
    // Sends the datagram
    // QByteArray QString::toLatin1() const
    // QHostAddress::Broadcast ,指定向广播地址发送,The IPv4 broadcast address. Equivalent to QHostAddress("255.255.255.255").
    int len = UdpSocket->writeDatagram(msg.toLatin1().data(), msg.length(), QHostAddress::Broadcast, port);

    if(len != msg.length()){
        // 返回 -1,没有传输成功
        QMessageBox::information(this, "error", "传输失败");
    }
}

基千 TCP 的网络聊天室程序

QTcpServer 类

QTcpServer 类用于监听客户端连接以及和客户端建立连接

函数

构造函数:

QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);

设置监听:

// port:如果指定为0表示随机绑定一个可用端口。
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);

// 判断当前对象是否在监听, 是返回true,没有监听返回false
bool QTcpServer::isListening() const;

// 如果当前对象正在监听,则返回监听的服务器地址信息, 否则返回 QHostAddress::Null
QHostAddress QTcpServer::serverAddress() const;

// 如果服务器正在侦听连接,则返回服务器的端口; 否则返回0
quint16 QTcpServer::serverPort() const

处理来自客户端的请求

// 当一个来自客户端建立连接的新请求可以处理时,自动调用该函数
[virtual protected] void QTcpServer::incomingConnection(qintptr socketDescriptor)
// 需要把新建的通信套接字的描述符设为参数 socketDescriptor

信号

// 当接受新连接导致错误时,将发射如下信号。socketError参数描述了发生的错误相关的信息。
[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError);

// 每次有新连接可用时都会发出 newConnection() 信号。
[signal] void QTcpServer::newConnection();

QTcpSocket 类

QTcpSocket是一个套接字通信类,不管是客户端还是服务器端都需要使用。在Qt中发送和接收数据也属于IO操作(网络IO)

函数

构造函数:

QTcpSocket::QTcpSocket(QObject *parent = Q_NULLPTR);

连接服务器:

[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);

[virtual] void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);

I / O 操作:

// 指定可接收的最大字节数 maxSize 的数据到指针 data 指向的内存中
qint64 QIODevice::read(char *data, qint64 maxSize);
// 指定可接收的最大字节数 maxSize,返回接收的字符串
QByteArray QIODevice::read(qint64 maxSize);
// 将当前可用操作数据全部读出,通过返回值返回读出的字符串
QByteArray QIODevice::readAll();


// 发送指针 data 指向的内存中的 maxSize 个字节的数据
qint64 QIODevice::write(const char *data, qint64 maxSize);
// 发送指针 data 指向的内存中的数据,字符串以 \0 作为结束标记
qint64 QIODevice::write(const char *data);
// 发送参数指定的字符串
qint64 QIODevice::write(const QByteArray &byteArray);

信号

// 在使用QTcpSocket进行套接字通信的过程中,如果该类对象发射出readyRead()信号,说明对端发送的数据达到了
// 之后就可以调用 read 函数接收数据了。
[signal] void QIODevice::readyRead();


// 调用connectToHost()函数并成功建立连接之后发出connected()信号。
[signal] void QAbstractSocket::connected();

// 在套接字断开连接时发出disconnected()信号。
[signal] void QAbstractSocket::disconnected();

局域网聊天(客户端)

要保证客户端能连接服务器端,在同一 wifi 下聊天可以自行百度修改防火墙配置,或者租个服务器运行客户端(也要设置防火墙允许Tcp连接)

新建一个名为 TcpClient 的类,这里我继承自 QWidget
(1)tcpclient.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QListWidget>
#include <QPushButton>
#include <QLabel>
#include <QGridLayout>
#include <QLineEdit>

#include <QHostAddress>
#include <QString>
#include <QTcpSocket>

class TcpClient : public QWidget
{
    Q_OBJECT

public:
    TcpClient(QWidget *parent = nullptr);
    ~TcpClient();

private:
    QListWidget *contentListWidget;
    QLineEdit *sendLineEdit;
    QPushButton *sendBtn;
    QLabel *userNameLabel;
    QLineEdit *userNameLineEdit;
    QLabel *serverIPLabel;
    QLineEdit *serverIPLineEdit;
    QLabel *portLabel;
    QLineEdit *portLineEdit;
    QPushButton *enterBtn;
    QGridLayout *mainLay;

    bool status; // true 表示已经进入聊天室,false表示已经离开聊天室
    int port;
    QHostAddress *serverIP;
    QString userName;
    QTcpSocket *tcpSocket;

public slots:
    void slot_Enter();
    void slot_Connected();
    void slot_Disconnected();
    void slot_DataReceived();
    void slot_Send();
};
#endif // WIDGET_H

(2)tcpclient.cpp 里实现

#include "tcpclient.h"

#include <QMessageBox>
#include <QHostInfo>

TcpClient::TcpClient(QWidget *parent)
    : QWidget(parent)
{
    setWindowTitle("TCP Client");
    contentListWidget = new QListWidget;
    sendLineEdit = new QLineEdit;
    sendBtn = new QPushButton("发送");
    userNameLabel = new QLabel("用户名: ");
    userNameLineEdit = new QLineEdit;
    serverIPLabel = new QLabel("服务器地址: ");
    serverIPLineEdit = new QLineEdit;
    portLabel = new QLabel("端口: ");
    portLineEdit = new QLineEdit;
    enterBtn = new QPushButton("进入聊天室");

    mainLay = new QGridLayout(this);
    mainLay->addWidget(contentListWidget, 0, 0, 1, 2);
    mainLay->addWidget(sendLineEdit, 1, 0, 1, 1);
    mainLay->addWidget(sendBtn, 1, 1, 1, 1);
    mainLay->addWidget(userNameLabel, 2, 0, 1, 1);
    mainLay->addWidget(userNameLineEdit, 2, 1, 1, 1);
    mainLay->addWidget(serverIPLabel, 3, 0, 1, 1);
    mainLay->addWidget(serverIPLineEdit, 3, 1, 1, 1);
    mainLay->addWidget(portLabel, 4, 0, 1, 1);
    mainLay->addWidget(portLineEdit, 4, 1, 1, 1);
    mainLay->addWidget(enterBtn, 5, 0, 1, 2);


    status = false;
    port = 8010;
    portLineEdit->setText(QString::number(port));
    serverIP = new QHostAddress();
    serverIPLineEdit->setText("127.0.0.1");// 服务器的ip地址

    connect(enterBtn, &QPushButton::clicked, this, &TcpClient::slot_Enter);
    connect(sendBtn, &QPushButton::clicked, this, &TcpClient::slot_Send);
    // 设置按钮不能处理键盘和鼠标事件
    sendBtn->setEnabled(false);
}

TcpClient::~TcpClient()
{
}

void TcpClient::slot_Enter()
{ // 实现进入和离开聊天室的功能
    if(!status) {
        QString ip = serverIPLineEdit->text();
        // 判断给定的IP地址能否被正确解析
        // setAddress参数为QString时,调用的是有bool返回值的重载函数
        if(!serverIP->setAddress(ip)) {
            QMessageBox::information(this, "error", "server ip address error");
            return;
        }
        if(userNameLineEdit->text().isEmpty()) {
            QMessageBox::information(this, "error", "User name can't be empty");
            return;
        }

        userName = userNameLineEdit->text();
        tcpSocket = new QTcpSocket(this);
        connect(tcpSocket, &QTcpSocket::connected, this, &TcpClient::slot_Connected);
        connect(tcpSocket, &QTcpSocket::disconnected, this, &TcpClient::slot_Disconnected);
        connect(tcpSocket, &QTcpSocket::readyRead, this, &TcpClient::slot_DataReceived);
        // 与TCP服务器连接,连接成功后发出 connected 信号
        tcpSocket->connectToHost(*serverIP, port);
        status = true;
    }
    else {
        QString msg = userName + " : Leave Chat Room";
        int len = tcpSocket->write(msg.toUtf8(), msg.length());
        if(len != msg.length()) return;
        // 断开连接,之后发出 disconnected 信号
        tcpSocket->disconnectFromHost();
        status = false;
    }
}

void TcpClient::slot_Connected()
{
    sendBtn->setEnabled(true);
    enterBtn->setText("离开");
    QString msg = userName + " Enter Chat Room";
    int len = tcpSocket->write(msg.toUtf8(), msg.length());
    if(len != msg.length()) {
        QMessageBox::information(this, "error", "enter chat room failed");
        return;
    }
}

void TcpClient::slot_Disconnected()
{
    sendBtn->setEnabled(false);
    enterBtn->setText("进入聊天室");
}

void TcpClient::slot_DataReceived()
{
    while(tcpSocket->bytesAvailable() > 0) {
//        QByteArray datagram;
//        datagram.resize(tcpSocket->bytesAvailable());
//        tcpSocket->read(datagram.data(), datagram.size());
//        QString msg = datagram.data();
//        msg = msg.left(datagram.size());
        QString msg = QString::fromUtf8(tcpSocket->readAll());
        contentListWidget->addItem(msg);
    }
}

void TcpClient::slot_Send()
{
    if(sendLineEdit->text().isEmpty()) return;
    QString msg = userName + ":" + sendLineEdit->text();
//    qDebug() << "Client send : " << msg << " size = " << msg.size() << " length : " << msg.length();
    tcpSocket->write(msg.toUtf8(), msg.toUtf8().size());
    sendLineEdit->clear();
}


局域网聊天(服务器端)

新建一个 TcpServer 类,这里我也继承自了 QWidget

做界面设计
直接用封装的 QTcpServer 即可

(1)tcpserver.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QListWidget>


#include "server.h"

class TcpServer : public QWidget
{
    Q_OBJECT

public:
    TcpServer(QWidget *parent = nullptr);
    ~TcpServer();

private:
    QListWidget *ContentListWidget;
    QLabel *PortLabel;
    QLineEdit *PortLineEdit;
    QPushButton *CreateBtn;
    QGridLayout *mainLay;

    int port;
    Server *server;

public slots:
    void slot_CreateServer();
    void slot_updateServer(QString, int);
};
#endif // WIDGET_H

(2)tcpserver.cpp

#include "tcpserver.h"

TcpServer::TcpServer(QWidget *parent)
    : QWidget(parent)
{
    setWindowTitle("TCP Server");
    this->resize(300, 300);

    ContentListWidget = new QListWidget;
    PortLabel = new QLabel("端口: ");
    PortLineEdit = new QLineEdit;
    CreateBtn = new QPushButton("创建聊天室");

    mainLay = new QGridLayout(this);
    mainLay->addWidget(ContentListWidget, 0, 0, 2, 2);
    mainLay->addWidget(PortLabel, 2, 0, 1, 1);
    mainLay->addWidget(PortLineEdit, 2, 1, 1, 1);
    mainLay->addWidget(CreateBtn, 3, 0, 1, 2);

    port = 8010;
    PortLineEdit->setText(QString::number(port));
    connect(CreateBtn, &QPushButton::clicked, this, &TcpServer::slot_CreateServer);

}

TcpServer::~TcpServer()
{

}

void TcpServer::slot_CreateServer()
{
    server = new Server(this, port);
    connect(server, &Server::signal_updateServer, this, &TcpServer::slot_updateServer);
    // QPushButtn 继承自 QWidget
    // In general an enabled widget handles keyboard and mouse events; a disabled widget does not.
    CreateBtn->setEnabled(false);
}

void TcpServer::slot_updateServer(QString msg, int len)
{
    ContentListWidget->addItem(msg.left(len));
}

自定义TcpClientSocket 类,继承自 QTcpSocket

服务器端用来和客户端通信的套接字,这里要进行自定义的一个原因是:服务器端通信套接字,接收完消息要进行一个转发,类似于回声服务器,但是这里不仅要发给发送消息的客户端,还要发送给其他客户端。我们需要在读完数据后立马触发信号,这样就能即时更新消息。

(1)tcpclientsocket.h

#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H

#include <QTcpSocket>
#include <QObject>

class TcpClientSocket : public QTcpSocket
{
    Q_OBJECT
public:
    TcpClientSocket(QObject *parent = 0);
// 服务端用于通信的套接字
signals:
    void signal_updateClients(QString, int); // 通知服务器向聊天室内的所有成员广播信息
    void signal_Disconnected(int);

protected slots:
    void slot_DataReceived();
    void slot_Disconnected();
};

#endif // TCPCLIENTSOCKET_H

(2)tcpclientsocket.cpp

#include "tcpclientsocket.h"
// 服务器端通信的套接字
TcpClientSocket::TcpClientSocket(QObject *)
{
//    if(parent) this->setParent(parent);

    // once every time new data is available for reading from the device's current read channel.
    connect(this, &QTcpSocket::readyRead, this, &TcpClientSocket::slot_DataReceived);
    // when the socket has been disconnected.
    connect(this, &QTcpSocket::disconnected, this, &TcpClientSocket::slot_Disconnected);

}

void TcpClientSocket::slot_DataReceived()
{// 读出数据
    //  qint64 QAbstractSocket::bytesAvailable() const
    // Returns the number of incoming bytes that are waiting to be read.
    while(this->bytesAvailable() > 0) {
        QString msg = QString::fromUtf8(this->readAll());
        int len = msg.size();
        //qDebug() << "server receive :" << msg;
        
        // 服务器端接收来自客户端的数据结束,向聊天室内的所有成员广播信息
        emit signal_updateClients(msg, len); 
    }
}

void TcpClientSocket::slot_Disconnected()
{
    // socketDescriptor() 用于获取底层的套接字描述符, 通常是一个整数
    // 向监听QTcpServer发送当前断开连接的套接字的描述符(套接字编号)
    emit signal_Disconnected(this->socketDescriptor());
}

自定义 Server 类,继承自 QTcpServer

QTcpServer 的作用就是监听来自客户端的请求

(1)server.h

#ifndef SERVER_H
#define SERVER_H

#include <QTcpServer>
#include <QObject>
#include <QList>
#include "tcpclientsocket.h"

class Server : public QTcpServer
{
    Q_OBJECT

public:
    Server(QObject *parent = 0, int port = 0);
    // 保存与客户端连接的 TcpClientSocket
    QList<TcpClientSocket *> tcpClientSocketList;

signals:
    void signal_updateServer(QString, int);// 更新ui,聊天室内容

public slots:
    void slot_updateClients(QString, int);
    void slot_Disconnected(int);

protected: // 重载函数别写错了,32位写int,64位写long long
    void incomingConnection(long long socketDescriptor);
};


#endif // SERVER_H

(2)server.cpp

#include "server.h"
#include <QHostAddress>

Server::Server(QObject *parent, int port) : QTcpServer(parent)
{
    // 在指定的port端口,对任意地址进行监听
    listen(QHostAddress::Any, port);
}
// 类似于accept,但此函数自动调用,处理来自客户端建立链接的请求
void Server::incomingConnection(long long socketDescriptor)
{

    // 新建一个用于和客户端通信的 TcpSocket
    TcpClientSocket *tcpClientSocket = new TcpClientSocket(this);
	
	// 如果tcpClientSocket接受完来自客户端的信息,触发更新
    connect(tcpClientSocket, &TcpClientSocket::signal_updateClients, this, &Server::slot_updateClients);
    // 监听用于通信的套接字是否断开连接
    connect(tcpClientSocket, &TcpClientSocket::signal_Disconnected, this, &Server::slot_Disconnected);
	
	//将新创建的通信套接字的描述符指定为参数socketdescriptor
    tcpClientSocket->setSocketDescriptor(socketDescriptor);
    //将这个套接字加入客户端套接字列表中
    tcpClientSocketList.append(tcpClientSocket);
}

void Server::slot_updateClients(QString msg, int len)
{// 服务器端将刚刚接收自某个客户端的信息,发送到所有客户端,实现聊天室消息同步
    // 更新服务器对话框的内容
    emit signal_updateServer(msg, len);
    // 向聊天室内的所有成员广播信息
    for(int i = 0; i < tcpClientSocketList.count(); ++i) {
    // 取出与第i个客户端建立连接的用于通信的套接字
        QTcpSocket *tmp = tcpClientSocketList.at(i);
        if(tmp->write(msg.toUtf8(), msg.toUtf8().size()) != msg.toUtf8().size()) {
            continue;
        }
    }
}

void Server::slot_Disconnected(int descriptor) {
// 遍历找到并删除断开的通信套接字
    for(int i = 0; i < tcpClientSocketList.count(); ++i) {
        QTcpSocket *tmp = tcpClientSocketList.at(i);
        if(tmp->socketDescriptor() == descriptor) {
            tcpClientSocketList.removeAt(i);
            return;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/190871.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言学习笔记之函数篇

与数学意义上的函数不同&#xff0c;C语言中的函数又称为过程&#xff0c;接口&#xff0c;具有极其重要的作用。教科书上将其定义为&#xff1a;程序中的子程序。 在计算机科学中&#xff0c;子程序&#xff08;英语&#xff1a;Subroutine, procedure, function, routine, me…

【Spring】Spring事务详解

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

基于springboot+vue的学生宿舍管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

单片机学习6——定时器/计数功能的概念

在8051单片机中有两个定时器/计数器&#xff0c;分别是定时器/计数器0和定时器/计数器1。 T/C0: 定时器/计数器0 T/C1: 定时器/计数器1 T0: 定时器0 T1: 定时器1 C0: 计数器0 C1: 计数器1 如果是对内部振荡源12分频的脉冲信号进行计数&#xff0c;对每个机器周期计数&am…

苹果的未来:分析其成长策略和 2 兆美元以上的野心

Apple正在蕴育新的创新增长。作为世界上最有价值的公司&#xff0c;苹果公司拥有超过2万亿美元的市值和超过1000亿美元的年利润&#xff0c;并成功用十几年实践去打造和培育了一个硬件、软件和服务“三位一体”的商业生态&#xff0c;始终坚持以用户体验为先&#xff0c;创新极…

地铁在线售票vue票务系统uniAPP+vue 微信小程序

功能介绍 管理员 &#xff08;1&#xff09;管理员登录功能 &#xff08;2&#xff09;查看和修改线路信息 &#xff08;3&#xff09;减少线路 &#xff08;4&#xff09;修改价格&#xff08;5站3元 5-10 5元 10-15站6元 往上8元&#xff09; &#xff08;5&#xff09;删除用…

手摸手vue2+Element-ui整合Axios

后端WebAPI准备 跨域问题 为了保证浏览器的安全,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,称为同源策略,同源策略是浏览器安全的基石 同源策略( Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能 所谓同源(即指在同一个域)就是两个页面具…

TDA笔记:夏克林老师,南洋理工大学

TDA比传统的统计方法有优势&#xff1a;benchmark中展现了这种优势 laplacian矩阵 多种单纯复形构造方式&#xff0c;可以构造出不同表征 二部图&#xff1a;Dowker complex Tor algebra可以用到多大数据 目前较新

竞赛选题 题目:基于卷积神经网络的手写字符识别 - 深度学习

文章目录 0 前言1 简介2 LeNet-5 模型的介绍2.1 结构解析2.2 C1层2.3 S2层S2层和C3层连接 2.4 F6与C5层 3 写数字识别算法模型的构建3.1 输入层设计3.2 激活函数的选取3.3 卷积层设计3.4 降采样层3.5 输出层设计 4 网络模型的总体结构5 部分实现代码6 在线手写识别7 最后 0 前言…

@Async注解的坑,小心

背景 前段时间&#xff0c;一个同事小姐姐跟我说她的项目起不来了&#xff0c;让我帮忙看一下&#xff0c;本着助人为乐的精神&#xff0c;这个忙肯定要去帮。 于是&#xff0c;我在她的控制台发现了如下的异常信息&#xff1a; Exception in thread "main" org.s…

传奇,全职业刀刀烈火原理揭秘

相信很多朋友都玩过传奇, 今天我们来揭秘一下,刀刀烈火的实现原理, 其实非常简单. 烈火作为一个技能,使用流程是先释放技能, 获得一个烈火附加的状态,那么下一次攻击就会带烈火效果了. 这里我们拿烈火附加状态,当突破口,进行扫描即可. 绝大部分情况应该是有状态为1(真),没状…

NX二次开发UF_CURVE_ask_wrap_curve_parents 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_wrap_curve_parents Defined in: uf_curve.h int UF_CURVE_ask_wrap_curve_parents(tag_t curve_tag, tag_t * defining_face, tag_t * defining_plane, tag_t * defin…

【KubeSphere】基于AWS在 Linux 上以 All-in-One 模式安装 KubeSphere

文章目录 一、实验配置说明二、实验准备工作1.确认系统版本2. 修改网络DNS3. 关闭SELINUX4. 关闭防火墙 三、实验依赖项安装四、下载 KubeKey五、一键化安装部署六、验证安装结果七、登录KubeSphere管理控制台八、参考链接 一、实验配置说明 本实验基于AWS启动一台新实例&…

电子学会C/C++编程等级考试2021年06月(三级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:数对 给定2到15个不同的正整数,你的任务是计算这些数里面有多少个数对满足:数对中一个数是另一个数的两倍。 比如给定1 4 3 2 9 7 18 22,得到的答案是3,因为2是1的两倍,4是2个两倍,18是9的两倍。 时间限制:1000 内存限制…

小程序如何进行版本升级

小程序版本升级是非常重要的&#xff0c;它可以帮助商家及时更新功能、修复bug&#xff0c;提升用户体验&#xff0c;增加小程序的竞争力。那么&#xff0c;商家怎么进行小程序版本升级呢&#xff1f;下面具体介绍。 在小程序管理员后台->版本设置处&#xff0c;会显示是否…

Django之Auth认证模块

文章目录 一、简介二、Auth模块是什么三、Auth模块常用方法create_user() 创建普通用户authenticate() 用户认证auth.login(HttpResponse&#xff0c;user)登录状态保持is_authenticated 登录认证判断auth.loginout(reqeust)退出登录login_required() 登录认证装饰器check_pass…

迭代器模式

自定义 Counter 结构体类型&#xff0c;并实现迭代器。其他语言的场景&#xff0c;读取数据库行数据时&#xff0c;使用的就是迭代器。我们使用for语言遍历数组&#xff0c;也是一种迭代。 结构体对象实现 Iterator trait&#xff0c;创建自定义的迭代器&#xff0c;只需要实现…

由于找不到msvcp120.dll无法继续执行代码是什么原因怎么修复

今天我想和大家分享的是关于“msvcp120.dll丢失的解决方法”。或许有些同学在平时使用电脑的过程中会遇到这个问题&#xff0c;但是并不知道该如何解决。那么&#xff0c;接下来我将从三个方面为大家介绍&#xff1a;msvcp120.dll丢失的原因、msvcp120.dll是什么以及msvcp120.d…

GEE 22:基于GEE实现物种分布模型(更新中。。。。。。)

物种分布模型 1. 数据点准备1.1 数据加载1.2 去除指定距离内的重复点1.3 定义研究区范围1.4 选择预测因子 1. 数据点准备 1.1 数据加载 首先需要将CSV文件导入到GEE平台中&#xff0c;同样也可以导入shp格式文件。 // 1.Loading and cleaning your species data *************…

LeetCode Hot100 114.二叉树展开为链表

题目&#xff1a; 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同…