Qt 网络通信

获取本机网络信息

(1)在 .pro 文件中加入

cpp 复制代码
QT       += network

(2)

cpp 复制代码
#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)

cpp 复制代码
#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)

cpp 复制代码
#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)

cpp 复制代码
#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)

cpp 复制代码
#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)

cpp 复制代码
#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 类用于监听客户端连接以及和客户端建立连接

函数

构造函数:

cpp 复制代码
QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);

设置监听:

cpp 复制代码
// 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

处理来自客户端的请求

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

信号

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

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

QTcpSocket 类

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

函数

构造函数:

cpp 复制代码
QTcpSocket::QTcpSocket(QObject *parent = Q_NULLPTR);

连接服务器:

cpp 复制代码
[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 操作:

cpp 复制代码
// 指定可接收的最大字节数 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);

信号

cpp 复制代码
// 在使用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

cpp 复制代码
#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 里实现

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

cpp 复制代码
#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

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

cpp 复制代码
#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

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

cpp 复制代码
#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

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;
        }
    }
}
相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript