QT-TCP网络编程

总体认识:

Qt NetWork提供了用于编写TCP/IP网络应用程序的各种类:

​ TCP的QTcpSocket和QTcpServer

​ UDP的QUdpSocket


TCP通信:

传输控制协议(transmission control protocol,TCP);可靠的,面向流和连接的传输协议,适合用于连续数据的传输.

通信必须先建立TCP连接;通信端需存在客户端和服务端.

服务端:

服务端必须使用QTcpServer进行端口监听,建立服务器;使用QTcpSocket建立连接,然后使用套接字(socket)进行通信.

QTcpServer的父类是QObject.

服务端程序首先需要使用listen()开始服务器监听,设置服务器监听的IP地址和端口,一般一个服务器只监听某个端口的网络连接.

当有新客户端连入时,QTcpServer的incomingConnection()会创建一个与该客户端连接的QTcpSocket对象,然后发送newConnection()信号;在该槽函数中可以用nextPendingConnection()接受客户端的连接,然后使用对应socket与客户端通信.

QTcpSocket是从QIODevice间接继承的类,因此是一种I/O设备类;

QTcpSocket其他函数都是从其父类QAbstractSocket继承或重定义的;

客户端:

客户端通过QTcpSocket对象与服务器建立连接并保持通信;客户端socket首先通过connectToHost()尝试连接服务器,需指定服务器IP地址和监听端口,该函数以异步方式连接到服务器,不会阻塞程序运行,连接成功后QTcpSocket发送connected()信号.

客户端与服务器建立连接后,各自的socket对象就可以向缓冲区写数据或读数据,当缓冲区有新数据进入时,socket会发送readyRead()信号,一般在对应槽函数读取缓冲区数据.

读取数据与写入数据的缓冲区是分开的,异步工作,不会冲突.


项目:局域网内部通信部分代码展示与解读

服务端:

//初始化服务端
void WidgetManager::initTCPServer()
{
    
    QString localIP = getLocalIP_server();	//获取服务端需要的本地IP

    tcpserver = new QTcpServer(this);	//基于本类创建的tcp server对象,该通信程序只同时存在一个服务端

    connect(tcpserver,SIGNAL(newConnection()),this,SLOT(do_newConnection()));	//将QTcpServer的newConnection信号与槽函数连接,当出现新客户端连入时触发自动调用处理
}

服务端的QTcpSocket对象初始化设置为nullptr不指向,后续用作虚拟客户端的对象;


//服务端获取本地IP
QString WidgetManager::getLocalIP_server()
{
    QString hostName = QHostInfo::localHostName();	//本地主机名
    QHostInfo hostInfo = QHostInfo::fromName(hostName);	//返回对应主机名的IP地址
    QString localIP = "";

    QList<QHostAddress> addrList = hostInfo.addresses();	//本机的IP地址列表

    if(addrList.isEmpty())
        return localIP;
    foreach (QHostAddress aHost, addrList) {
        if(QAbstractSocket::IPv4Protocol == aHost.protocol()){
            localIP = aHost.toString();	//查找IPv4协议的IP地址
            break;
        }
    }
    return localIP;
}

void WidgetManager::serverStartListen(QString serverIP,QString serverPort)
{
    QString IP = serverIP;	//本机监听为127.0.0.1,也可以是QHostAddress::LocalHost
    quint16 port = serverPort.toInt();

    QHostAddress address(IP);	//IP转换为QHostAddress用于参数

    tcpserver->listen(address,port);	//调用listen开始监听

    tcp_server_textfield_append("***开始监听 :"+tcpserver->serverAddress().toString()+" 端口: "+QString::number(tcpserver->serverPort()));

    tcp_server_state_changed(QString("监听状态 : 正在监听"));
}

void WidgetManager::serverStopListen()
{
    if(tcpserver->isListening())	//正在监听
    {
        if(tcpserver_socket != nullptr)	//socket对象存在
        {
            if(tcpserver_socket->state() == QAbstractSocket::ConnectedState)	//状态为已连接
            {
                tcpserver_socket->disconnectFromHost();	//虚拟客户端从服务端断开
            }
        }

        tcpserver->close();	//关闭服务端,此处为停止服务端本身的监听行为

        tcp_server_state_changed(QString("监听状态 : 停止监听"));
    }
}

void WidgetManager::do_newConnection()
{
    tcpserver_socket = tcpserver->nextPendingConnection();	//接受新连接

    connect(tcpserver_socket,SIGNAL(connected()),this,SLOT(do_clientConnected()));	//连接函数与槽函数绑定
    do_clientConnected();	//槽函数试运行,同时让假定服务端创建的虚拟客户端连接
    connect(tcpserver_socket,SIGNAL(disconnected()),this,SLOT(do_clientDisconnected()));	//断连函数与槽函数绑定
    connect(tcpserver_socket,&QTcpSocket::stateChanged,this,&WidgetManager::do_socketStateChanged);	//状态检测函数与槽函数绑定
    do_socketStateChanged(tcpserver_socket->state());	//试运行确定连接状态
    connect(tcpserver_socket,SIGNAL(readyRead()),this,SLOT(do_socketReadyRead()));	//readyRead函数绑定槽函数,缓冲区存在数据后即可触发
}

void WidgetManager::do_socketStateChanged(QAbstractSocket::SocketState socketState)
{
    switch (socketState) {
    case QAbstractSocket::UnconnectedState:
        tcp_server_state_changed("socket 状态: UnconnectedState");    break;
    case QAbstractSocket::HostLookupState:
        tcp_server_state_changed("socket 状态: HostLookupState");    break;
    case QAbstractSocket::ConnectingState:
        tcp_server_state_changed("socket 状态: ConnectingState");    break;
    case QAbstractSocket::BoundState:
        tcp_server_state_changed("socket 状态: BoundState");    break;
    case QAbstractSocket::ClosingState:
        tcp_server_state_changed("socket 状态: ClosingState");    break;
    case QAbstractSocket::ListeningState:
        tcp_server_state_changed("socket 状态: ListeningState");    break;
    }
}

void WidgetManager::do_clientDisconnected()
{
    tcp_server_textfield_append("**client socket disconnected");
    tcpserver_socket->deleteLater();	//删除服务端的socket对象
}

客户端:

//初始化客户端
void WidgetManager::initTCPClient()
{
    tcpClient = new QTcpSocket(this);

    QString localIP = getLocalIP_client();

    // 保留IP显示状态

    connect(tcpClient,SIGNAL(connected()),this,SLOT(do_connected()));	//槽函数连接情况与服务端基本一致
    connect(tcpClient,SIGNAL(disconnected()),this,SLOT(do_disconnected()));
    connect(tcpClient,&QTcpSocket::stateChanged,this,&WidgetManager::do_socketStateChange);
    connect(tcpClient,SIGNAL(readyRead()),this,SLOT(do_socketReadyReadClient()));
}

void WidgetManager::clientStartConnect(QString address, QString port)
{
    QString addr = address;
    quint16 prt = port.toInt();

    tcpClient->connectToHost(addr,prt);	//客户端尝试连接服务端,由服务端决策连接结果
}

void WidgetManager::clientStopConnect()
{
    if(tcpClient->state() == QAbstractSocket::ConnectedState)
        tcpClient->disconnectFromHost();	//客户端主动断开,服务端会有响应
}

void WidgetManager::clientSendData(QString data)
{
    QString msg = data;

    QByteArray str = msg.toUtf8();

    str.append('\n');

    tcpClient->write(str);

    tcpClient->flush();	//写入即发送的刷新函数
}

其余函数与上服务端一致,另编写见完整代码;

此版于2023/10/1 10:56更新,参考QT6开发指南

posted @ 2023-10-01 10:59  Neko_Code  阅读(28)  评论(0编辑  收藏  举报