Qt网络编程之TCP通信(一)聊天窗口的实现

9 篇文章 0 订阅
2 篇文章 1 订阅

0 关于TCP通信

TCP:即传输控制协议,它提供了一种面向连接的、可靠的、字节流服务。
所谓 面向连接 指的是在数据交换之前必须先建立连接,然后实现点对点通信;
可靠性体现在它的丢包重发机制,另外数据被分割为数据块发送,接收时要通过首部与数据的校验和;
字节流体现在TCP发送、接收存在缓冲区,故通信双方读写操作次数可以不同.
关于更多TCP的内容比如建立、断开连接,报文段等内容,网上有很多教程,本文着重讲解Qt下TCP通信过程。


一、Qt下TCP通信流程

TCP中通信双方为别为客户端服务器,Qt中为TCP通信提供了两个类QTcpServerQTcpSocket,分别为监听套接字(监听有无连接请求,用于服务器端)和通信套接字(发送、读取数据)。使用时,应在项目文件中添加QT += network,然后再添加相应的头文件。

首先介绍服务器端

  1. 由于服务器端既要监听有无客户端的连接请求,又要发送和接收数据,故需要创建监听套接字对象通信套接字对象
  2. 然后监听套接字开始监听IP地址和端口是否有连接请求:
    tcpServer->listen(QHostAddrss::Any,8888);
    
    其中,QTcpServer::listen(QHostAddress adr,quint16 port)即为监听函数,它的两个参数分别为绑定的IP地址和端口号,该函数有bool类型返回值,返回true表明监听建立成功,返回false表明监听建立失败;
  3. 监听建立成功后,当有新的连接发起时,会触发服务器newConnection()信号,所以要定义一个槽函数对请求连接的客户端进行操作,并将该槽函数与newConnection()进行连接:
    connect(TcpServer,SIGNAL(newConnection()),this,SLOT(mconnect()));
    
  4. 在槽函数中取出客户端的通信套接字,如果客户端有数据发送过来,则会触发通信套接字的readyRead()信号,故需要顶一个槽函数来读取数据,并将该槽函数与readyRead()连接:
    void Widget::mconnect()
    {
        qDebug()<<"connect sucess";
        TcpSocket = TcpServer->nextPendingConnection();
        connect(TcpSocket,SIGNAL(readyRead()),this,SLOT(RcvData()));
        QString ip = TcpSocket->peerAddress().toString();
        quint16 port = TcpSocket->peerPort();
        ui->textEdit_read->setText(QString("[%1:%2]:成功连接").arg(ip).arg(port));
    }
    
    其中TcpSocket = TcpServer->nextPendingConnection();需要注意一下,由于服务器可能接收到多个客户端的连接请求,所以有新连接发起时,系统会将新连接放入队列PendingConnection中,故取出通信套接字时秩序调用TcpServer->nextPendingConnection(),便会返回下一个可用的通信套接字。
    另外,QTcpServer还提供了几个函数对该队列进行操作:
    bool hasPendingConnections(); //是否有可用新连接
    int maxPendingConnections();  //返回队列的最大值
    void setMaxPendingConnections(int numConnections); //设置队列最大值
    QTcpSocket *nextPendingConnection(); //返回下一个可用的通信套接字
    
  5. RcvData()中读取数据并显示:
    void Widget::RcvData()
    {
        QString msg = TcpSocket->readAll();
        ui->textEdit_read->setText(msg);
    }
    
    这样,服务器接收数据便完成了,发送数据很简单:
  6. 发送数据:
    void Widget::on_pushButton_send_clicked()
    {
        QString str = ui->textEdit_write->toPlainText();
        TcpSocket->write(str.toUtf8().data());
    }
    

接下来是客户端

  1. 相比较服务器而言,客户端由于只有发起连接请求与数据收发的功能,而无需监听,所以只需要创建通信套接字对象
  2. 首先是发起连接请求 ,QTcpSocket提供了connectToHost(QHostAddress adr,quint16 port)函数,用于向指定的Ip地址和端口发起连接请求:
	void Client::on_pushButton_connect_clicked()
	{
	    socket->abort();       //发起连接之前先断开原来的连接
	    QString ip = ui->lineEdit_ip->text();
	    quint16 port = ui->lineEdit_port->text().toInt();
	    qDebug()<<ip<<port;
	    socket->connectToHost(QHostAddress(ip),port);
	}
  1. 同样,发送数据只需用到QtcpSocket::write(QByteArry data)函数:
     void Client::on_pushButton_send_clicked()
     {
         QString txt = ui->textEdit_write->toPlainText();
         socket->write(txt.toUtf8().data());
     }
    
  2. 接收数据放在newConnection()对应的槽函数中:
    void Client::recivedata()
    {
        QString datas=socket->readAll();
        ui->textEdit_read->setText(datas);
    }
    

二、程序源码

项目结构:
在这里插入图片描述
ui界面–Widget:
在这里插入图片描述
在这里插入图片描述
ui界面–Client:
在这里插入图片描述
在这里插入图片描述
client.h:

#ifndef CLIENT_H
#define CLIENT_H

#include <QWidget>
#include <QTcpSocket>

namespace Ui {
class Client;
}

class Client : public QWidget
{
    Q_OBJECT

public:
    explicit Client(QWidget *parent = 0);
    ~Client();

private slots:

    void displayError(QAbstractSocket::SocketError);

    void on_pushButton_connect_clicked();

    void recivedata();

    void on_pushButton_send_clicked();

private:
    Ui::Client *ui;
    QTcpSocket *socket;
};

#endif // CLIENT_H

client.cpp:

#include "client.h"
#include "ui_client.h"
#include <QHostAddress>
#include <QDebug>
Client::Client(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Client)
{
    ui->setupUi(this);
    socket = new QTcpSocket(this);
    connect(socket,SIGNAL(readyRead()),this,SLOT(recivedata()));
    connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),
        this,SLOT(displayError(QAbstractSocket::SocketError)));
}

Client::~Client()
{
    delete ui;
}

void Client::on_pushButton_connect_clicked()
{
    socket->abort();
    QString ip = ui->lineEdit_ip->text();
    quint16 port = ui->lineEdit_port->text().toInt();
    qDebug()<<ip<<port;
    socket->connectToHost(QHostAddress(ip),port);
}
void Client::displayError(QAbstractSocket::SocketError)
{
   qDebug()<<socket->errorString();
   socket->close();
}
void Client::recivedata()
{
    QString datas=socket->readAll();
    ui->textEdit_read->setText(datas);
}

void Client::on_pushButton_send_clicked()
{
    QString txt = ui->textEdit_write->toPlainText();
    socket->write(txt.toUtf8().data());
}

widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void init();

    void newListen();

    void mconnect();

    void RcvData();

    void on_pushButton_send_clicked();

    void on_pushButton_close_clicked();

    void displayError(QAbstractSocket::SocketError);

private:
    Ui::Widget *ui;
    QTcpServer *TcpServer;
    QTcpSocket *TcpSocket;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QList>
#include <QNetworkInterface>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    init();

}
Widget::~Widget()
{
    delete ui;
}
void Widget::init()
{
    TcpServer = new QTcpServer(this);
    TcpSocket = new QTcpSocket(this);
    newListen();
    connect(TcpServer,SIGNAL(newConnection()),this,SLOT(mconnect()));
    connect(TcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),
                this,SLOT(displayError(QAbstractSocket::SocketError)));

}
void Widget::newListen()
{
    if(!TcpServer->listen(QHostAddress::Any,8000))
    {
        qDebug()<<TcpServer->errorString();
        close();
        return;
    }
}
void Widget::on_pushButton_send_clicked()
{
    QString str = ui->textEdit_write->toPlainText();
    TcpSocket->write(str.toUtf8().data());
}

void Widget::on_pushButton_close_clicked()
{
    TcpSocket->disconnectFromHost();
    TcpSocket->close();
}
void Widget::mconnect()
{
    qDebug()<<"connect";
    TcpSocket = TcpServer->nextPendingConnection();
    connect(TcpSocket,SIGNAL(readyRead()),this,SLOT(RcvData()));
    QString ip = TcpSocket->peerAddress().toString();
    quint16 port = TcpSocket->peerPort();
    ui->textEdit_read->setText(QString("[%1:%2]:成功连接").arg(ip).arg(port));
}
void Widget::displayError(QAbstractSocket::SocketError)
{
    qDebug()<<TcpSocket->errorString();
        TcpSocket->close();
}
void Widget::RcvData()
{
    QString msg = TcpSocket->readAll();
    ui->textEdit_read->setText(msg);
}

main.cpp

#include "widget.h"
#include <QApplication>
#include "client.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    Client wr;
    wr.show();
    w.show();

    return a.exec();
}

三、测试情况

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
项目链接:https://github.com/Mr-Yslf/BlogResources.git

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值