Qt学习(十四)—— 网络通信之TCP

本文详细介绍在QT环境下如何实现TCP通信,包括服务端监听、客户端连接、数据收发及连接管理等核心过程。通过具体代码示例,展示了QTcpServer和QTcpSocket的使用方法。

Linux下的TCP通信过程

在这里插入图片描述

在这里插入图片描述
【转自:Linux C/C++ TCP Socket通信实例

QT下的TCP通信过程

在这里插入图片描述

listen()【服务端】

Qt中,服务端的监听套接字不再是socket,而是QTcpServer和QTcpSocket。此外,它将绑定bind()监听listen()合为一个函数,即监听listen(),在调用的同时指定主机地址和端口号。

connectToHost()【客户端】

而客户端的通信套接字也不再是socket,而是QTcpSocket,连接也不是connect(),而是connectToHost(),意为主动向主机(服务端)发起连接。

newConnection()【服务端】

如果客户端发起连接并且成功,服务端将会收到一个信号。在Qt中,服务端不再是由accept()来确定连接的建立,而是会触发一个newConnection()信号。既然是信号,就要有与之对应的槽函数。槽函数中主要的工作是取出建立好的套接字QTcpSocket(它是真正的通信套接字)。

write()【服务端/客户端】

客户端用write()发送数据,如果数据传送成功,对方的通信套接字会触发readyRead()信号(继承自QIODevice类的信号),说明可以开始读数据了,我们需要在对应的槽函数中做接收处理。反过来,服务端给客户端发信息也是一样的。

connected() & disconnected()【服务端/客户端】

只要成功与对方建立连接,无论是服务端还是客户端,通信套接字都会自动触发connected()信号,而如果对方主动断开连接,通信套接字会自动触发disconnected()信号,我们可以利用这两个信号作出一些提示,比如在客户端与服务器端成功连接后,在客户端窗口输出“已成功连接服务器!”或者“已与服务器断开连接!”。

P.S:

  • 用到网络编程时,Qt的.pro文件中要加上 QT += network。如果不加的话,关于TCP的头文件都是不会有提示的。
  • 服务端有两个套接字,写这部分代码的时候要记得加上<QTcpServer><QTcpSocket>

listen()是服务端通过QTcpServer对象调用的,它的参数是主机地址和端口号。主机地址默认是QHostAddress::Any —— 绑定当前网卡所有的IP地址。下面我们在服务端创建一个监听套接字:

p_tcpServer=new QTcpServer(this);           
p_tcpSocket=new QTcpSocket(this);           
//服务端监听                                     
p_tcpServer->listen(QHostAddress::Any,8888);

只要客户端和服务端建立连接,服务端就会产生一个newConnection()信号,在相应的槽函数中调用QTcpServer的nextPendingConnection()取出建立好连接的套接字(取出来就是一个指针,不需要动态分配空间)。

如果我们想在服务端窗口显示当前是谁给我们发消息过来(获取对方的IP地址和端口号),也可以实现,这些信息就包含在套接字中。只要调用通信套接字QTcpSocket对象的peerAddress()方法就可以获取对方的IP地址,但是到这一步获取到的IP地址是我们看不懂的,还需要再调用toString()来转化成常见的xxx.xxx.xxx.xxx格式的IP地址。端口是用Qt中封装好的内置类型qint16来表示的,也是需要用调用QTcpSocket对象的peerPort()方法来获取。

//服务端连接                                                                         
connect(p_tcpServer,&QTcpServer::newConnection,this,&ServerWidget::fetchSocket);
//fetchSocket的实现
void ServerWidget::fetchSocket(){
   
   
    //从新建的连接中取出套接字
    p_tcpSocket=p_tcpServer->nextPendingConnection();
    //获取IP地址
    QString ip=p_tcpSocket->peerAddress().toString();
    //获取端口号
    qint16 port=p_tcpSocket->peerPort();
    //连接信息输出到文本框中
    p_recvBox->setText(QString("[%1:%2] 连接成功!").arg(ip).arg(port));
    //从套接字中读取数据
    connect(p_tcpSocket,&QTcpSocket::readyRead,this,&ServerWidget::readData);
}

实现效果:
在这里插入图片描述
TCP通信的机制与打电话很相似,只要获取到对方的通信套接字QTcpSocket,后面往通信套接字里写入的数据都会被发送到指定IP地址下指定的端口,就好像双方之间建立了一个通道。下面让我们来看看客户端是怎么与服务端通过通信套接字建立连接的:

//建立连接                                                                           
connect(p_connectButton,&QPushButton::clicked,this,&ClientWidget::getConnection);
//getConnection的实现
void ClientWidget::getConnection(){
   
   
    //获取服务器IP地址和端口号,这是我们通过文本框输入的
    QString m_ip=p_ipEdit->text();
    qint16 m_port=p_portEdit->text().toInt();
    //主动与服务器建立连接
    p_tcpSocket->connectToHost(QHostAddress(m_ip),m_port);
}

只要客户端connectToHost()中的IP地址与端口号和服务器端listen()中监听的IP地址与端口号是一致的,那么它们之前的通道就建立起来了。建立完连接后,当然就是进行消息的收发了。

往通信套接字中写数据调用的是write()方法,它的参数可以是char *,也可以是QByteArray字节数组,但我们从文本编辑区获取的内容一般是QString类型的,所以这里还要进行一个转化。

//发送消息                                                                   
connect(p_sendButton,&QPushButton::clicked,this,&ServerWidget::sendData);
//sendData的实现
void ServerWidget::sendData(){
   
   
    //先进行判断,是否已经获取套接字
    if(p_tcpSocket==NULL){
   
   
        return;
    }
    //获取文本编辑框的内容
    QString str=p_sendBox->toPlainText();
    //往套接字中写入数据
    p_tcpSocket->write(str.toUtf8().data());
}

当服务端或客户端往通信套接字中写入数据后,另一端会自动触发readyRead()信号,我们可以在与之对应的槽函数里面,把套接字中的数据取出并在窗口中展示:

//接收从服务端发来的数据                                                            
connect(p_tcpSocket,&QTcpSocket::readyRead,this,&ClientWidget::readData);
//readData的实现
void ClientWidget::readData(){
   
   
    QByteArray array=p_tcpSocket->readAll();
    QString str="服务端:";
    str.append(QString(array));
    p_recvBox->append(str);
}

实现效果:
在这里插入图片描述
(这个效果要配合客户端一起使用才能出来。)

当客户端或服务端想主动断开连接时,需要调用QTcpSocket的disconnectFromHost()方法,然后调用QTcpSocket的close()方法。断开连接后,再往QTcpSocket套接字中写入数据就不会再被另一方获取了,除非重新建立连接:

//关闭连接                                                                           
connect(p_closeButton,&QPushButton::clicked,this,&ServerWidget::closeConnection);
//closeConnection的实现
void ServerWidget::closeConnection(){
   
   
    //先进行判断,是否已经获取套接字
    if(p_tcpSocket==NULL){
   
   
        return;
    }
    //主动和客户端断开连接
    p_tcpSocket->disconnectFromHost();
    p_tcpSocket->close();
    //断开后,将套接字重新赋值为NULL
    p_tcpSocket=NULL;
}

P.S:这里如果不对套接字是否为空进行判断,在套接字为空的情况下去断开连接,会使程序异常结束。而在断开连接之后,需要将套接字指针置为NULL,否则你不知道它会指向哪里。

服务端的实现

//ServerWidget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QTextEdit>
#include <QWidget>

class ServerWidget : public QWidget
{
   
   
    Q_OBJECT

public:
    ServerWidget(QWidget *parent = 0);
    ~ServerWidget();
private:
    QTcpServer *p_tcpServer;    //监听套接字
    QTcpSocket *p_tcpSocket;    //通信套接字
    QTextEdit *p_recvBox;   //接收消息文本框,只读
    QTextEdit *p_sendBox;   //发送消息文本框
protected:
    void ServerWidget::fetchSocket();   //获取通信套接字
    void ServerWidget::readData();  //从套接字中读取数据
    void ServerWidget::sendData();  //往套接字中写入数据
    void ServerWidg
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值