关闭

Qt5网络与通信

1760人阅读 评论(3) 收藏 举报
分类:
在应用程序开发中网络编程非常重要,目前互联网通信的TCP/IP协议,自上而下分为应用层、传输层、网际层和网络接口层这四层。实际编写网络应用程序时只使用到
传输层和应用层,所涉及的协议主要包括UDP、TCP、FTP和HTTP等。
10.1获取本机网络信息
在网络应用中,进场需要获得本机的主机名、IP地址和硬件地址等网络信息。运用QHostInfo、QNetworkInterface、QNetworkAddressEntry可获得本机的网络信息。

运行效果如下:




networkinformation.h

#ifndef NETWORKINFORMATION_H
#define NETWORKINFORMATION_H

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

class NetworkInformation : public QWidget
{
    Q_OBJECT

public:
    NetworkInformation(QWidget *parent = 0);
    ~NetworkInformation();
    void getHostInformation();
public slots:
    void slotHostDetailInformation();
private:
    QLabel *hostLabel;
    QLineEdit *hostNameLineEdit;
    QLabel *ipLabel;
    QLineEdit *ipLineEdit;
    QPushButton *detailBtn;
    QGridLayout *mainLayout;
};

#endif // NETWORKINFORMATION_H

networkinformation.cpp

#include "networkinformation.h"
#include <QHostInfo>
#include <QNetworkInterface>

NetworkInformation::NetworkInformation(QWidget *parent)
    : QWidget(parent)
{
    hostLabel = new QLabel(tr("主机名:"));
    hostNameLineEdit = new QLineEdit;
    ipLabel = new QLabel(tr("IP 地址:"));
    ipLineEdit = new QLineEdit;
    detailBtn = new QPushButton(tr("详情"));
    mainLayout = new QGridLayout(this);
    mainLayout->addWidget (hostLabel, 0, 0);
    mainLayout->addWidget (hostNameLineEdit, 0, 1);
    mainLayout->addWidget (ipLabel, 1, 0);
    mainLayout->addWidget (ipLineEdit, 1, 1);
    mainLayout->addWidget (detailBtn, 2, 0, 1, 2);
    getHostInformation ();
    connect (detailBtn, SIGNAL(clicked(bool)), this, SLOT(slotHostDetailInformation()));
}

NetworkInformation::~NetworkInformation()
{

}
/*
 * QString localHostName = QHostInfo::localHostName ():获得本机主机名。QHostInfo提供了一系列有关网络信息的静态函数,
 * 可以根据主机名获得分配的IP地址,也可以根据IP地址获得相应的主机名
 *
 * QHostInfo hostInfo = QHostInfo::fromName (localHostName):根据主机名获得相关信息,包括IP地址等。QHostInfo::
 * fromName()函数通过主机名查找IP地址信息。
 *
 * if(!listAdress.isEmpty ()){...}获得的主机IP地址列表可能为空。在不为空的情况下使用第一个IP地址。
*/
void NetworkInformation::getHostInformation ()
{
    QString localHostName = QHostInfo::localHostName ();
    hostNameLineEdit->setText (localHostName);
    QHostInfo hostInfo = QHostInfo::fromName (localHostName);
    QList<QHostAddress> listAdress = hostInfo.addresses ();
    if(!listAdress.isEmpty ())
    {
        ipLineEdit->setText (listAdress.first ().toString ());
    }
}
/*
 * QNetworkInterface类提供了一个主机IP地址和网络接口的列表
 *
 * interface.name():获得网络接口的名称
 * interface.hardwareAddress():获得网络接口的硬件地址
 * interface.addressEntries():每个网络接口包括0个或多个IP地址,每个IP地址有选择性地与一个子网掩码和一个广播地址相关联。
 * QNetworkAddressEntry类存储了被网络接口支持的一个IP地址,同时还包括与之相关的子网掩码和广播地址
 *
 * QMessageBox::information
 * (
  QWidget*parent,                       //消息框的父窗口指针
  const QString& title,                 //消息框的标题栏
  const QString& text,                  //消息框的文字提示信息
  StandardButtonsbuttons=Ok,            //同Question消息框的注释内容
  StandardButton defaultButton=NoButton //同Question消息框的注释内容
);

*/
void NetworkInformation::slotHostDetailInformation ()
{
    QString detail = "";
    QList<QNetworkInterface> list = QNetworkInterface::allInterfaces ();

    for(int i = 0; i < list.count (); i++)
    {
        QNetworkInterface interface = list.at (i);
        detail = detail + tr("设备") + interface.name () + "\n";
        detail = detail + tr("硬件地址") + interface.hardwareAddress () + "\n";

        QList<QNetworkAddressEntry> entryList = interface.addressEntries ();
        for(int j = 0; j  < entryList.count (); j++)
        {
            QNetworkAddressEntry entry = entryList.at(j);
            detail = detail + "\t" + tr("IP 地址:") + entry.ip ().toString () + "\n";
            detail = detail + "\t" + tr("子网掩码") + entry.netmask ().toString () + "\n";
            detail = detail + "\t" + tr("广播地址") + entry.broadcast ().toString () + "\n";
        }
    }
    QMessageBox::information (this, tr("Detail"), detail);
}
10.2基于UDP的网络广播程序
用户数据报协议(User Data Protocol,UDP)是一种简单轻量级、不可靠、面向数据报、无连接的传输层协议,可以应用在可靠性不是十分重要的场合,如短消息、广
播信息等。
适合应用的情况有以下几种:
     网络数据大多为短消息。
     拥有大量客户端。
     对数据安全性无特殊要求。
     网络负担非常重,但对响应速度要求高。
10.2.1 UDP协议工作原理
如下图所示,UDP客户端向UDP服务器发送一定长度的请求报文,报文大小的限制与各系统的协议实现有关,但不得超过其下层IP协议规定的64KB;UDP服务器同样以报
文形式作出响应。如果服务器未收到此请求,客户端不会重发,因此报文的传输是不可靠的。

例如,常用的聊天工具--腾讯QQ软件就是使用UDP协议发送协议消息的,因此有时会出现收不到消息的情况。
10.2.2 UDP编程模型
基于UDP协议的经典编程模型,程序编程的通信流程如图所示。

可以看出,在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发出数据报。类似地,服务器也不从客户端接收链接,只负责调用接收函数,
等待来自某客户端的数据到达。
Qt中通过QUdpSocket类实现UDP协议的编程。接下来通过一个实例,介绍如何实现基于UDP协议的广播应用,它由UDP服务器和UDP客户端两部分组成。

10.2.3 UDP服务器编程
udpserver.h

#ifndef UDPSERVER_H
#define UDPSERVER_H

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

class UdpServer : public QDialog
{
    Q_OBJECT

public:
    UdpServer(QWidget *parent = 0);
    ~UdpServer();
public slots:
    void StartBtnClicked();
    void timeout();
private:
    QLabel *TimerLabel;
    QLineEdit *TextLineEdit;
    QPushButton *StartBtn;
    QVBoxLayout *mainLayout;
    int port;                   //UDP端口号
    bool isStarted;
    QUdpSocket *udpSocket;
    QTimer *timer;
};

#endif // UDPSERVER_H
udpserver.cpp
#include "udpserver.h"

UdpServer::UdpServer(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle (tr("UDP Server"));
    TimerLabel = new QLabel(tr("计时器:"));
    TextLineEdit = new QLineEdit;
    StartBtn = new QPushButton("开始");

    mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget (TimerLabel);
    mainLayout->addWidget (TextLineEdit);
    mainLayout->addWidget (StartBtn);
    connect (StartBtn, SIGNAL(clicked(bool)), this, SLOT(StartBtnClicked()));

    port = 6666;                        //设置UDP的端口号参数,服务器定时向此端口发送广播信息
    isStarted = false;
    udpSocket = new QUdpSocket(this);   //创建一个QUdpSocket
    timer = new QTimer(this);
    connect (timer, SIGNAL(timeout()), this, SLOT(timeout()));
}

UdpServer::~UdpServer()
{

}
/*
 * StartBtnClicked()函数
*/
void UdpServer::StartBtnClicked ()
{
    if(!isStarted)
    {
        StartBtn->setText (tr("停止"));
        timer->start (1000);
        isStarted = true;
    }
    else
    {
        StartBtn->setText (tr("开始"));
        timer->stop ();
        isStarted = false;
    }
}
/*
 * timeout()函数完成了向端口发送广播信息的功能
 *
 * QHostAddress::Broadcast指定向广播地址发送
*/
void UdpServer::timeout ()
{
    QString msg = TextLineEdit->text ();
    int length = 0;
    if(msg == "")
    {
        return ;
    }
    if(length = udpSocket->writeDatagram (msg.toLatin1 (),
       msg.length (), QHostAddress::Broadcast, port) != msg.length ())
    {
        return;
    }
}

10.2.4 UDP客户端编程
udpclient.h

#ifndef UDPCLIENT_H
#define UDPCLIENT_H

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

class UdpClient : public QDialog
{
    Q_OBJECT

public:
    UdpClient(QWidget *parent = 0);
    ~UdpClient();
public slots:
    void CloseBtnClicked();
    void dataReceived();
private:
    QTextEdit *ReciveTextEdit;
    QPushButton *closeBtn;
    QVBoxLayout *mainLayout;
    int port;       //UDP端口
    QUdpSocket *udpSocket;
};

#endif // UDPCLIENT_H
udpclient.cpp
#include "udpclient.h"
#include <QMessageBox>

/*
 * connect (udpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived())): 链接QIODevice的readyRead()信号。
 * QUdpSocket也是一个I/O设备,从QIODevice继承而来,当有数据到达I/O设备时,发出readyRead()信号。
*/
UdpClient::UdpClient(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle (tr("UDP Client"));
    ReciveTextEdit = new QTextEdit;
    closeBtn = new QPushButton(tr("close"), this);
    mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget (ReciveTextEdit);
    mainLayout->addWidget (closeBtn);
    connect (closeBtn, SIGNAL(clicked()), this, SLOT(CloseBtnClicked()));
    port = 6666;
    udpSocket = new QUdpSocket(this);
    connect (udpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived()));

    bool result = udpSocket->bind (port);   //绑定到指定的端口上
    if(!result)
    {
        QMessageBox::information (this, tr("error"), tr("udp socked create error!"));
        return;
    }
}

UdpClient::~UdpClient()
{

}
/*
 * udpSocket->hasPendingDatagrams ():判断UdpSocket中是否有数据报可读
 * hasPendingDatagrams ()方法在在至少有一个数据报可读时返回true,否则返回false。
 *
 * QByteArray datagram;
 * datagram.resize (udpSocket->pendingDatagramSize ());
 * udpSocket->readDatagram (datagram.data (), datagram.size ());
 * 实现读取第一个数据报,pendingDatagramSize()可以获得第一个数据报的长度
 *
*/
void UdpClient::dataReceived ()
{
    while(udpSocket->hasPendingDatagrams ())
    {
        QByteArray datagram;
        datagram.resize (udpSocket->pendingDatagramSize ());
        udpSocket->readDatagram (datagram.data (), datagram.size ());
        QString msg = datagram.data ();
        ReciveTextEdit->insertPlainText (msg);
    }
}

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

服务器、客户端的运行界面



10.3 基于TCP的网络聊天室程序
传输控制协议(Transmission Control Protocol,TCP)是一种可靠、面向连接、面向数据流的传输协议,许多高层应用协议(包括HTTP、FTP等)都是以它为基础的,
TCP协议非常适合数据的连续传输。
TCP协议与UDP协议的差别建表

比较项 TCP UDP
是否连接 面向连接 无连接
传输可靠性 可靠 不可靠
流量控制 提供 不提供
工作方式 全双工 可以全双工
应用场合 大量数据 少量数据
速度

10.3.1  TCP协议工作原理
TCP协议能够为应用程序提供可靠的通信连接,使一台计算机发出的字节流无差错地送达网络上的其他计算机。因此,对可靠性要求高的数据通信系统往往使用TCP协议
传输数据,但在正式收发数据前,通信双方必须先建立连接。

10.3.2 TCP编程模型
首先启动服务器,一段时间后启动客户端,它与此服务器经历三次握手后建立连接。此后的一段时间内,客户端向服务器发送一个请求,服务器处理这个请求,并为客户端发回一个响应。这个过程一直持续下去,直到客户端为服务器发一个文件结束符,并关闭客户端连接,接着服务器也关闭服务器端的连接,结束运行或等待一个新的客户端连接。
Qt中通过QTcpSocket类和QTcpServer类实现TCP协议的编程。下面介绍如何实现一个基于TCP协议的网络聊天室应用,它同样也由客户端和服务器两部分组成。


TcpServer

tcpserver.h

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QListWidget>
#include <QGridLayout>
#include <QLineEdit>
#include "server.h"

class TcpServer : public QDialog
{
    Q_OBJECT

public:
    TcpServer(QWidget *parent = 0, Qt::WindowFlags f = 0);
    ~TcpServer();
private:
    QListWidget *ContentListWidget;
    QLabel *PortLabel;
    QLineEdit *PortLineEdit;
    QPushButton *CreateBtn;
    QGridLayout *mainLayout;
    int port;
    Server *server;
public slots:
    void slotCreateServer();
    void updateServer(QString, int);
};

#endif // TCPSERVER_H
tcpserver.cpp
#include "tcpserver.h"

TcpServer::TcpServer(QWidget *parent, Qt::WindowFlags f)
    : QDialog(parent)
{
    setWindowTitle (tr("TCP Server"));
    ContentListWidget = new QListWidget;
    PortLabel = new QLabel(tr("端口:"));
    PortLineEdit = new QLineEdit;
    CreateBtn = new QPushButton(tr("创建聊天室"));
    mainLayout = new QGridLayout(this);
    mainLayout->addWidget (ContentListWidget, 0, 0, 1 , 2);
    mainLayout->addWidget (PortLabel, 1, 0);
    mainLayout->addWidget (PortLineEdit, 1, 1);
    mainLayout->addWidget (CreateBtn, 2, 0, 1, 2);

    port = 8010;
    PortLineEdit->setText (QString::number (port));
    connect (CreateBtn, SIGNAL(clicked()), this, SLOT(slotCreateServer()));
}

TcpServer::~TcpServer()
{

}
/*
 * 创建一个TCP服务器
 * connect (server, SIGNAL(updateServer(QString,int)), this, SLOT(updateServer(QString,int)))
*/
void TcpServer::slotCreateServer ()
{
    server = new Server(this, port);
    connect (server, SIGNAL(updateServer(QString,int)), this, SLOT(updateServer(QString,int)));
    CreateBtn->setEnabled (false);
}

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

tcpclientsocket.h

#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H

#include <QTcpSocket>

class TcpClientSocket : public QTcpSocket
{
    Q_OBJECT        //添加宏(Q_OBJECT)是为了实现信号与槽的通信
public:
    TcpClientSocket(QObject *parent = 0);
signals:
    void updateClients(QString, int);
    void disconnected (int);
protected slots:
    void dataReceived();
    void slotDisconnected();
};

#endif // TCPCLIENTSOCKET_H
tcpclientsocket.cpp
#include "tcpclientsocket.h"

/*
 * connect (this, SIGNAL(readyRead()), this, SLOT(dataReceived())):readyRead()是QIODevice的signal,由
 * QTcpSocket继承而来。QIODevice是所有输入/输出设备的一个抽象类,其中定义了基本的接口,在Qt中,QTcpSocket也被看成一个
 * QIODevice,readyRead()信号在有数据到来时发出。
 *
 * connect (this, SIGNAL(disconnected()), this, SLOT(slotDisconnected())): disconnected()信号在断开连接时发出。
*/
TcpClientSocket::TcpClientSocket(QObject *parent)
{
    connect (this, SIGNAL(readyRead()), this, SLOT(dataReceived()));
    connect (this, SIGNAL(disconnected()), this, SLOT(slotDisconnected()));
}
/*
 * 当有数据到来时,触发dataReceived()函数,从套接字中将有效数据取出,然后发出updateClients()信号。updateClients()信号
 * 是通知服务器向聊天室内的所有成员广播信息。
*/
void TcpClientSocket::dataReceived ()
{
    while(bytesAvailable () > 0)
    {
        int length = bytesAvailable ();
        char buf[1024];
        read(buf, length);

        QString msg = buf;
        emit updateClients(msg,length);
    }
}
/*
 * 断开socket描述符
*/
void TcpClientSocket::slotDisconnected ()
{
    emit disconnected (this->socketDescriptor ());
}

server.h

#ifndef SERVER_H
#define SERVER_H

#include <QTcpServer>
#include <QObject>
#include "tcpclientsocket.h"
/*
 * QList<TcpClientSocket*> TcpClientSocketList用来保存与每个客户端连接的TcpClientSocket
*/
class Server : public QTcpServer
{
    Q_OBJECT    //添加宏是为了实现信号与槽的通信
public:
    Server(QObject *parent = 0, int port = 0);
    QList<TcpClientSocket*> tcpClientSocketList;
signals:
    void updateServer(QString, int);
public slots:
    void updateClients(QString, int);
    void slotDisconnected(int);
protected:
    void incomingConnection(int socketDescriptor);
};

#endif // SERVER_H

server.cpp

#include "server.h"

/*
 * listen(QHostAddress::Any, port)在指定的端口对任意地址进行监听
 * QHostAddress定义了几种特殊的IP地址,如QHostAddress::NULL表示一个空地址;QHostAddress::LocalHost表示IPv4的本机
 * 地址127.0.0.1;QHostAddress::LocallHostIPv6表示IPv6的本机地址;QHostAddress::Broadcast表示广播地址
 * 255.255.255.255;QHostAddress::Any表示IPv4的任意地址0.0.0.0;QHostAddress::AnyIPv6表示IPv6的任意地址。
*/
Server::Server(QObject *parent, int port)
    :QTcpServer(parent)
{
    listen(QHostAddress::Any, port);
}
/*
 * TcpClientSocket *tcpClientSocket = new TcpClientSocket(this):
 * 创建一个新的TcpClientSocket与客户端通信
 *
 * connect (tcpClientSocket, SIGNAL(updateClients(QString, int)),
 * this, SLOT(updateClients(QString,int))): 连接TcpClientSocket的updateClients信号。
 *
 * connect (tcpClientSocket, SIGNAL(disconnected(int)),
 * this, SLOT(slotDisconnected(int))): 连接TcpClientSocket的disconnected信号。
 *
 * tcpClientSocket->setSocketDescriptor (socketDescriptor): 将新创建的TcpClientSocket的
 * 套接字描述符指定为参数socketDescriptor
 *
 * tcpClientSocketList.append (tcpClientSocket):将TcpClientSocket加入客户端套接字列表以便管理。
 */
void Server::incomingConnection (int socketDescriptor)
{
    TcpClientSocket *tcpClientSocket = new TcpClientSocket(this);
    connect (tcpClientSocket, SIGNAL(updateClients(QString, int)),
             this, SLOT(updateClients(QString,int)));
    connect (tcpClientSocket, SIGNAL(disconnected(int)),
             this, SLOT(slotDisconnected(int)));
    tcpClientSocket->setSocketDescriptor (socketDescriptor);
    tcpClientSocketList.append (tcpClientSocket);
}

/*
 * emit updateServer (msg, length): 发出updateServer信号,用来通知服务器对话框更新相应的显示状态
 *
 * for(int i = 0; i < TcpClientSocketList.count (); i++): 实现信息的广播,tcpClientSocketList
 * 中保存了所有与服务器相连的TcpClientSocket对象。
*/
void Server::updateClients (QString msg, int length)
{
    emit updateServer (msg, length);
    for(int i = 0; i < tcpClientSocketList.count (); i++)
    {
        QTcpSocket  *item = tcpClientSocketList.at (i);
        if(item->write (msg.toLatin1 (), length) != length)
        {
            continue;
        }
    }
}
/*
 * slotDisconnected()函数实现从TcpClientSocketList列表中将断开连接的TcpClientSocket对象删除的功能。
*/
void Server::slotDisconnected (int descriptor)
{
    for(int i = 0; i < tcpClientSocketList.count (); i++)
    {
        QTcpSocket *item = tcpClientSocketList.at (i);
        if(item->socketDescriptor () == descriptor)
        {
            tcpClientSocketList.removeAt (i);
            return;
        }
    }
    return;
}

TcpClient

tcpclient.h

#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QDialog>
#include <QListWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QTcpSocket>
#include <QHostAddress>

class TcpClient : public QDialog
{
    Q_OBJECT

public:
    TcpClient(QWidget *parent = 0, Qt::WindowFlags f = 0);
    ~TcpClient();

private:
    QListWidget *contenListWidget;
    QLineEdit *sendLineEdit;
    QPushButton *sendBtn;
    QLabel *userNameLabel;
    QLineEdit *userNameLineEdit;
    QLabel *serverIPLabel;
    QLineEdit *serverIPLineEdit;
    QLabel *portLabel;
    QLineEdit *portLineEdit;
    QPushButton *enterBtn;
    QGridLayout *mainLayout;
    bool status;
    int port;
    QHostAddress *serverIP;
    QString userName;
    QTcpSocket *tcpSocket;

public slots:
    void slotEnter();
    void slotConnected();
    void slotDisConnected();
    void dataReceived();
    void slotSend();
};

#endif // TCPCLIENT_H

tcpclient.cpp

#include "tcpclient.h"
#include <QMessageBox>

TcpClient::TcpClient(QWidget *parent, Qt::WindowFlags f)
    : QDialog(parent)
{
    setWindowTitle (tr("TCP Client"));
    contenListWidget = new QListWidget;

    sendLineEdit = new QLineEdit;
    sendBtn = new QPushButton(tr("发送"));

    userNameLabel = new QLabel(tr("用户名:"));
    userNameLineEdit = new QLineEdit;

    serverIPLabel = new QLabel(tr("服务器地址:"));
    serverIPLineEdit = new QLineEdit;

    portLabel = new QLabel(tr("端口:"));
    portLineEdit = new QLineEdit;

    enterBtn = new QPushButton(tr("进入聊天室"));
    mainLayout = new QGridLayout(this);
    mainLayout->addWidget (contenListWidget, 0, 0, 1, 2);
    mainLayout->addWidget (sendLineEdit, 1, 0);
    mainLayout->addWidget (sendBtn, 1, 1);
    mainLayout->addWidget (userNameLabel, 2, 0);
    mainLayout->addWidget (userNameLineEdit, 2, 1);
    mainLayout->addWidget (serverIPLabel, 3, 0);
    mainLayout->addWidget (serverIPLineEdit, 3, 1);
    mainLayout->addWidget (portLabel, 4, 0);
    mainLayout->addWidget (portLineEdit, 4, 1);
    mainLayout->addWidget (enterBtn, 5, 0, 1, 2);

    status = false;
    port = 8010;
    portLineEdit->setText (QString::number (port));
    serverIP = new QHostAddress();

    connect (enterBtn, SIGNAL(clicked(bool)), this, SLOT(slotEnter()));
    connect (sendBtn, SIGNAL(clicked(bool)), this, SLOT(slotSend()));

    sendBtn->setEnabled (false);
}

TcpClient::~TcpClient()
{

}
/*
 * status表示当前的状态,true表示已经进入聊天室,false表示已经离开聊天室。根据status的状态决定是执行“进入”还是“离开”
 * serverIP->setAddress (ip): 用来判断给定的IP地址是否能够被正确解析
 * tcpSocket->connectToHost (*serverIP, port):与TCP服务器端连接,连接成功后发出connected()信号
 * QString msg = userName + tr(":Leave Char room"):构造一个离开聊天室的消息
*/
void TcpClient::slotEnter ()
{
    if(!status)
    {
        /*完成输入合法性检验*/
        QString ip = serverIPLineEdit->text ();
        if(!serverIP->setAddress (ip))
        {
            QMessageBox::information (this, tr("error"), tr("server ip address error!"));
            return;
        }
        if(userNameLineEdit->text () == "")
        {
            QMessageBox::information (this, tr("error"), tr("user name is error!"));
            return;
        }
        userName = userNameLineEdit->text ();
        /*创建一个QTcpSocket类对象,并将信号/槽连接起来*/
        tcpSocket = new QTcpSocket(this);
        connect (tcpSocket, SIGNAL(connected()), this, SLOT(slotConnected()));
        connect (tcpSocket, SIGNAL(disconnected()), this, SLOT(slotDisConnected()));
        connect (tcpSocket, SIGNAL(readyRead()), this, SLOT(dataReceived()));

        tcpSocket->connectToHost (*serverIP, port);
        status = true;
    }
    else
    {
        int length = 0;
        QString msg = userName + tr(":Leave Char room");
        if(length = tcpSocket->write (msg.toLatin1 (), msg.length ()) != msg.length ())
        {
            return ;
        }
        tcpSocket->disconnectFromHost ();
        status = false;
    }
}

void TcpClient::slotSend ()
{
    if(sendLineEdit->text () == "")
    {
        return;
    }
    QString msg = userName + ":" + sendLineEdit->text ();

    tcpSocket->write (msg.toLatin1 (), msg.length ());
    sendLineEdit->clear ();
}

void TcpClient::slotConnected ()
{
    sendBtn->setEnabled (true);
    enterBtn->setText (tr("离开"));

    int length = 0;
    QString msg = userName + tr(":Enter Chat Room");
    if((length = tcpSocket->write (msg.toLatin1 (), msg.length ())) != msg.length ())
    {
        return;
    }
}

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

void TcpClient::dataReceived ()
{
    while(tcpSocket->bytesAvailable () > 0)
    {
        QByteArray datagram;
        datagram.resize (tcpSocket->bytesAvailable ());
        tcpSocket->read (datagram.data (), datagram.size ());
        QString msg = datagram.data ();
        contenListWidget->addItem (msg.left (datagram.size ()));
    }
}

10.4 Qt网络应用开发初步
myHTTP
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtNetwork>

/*
 * 首先创建一个QNetworkAccessManager类的实例,它用来发送网络请求和接收应答。然后关联了管理器finished()信号和自定义的
 * 槽,每当网络应答结束时都会发送一个信号。最后使用了get()函数来发送一个网络请求,网络请求使用了QNetworkRequest类表示,
 * get()函数返回一个QNetworkReply对象。
 * 初始时隐藏下载进度条
*/
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    manger = new QNetworkAccessManager(this);
    connect (manger, SIGNAL(finished(QNetworkReply)), this, SLOT(replyFinished(QNetworkReply*)));
    manger->get (QNetworkRequest(QUrl("http://www.baidu.com")));
    ui->progressBar->hide ();
}

MainWindow::~MainWindow()
{
    delete ui;
}
/*
 * QNetworkReply继承自QIODevice类,所以可以像操作一般的I/O设备一样操作该类。这里使用了readAll()函数来读取所有的应答数据
 * 在完成数据的读取后,需要使用deleteLater()删除reply对象。
*/
void MainWindow::replyFinished (QNetworkReply *reply)
{
    QString all = reply->readAll ();
    ui->textBrowser->setText (all);
    reply->deleteLater ();
}
/*
 * get()函数发送网络请求,进行了QNetworkReply对象的几个信号和自定义槽的关联。其中,readyRead()信号继承自QIODevice类,
 * 每当有新的数据可以读取时,都会发送该信号;每当网络请求的下载进度更新时都会发送downloadProgress()信号,用于更新进度条:
 * 每当应答处理结束时,都会发送finished()信号,该信号与前面程序中QNetworkAccessManager类的finished()信号作用相同,
 * 只不过是发送者不同,参数也不同而已。
*/
void MainWindow::startRequest (QUrl url)
{
    reply = manger->get(QNetworkRequest(url));
    connect (reply, SIGNAL(readyRead()), this, SLOT(httpReadyRead()));
    connect (reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDataReadProgress(qint64,qint64)));
    connect (reply, SIGNAL(finished()), this, SLOT(httpFinished()));
}
/*
 * 首先判断是否创建了文件。如果是,则读取返回的所有数据,然后写入文件中。该文件是在后面的“下载”按钮的单击信号的槽中创建并打开的。
*/
void MainWindow::httpReadyRead ()
{
    if(file)
    {
        file->write (reply->readAll ());
    }
}

void MainWindow::updateDataReadProgress (qint64 bytesRead, qint64 totalButes)
{
    ui->progressBar->setMaximum (totalButes);
    ui->progressBar->setValue (bytesRead);
}

void MainWindow::httpFinished ()
{
    ui->progressBar->hide ();
    file->flush ();
    file->close ();
    reply->deleteLater ();
    delete file;
    file = 0;
}

void MainWindow::on_pushButton_clicked ()
{
    url = ui->lineEdit->text();
    QFileInfo info(url.path());
    QString fileName(info.fileName ());
    file = new QFile(fileName);
    if(!file->open (QIODevice::WriteOnly))
    {
        qDebug() << "file open error!";
        delete file;
        file = 0;
        return ;
    }
    startRequest (url);
    ui->progressBar->setValue (0);
    ui->progressBar->show ();
}

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:73069次
    • 积分:1218
    • 等级:
    • 排名:千里之外
    • 原创:48篇
    • 转载:0篇
    • 译文:0篇
    • 评论:24条
    最新评论