基于Qt编写TCP通讯程序测试程序之超级详细教程

转载自:http://liuyuananfang.blog.163.com/blog/static/843244212011118953799/

Qt编写TCP通讯程序 客户端与服务器端

 

说明:这是本人的练手之作,学习这个程序花费了大量时间,学习过程中借鉴了网友的成果。现在百忙之中将我的学习成果制作成学习教程供大家学习。同时我也希望大家都将自己的学习成果发布到网上,让大家共享。本教程文档和源代码我发布在CSDN网站http://download.csdn.net  大家可以搜索下载。CSDN网站下载链接http://download.csdn.net/detail/liuguangzhou123/3917541

由于我是初学者,该程序肯定会有BUG和许多需要改进的地方。有需要讨论的请加我的QQ:489478088,加我时请注明:QT学习讨论。或者加入我的QQ群84998716。

下载源代码后,由于不同版本的QT Creator创建的环境不同,可能无法正确运行,所以打开后,QT Creator可能会提示QT Creator发现其他环境的配置文件,问是否要载入,选择No。提示如下:

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

 

这时要选择NO。如果还无法运行,那你就只好新建一个工程,把我的源文件代码全部复制到你的新建工程里,再运行。

 

下面,开始讲解如何编写TCP测试软件:

第一步,创建工程,选中QtNetwork支持,基类选择Widget。如果创建工程中没有QtNetwork选项,在工程建好后,在工程文件*.pro文件里QT       += core gui语句后面加入

QT       += network

否则编译将出现“QNetworkInterface: No such file o”等错误

 

第二步,新建->文件或工程->选择QT项目->选择qt设计师界面类->选择Dialog without buttons,类名为client,完成。

编辑clientui界面,右击空白部分,将对象名称改为“client”,在界面添加标签按钮等,添加完成如下:

 

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

 

 

第三步,服务器地址行编辑器(Line Edit)的对象名改为clientIPlineEdit;

数据发送区行编辑器(Line Edit)的对象名改为clientMessagelineEdit;

端口行编辑器(Line Edit)的对象名改为clientPortlineEdit;

数据显示区行编辑器(Line Edit)的对象名改为messagetextBrowser;

发送按钮的对象名改为clientSendpushButton;

清空按钮的对象名改为cCleanpushButton;

连接按钮的对象名改为connectpushButton;

断开按钮的对象名改为disconnectpushButton;

Ready标签的对象名改为cStatuslabel。

第四步,编辑client.h文件

1.  添加:
#include <QNetworkInterface>

#include <QtNetwork>

2. 在函数class client : public QDialog添加

private:

    QList<QHostAddress> IPlist;

    QTcpServer *tcpServer;

    QTcpSocket *tcpClient;

    QString clientPort;

    QString serverIP;

    QString clientMessage;  //客户端发出的信息

    QString message;

    quint16 blockSize;  //存放文件的大小信息

    QTcpSocket *clientConnection;

private slots:

    void clientSendMessage();

    void updateStatus();

    void readMessage();  //接收数据

    void displayError(QAbstractSocket::SocketError);  //显示客户端错误

    void on_connectpushButton_clicked();

    void createServerSocket();

    void on_cCleanpushButton_clicked();

    void updateClientStatusConnect();

    void updateClientStatusDisconnect();

void on_disconnectpushButton_clicked();

 

第五步,编辑client.cpp文件

1.       添加#include <QMessageBox>

2.       在构造函数Widget::Widget(QWidget *parent)添加:

connect (ui->clientSendpushButton, SIGNAL(clicked()), this, SLOT(clientSendMessage()));

    ui->clientSendpushButton->setEnabled (false);

    ui->disconnectpushButton->setEnabled (false);

3.  打开client.ui界面,单击界面的空白处,将界面的Windows tittle的属性改为“客户端”。

 

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

 

分别右击“清空”、“连接”、“断开”按钮,选择“go to slot” ,信号选择clicked().各函数修改如下:

void client::on_cCleanpushButton_clicked()

{

    ui->messagetextBrowser->setText ("");

}

//客户端连接按钮槽函数
void client::on_connectpushButton_clicked()
{
    tcpClient = new QTcpSocket(this);
    connect (tcpClient, SIGNAL(readyRead()), this, SLOT(readMessage()));
    connect (tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
    connect (tcpClient, SIGNAL(connected()), this, SLOT(updateClientStatusConnect())); //更新状态
    connect (tcpClient, SIGNAL(disconnected()), this, SLOT(updateClientStatusDisconnect()));
    blockSize = 0;//初始化
    tcpClient->abort ();
    serverIP = ui->clientIPlineEdit->text ();
    clientPort = ui->clientPortlineEdit->text ();
    if(serverIP.isEmpty () || clientPort.isEmpty ())
    {
        QMessageBox::warning (this, tr("Warnning"), tr("服务器IP或端口号不能为空"));
        return;
    }
    tcpClient->connectToHost (serverIP, clientPort.toInt ()); //连接到主机

}

 

//客户端断开按钮槽函数
void client::on_disconnectpushButton_clicked()
{
    ui->clientMessagelineEdit->setText (tr("clientStop"));
    clientSendMessage ();
    ui->clientMessagelineEdit->setText (tr(""));
    tcpClient->close ();

}

 

4.实现各种函数

//客户端发送信息

void client::clientSendMessage ()

{

    clientMessage = ui->clientMessagelineEdit->text ();

    if(clientMessage.isEmpty ())

    {

        QMessageBox::warning (this, tr("Warnning"), tr("请输入发送数据"));

        return;

    }

    QByteArray block;

    QDataStream out(&block, QIODevice::WriteOnly);

    out.setVersion (QDataStream::Qt_4_7);

    out << (quint16) 0;

    out << clientMessage;

    out.device ()->seek (0);

    out << (quint16)(block.size () - sizeof(quint16));

    tcpClient->write (block);

    if(clientMessage.contains ("clientStop"))

        return;

    ui->messagetextBrowser->insertPlainText (tr("send message: %1 \n").arg (clientMessage));

}

 

//客户端错误提示
void client::displayError (QAbstractSocket::SocketError)
{
      QMessageBox::warning (this, tr("Warnning"), tcpClient->errorString ());
      tcpClient->close ();
      ui->connectpushButton->setEnabled (true);
      ui->disconnectpushButton->setEnabled (false);
      ui->clientSendpushButton->setEnabled (false);

}

 

//客户端更新连接状态
void client::updateClientStatusConnect ()
{
    ui->cStatuslabel->setText (tr("已连接"));
    ui->connectpushButton->setEnabled (false);
    ui->disconnectpushButton->setEnabled (true);
    ui->clientSendpushButton->setEnabled (true);
}
//客户端更新断开状态
void client::updateClientStatusDisconnect ()
{
    ui->cStatuslabel->setText (tr("已断开"));
    tcpClient->close ();
    ui->connectpushButton->setEnabled (true);
    ui->disconnectpushButton->setEnabled (false);
    ui->clientSendpushButton->setEnabled (false);

}

 

 

//需要自己添加的函数

void client::updateStatus()

{

    //暂时为空

}

//客户端读取信息

void client::readMessage ()

{

    QDataStream in(tcpClient);

    in.setVersion (QDataStream::Qt_4_7);

    if(blockSize == 0)

    {

        // 判断接收的数据是否有两字节,也就是文件的大小信息

        // 如果有则保存到 blockSize 变量中,没有则返回,继续接收数据

        if(tcpClient->bytesAvailable () < (int)sizeof(quint16))

            return ;

        in >> blockSize;

    }

    if(tcpClient->bytesAvailable () < blockSize)// 如果没有得到全部的数据,则返回,继续接收数据

        return;

    in >> message;

    if(message.contains ("serverStop")) //如果收到是服务器停止监听的信息

    {

        tcpClient->close ();

        ui->cStatuslabel->setText (tr("服务器断开连接"));

        blockSize = 0;

        return;

    }

    ui->messagetextBrowser->insertPlainText (tr("reveived message: %1 \n").arg (message));

    blockSize = 0;

}

//客户端创建套接字

void client::createServerSocket ()

{

    clientConnection = tcpServer->nextPendingConnection ();

    connect (clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));

}

 

这样,客户端就编写好了。

 

第六步,编辑main.h文件,该文件修改后如下:

 

#include <QtGui/QApplication>

#include "widget.h"

#include "client.h"

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    Widget w;

    //w.show();

    client w1;

    w1.show();

    return a.exec();

}

可以看到效果如下,注意标题栏改成“客户端”后可能出现乱码,原因是没有添加中文支持,如何添加中文支持将在后面讲解。

 

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

 

第七步,编辑widget.ui界面文件,添加标签和按钮,如下

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

 

服务器地址行编辑器(Combo box)的对象名改为serverIPcomboBox;

数据发送区行编辑器(Line Edit)的对象名改为serverMessagelineEdit;

端口行编辑器(Line Edit)的对象名改为serverPortlineEdit;

数据显示区行编辑器(Line Edit)的对象名改为servertextBrowser;

发送按钮的对象名改为serverSendpushButton;

清空按钮的对象名改为sCleanpushButton;

侦听按钮的对象名改为listenpushButton;

停止按钮的对象名改为stoppushButton;

Ready标签的对象名改为statuslabel。

 

第八步,编辑widget.h文件

1.  添加
#include <QNetworkInterface>

#include <QtNetwork>

2.       在类class Widget : public QWidget里面添加

 

public:

    explicit Widget(QWidget *parent = 0);

    ~Widget();

    void getLocalIP();//获取本机IP地址

private:

    Ui::Widget *ui;

    QList<QHostAddress> IPlist;

    QTcpServer *tcpServer;

    QString serverPort;

    QString serverIP;

    QString serverMessage;  //服务端发出的信息

    QString message;

    quint16 blockSize;  //存放文件的大小信息

    QTcpSocket *clientConnection;

private  slots:

    void serverSendMessage();

    void on_listenpushButton_clicked();

    void updateStatus();

    void displayErrorS(QAbstractSocket::SocketError); //显示服务端错误

    void screateServerSocket();

    void on_sCleanpushButton_clicked();

    void on_stoppushButton_clicked();

void serverReadMessage();

 

第九步,编辑widget.cpp文件

1.添加#include <QMessageBox>

2.构造函数修改如下:

Widget::Widget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::Widget)

{

    ui->setupUi(this);

    getLocalIP ();

    clientConnection = NULL;

    connect (ui->serverSendpushButton, SIGNAL(clicked()), this, SLOT(serverSendMessage()));

    //ui->serverSendpushButton->setEnabled (false);

    ui->serverSendpushButton->setEnabled (true);

    ui->stoppushButton->setEnabled (false);

}

 

3.  打开widget.ui界面,单击界面的空白处,将界面的Windows tittle的属性改为“服务器端”。

 

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

 

在widget.ui界面分别右击 “清空”、“侦听”、“停止”按钮,选择“go to slot” ,信号选择clicked().各函数修改如下:

 

//侦听按钮槽函数

void Widget::on_listenpushButton_clicked()

{

    serverPort = ui->serverPortlineEdit->text ();

    serverIP = ui->serverIPcomboBox->currentText ();

    if(serverPort.isEmpty ())

    {

        QMessageBox::warning (this, tr("Warnning"), tr("端口号不能为空"));

        return;

    }

    tcpServer = new QTcpServer(this);

    if(!tcpServer->listen (QHostAddress::Any, serverPort.toInt ()))

    {

        QMessageBox::warning (this, tr("Warnning"), tcpServer->errorString ());

        close ();

    }

    connect (tcpServer, SIGNAL(newConnection()), this, SLOT(updateStatus()));

    connect (tcpServer, SIGNAL(newConnection()), this, SLOT(screateServerSocket()));  //有新的连接到来,则开始创建套接字

    ui->statuslabel->setText (tr("开始监听"));

    ui->listenpushButton->setEnabled (false);

    ui->stoppushButton->setEnabled (true);

}

 

//清空按钮槽函数

void Widget::on_sCleanpushButton_clicked()

{

    ui->servertextBrowser->setText ("");

}

//停止按钮槽函数

void Widget::on_stoppushButton_clicked()

{

    ui->serverMessagelineEdit->setText (tr("serverStop")); //发送服务端停止监听信息

    serverSendMessage ();

    ui->serverMessagelineEdit->setText (tr(""));

    tcpServer->close ();

    ui->statuslabel->setText (tr("停止监听"));

    ui->listenpushButton->setEnabled (true);

    ui->stoppushButton->setEnabled (false);

    ui->serverSendpushButton->setEnabled (false);

}

 

4.  将其余函数补充完整:

 

//服务器端读取信息

void Widget::serverReadMessage ()

{

    QDataStream in(clientConnection);

    in.setVersion (QDataStream::Qt_4_7);

    if(blockSize == 0)

    {

        // 判断接收的数据是否有两字节,也就是文件的大小信息

        // 如果有则保存到 blockSize 变量中,没有则返回,继续接收数据

        if(clientConnection->bytesAvailable () < (int)sizeof(quint16))

            return ;

        in >> blockSize;

    }

    if(clientConnection->bytesAvailable () < blockSize)// 如果没有得到全部的数据,则返回,继续接收数据

        return;

    in >> message;

    if(message.contains ("clientStop")) //如果收到是客户端断开连接的信息

    {

        clientConnection->close ();

        ui->serverSendpushButton->setEnabled (false);

        ui->statuslabel->setText (tr("客户端断开连接"));

        blockSize = 0;

        return;

    }

    ui->servertextBrowser->insertPlainText (tr("reveived message: %1 \n").arg (message));

    blockSize = 0;

}

//服务器端发送信息

void Widget::serverSendMessage ()

{

    if(!clientConnection) //判断有没有实例化

        if(!(clientConnection = tcpServer->nextPendingConnection ())) //没有客户端连接

        {

            return;

        }

    serverMessage = ui->serverMessagelineEdit->text ();

    if(serverMessage.isEmpty ())

    {

        QMessageBox::warning (this, tr("Warnning"), tr("请输入发送数据"));

        return;

    }

    QByteArray block;

    QDataStream out(&block, QIODevice::WriteOnly);

    out.setVersion (QDataStream::Qt_4_7);

    out << (quint16) 0;

    out << serverMessage;

    out.device ()->seek (0);

    out << (quint16) (block.size () - sizeof(quint16));

    connect(clientConnection, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayErrorS(QAbstractSocket::SocketError)));

    clientConnection->write (block);

    if(serverMessage.contains ("serverStop"))

        return;

    ui->servertextBrowser->insertPlainText (tr("send message: %1 \n").arg (serverMessage));

}

void Widget::getLocalIP ()

{

    IPlist = QNetworkInterface::allAddresses ();

    foreach(QHostAddress IP, IPlist)

    {

        ui->serverIPcomboBox->addItem (IP.toString ());

    }

}

 

//需要自己添加的函数

void Widget::updateStatus()

{

    //暂时为空

}

//服务器端创建套接字

void Widget::screateServerSocket ()

{

    ui->statuslabel->setText(tr("客户端已连接"));

    ui->serverSendpushButton->setEnabled (true);

    clientConnection = tcpServer->nextPendingConnection ();

    blockSize = 0;//初始化

    connect (clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));

    connect (clientConnection, SIGNAL(readyRead()), this, SLOT(serverReadMessage()));

}

 //服务器端错误提示

void Widget::displayErrorS (QAbstractSocket::SocketError)

{

  //  QMessageBox::warning (this, tr("Warnning"), clientConnection->errorString ());

    ui->servertextBrowser->insertPlainText (clientConnection->errorString ());

    connect (clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));

    clientConnection->disconnectFromHost ();

    ui->statuslabel->setText (tr("断开连接"));

}

第十步,编辑main.cpp文件,修改好后如下:

#include <QtGui/QApplication>

#include <QTextCodec>   //添加中文支持2-1

#include "widget.h"

#include "client.h"

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QTextCodec::setCodecForTr(QTextCodec::codecForLocale());//添加中文支持2-2

    Widget w;

    w.show();

    client w1;

    w1.show();

    return a.exec();

}

其中,两条语句便可完成中文支持。运行程序,如下:

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

 

基于Qt编写TCP通讯程序测试程序之超级详细教程 - liuyuananfang - liuyuananfang的博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值