目录
网络模块介绍
TCP介绍
TCP 服务端应用实例
TCP 客户端应用实例
运行结果:
网络模块介绍
Qt
网络模块为我们提供了编写
TCP / IP
客户端和服务器的类。它提供了较低级别的类,例
如代表低级网络概念的
QTcpSocket
,
QTcpServer
和
QUdpSocket
,以及诸如
QNetworkRequest
,
QNetworkReply
和
QNetworkAccessManager
之类的高级类来执行使用通用协议的网络操作。
它
还提供了诸如
QNetworkConfiguration
,
QNetworkConfigurationManager
和
QNetworkSession
等类,
实现承载管理。
想要在程序中使用
Qt
网络模块,我们需要在
pro
项目配置文件里增加下面的一条语句。
QT += network
TCP介绍
TCP
协议(
Transmission Control Protocol
)全称是传输控制协议是一种
面向连接的、可靠的、
基于字节流
的传输层通信协议。
TCP
通信必
须先建立
TCP
连接
,通信端分为客户端和服务端。服务端通过监听某个端口
来监听是否有客户端连接到来,如果有连接到来,则建立新的
socket
连接;客户端通过
ip
和
port
连接服务端,当成功建立连接之后,就可进行数据的收发了。需要注意的是,在
Qt
中,
Qt
把
socket
当成输入输出流来对待的,数据的收发是通过
read()
和
write()
来进行的,需要与我
们常见的
send()
与
recv()
进行区分。
TCP
客户端与服务端通信示意图如下。
TCP 服务端应用实例
本例大体流程
首先获取本地
IP
地址。创建一个
tcpSocket
套接字,一个
tcpServer
服务端。点击监听即监听本
地的主机
IP
地址和端口,同时等待服务端的连接。此程序需要结合客户端一起使用,可实现多个客户端的传播。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
/* tcp 服务器 */
QTcpServer *tcpServer;
/* 通信套接字 */
QTcpSocket *tcpSocket;
/* 存储本地的 ip 列表地址 */
QList<QHostAddress> IPlist;
/* 获取本地的所有 ip */
void getLocalHostIP();
private slots:
/* 客户端连接处理槽函数 */
void clientConnected();
/* 开始监听槽函数 */
void startListen();
/* 停止监听槽函数 */
void stopListen();
/* 清除文本框时的内容 */
void clearTextBrowser();
/* 接收到消息 */
void receiveMessages();
/* 发送消息 */
void sendMessages();
/* 连接状态改变槽函数 */
void socketStateChange(QAbstractSocket::SocketState);
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(this);
ui->spinBox->setRange(10000, 99999);
ui->pushButton_5->setEnabled(false);
getLocalHostIP();
/* 信号槽连接 */
connect(ui->pushButton, SIGNAL(clicked()),this, SLOT(startListen()));
connect(ui->pushButton_5, SIGNAL(clicked()),this, SLOT(stopListen()));
connect(ui->pushButton_6, SIGNAL(clicked()),this, SLOT(clearTextBrowser()));
connect(ui->pushButton_4, SIGNAL(clicked()),this, SLOT(sendMessages()));
connect(tcpServer, SIGNAL(newConnection()),this, SLOT(clientConnected()));
}
Widget::~Widget()
{
delete ui;
}
/* 新的客户端连接 */
void Widget::clientConnected()
{
/* 获取客户端的套接字 */
tcpSocket = tcpServer->nextPendingConnection();
/* 客户端的 ip 信息 */
QString ip = tcpSocket->peerAddress().toString();
/* 客户端的端口信息 */
quint16 port = tcpSocket->peerPort();
/* 在文本浏览框里显示出客户端的连接信息 */
ui->textBrowser->append("客户端已连接");
ui->textBrowser->append("客户端 ip 地址:" + ip);
ui->textBrowser->append("客户端端口:" + QString::number(port));
connect(tcpSocket, SIGNAL(readyRead()),this, SLOT(receiveMessages()));
connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,
SLOT(socketStateChange(QAbstractSocket::SocketState)));
}
void Widget::getLocalHostIP()
{
QList<QNetworkInterface> list= QNetworkInterface::allInterfaces();
/* 遍历 list */
foreach (QNetworkInterface interface, list) {
/* QNetworkAddressEntry 类存储 IP 地址子网掩码和广播地址 */
QList<QNetworkAddressEntry> entryList= interface.addressEntries();
/* 遍历 entryList */
foreach (QNetworkAddressEntry entry, entryList) {
/* 过滤 IPv6 地址,只留下 IPv4 */
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
ui->comboBox->addItem(entry.ip().toString());
/* 添加到 IP 列表中 */
IPlist<<entry.ip();
}
}
}
}
/* 开始监听槽函数 */
void Widget::startListen()
{
/* 需要判断当前主机是否有IP项 */
if (ui->comboBox->currentIndex() != -1) {
qDebug()<<"start listen"<<endl;
tcpServer->listen(IPlist[ui->comboBox->currentIndex()],ui->spinBox->value());
/* 设置按钮与下拉列表框的状态 */
ui->pushButton->setEnabled(false);
ui->pushButton_5->setEnabled(true);
ui->comboBox->setEnabled(false);
ui->spinBox->setEnabled(false);
/* 在文本浏览框里显示出服务端 */
ui->textBrowser->append("服务器IP地址:"+ ui->comboBox->currentText());
ui->textBrowser->append("正在监听端口:"+ ui->spinBox->text());
}
}
/* 停止监听槽函数 */
void Widget::stopListen()
{
qDebug()<<"stop listen"<<endl;
/* 停止监听 */
tcpServer->close();
/* 如果是连接上了也应该断开,如果不断开客户端还能继续发送信息,
* 因为socket未断开,还在监听上一次端口 */
if (tcpSocket->state() == tcpSocket->ConnectedState)
tcpSocket->disconnectFromHost();
/* 设置按钮与下拉列表框的状态 */
ui->pushButton_5->setEnabled(false);
ui->pushButton->setEnabled(true);
ui->comboBox->setEnabled(true);
ui->spinBox->setEnabled(true);
/* 将停止监听的信息添加到文本浏览框中 */
ui->textBrowser->append("已停止监听端口:" + ui->spinBox->text());
}
/* 清除文本框时的内容 */
void Widget::clearTextBrowser()
{
/* 清除文本浏览器的内容 */
ui->textBrowser->clear();
}
/* 接收到消息 */
void Widget::receiveMessages()
{
/* 读取接收到的消息 */
QString messages = "客户端:" + tcpSocket->readAll();
ui->textBrowser->append(messages);
}
/* 发送消息 */
void Widget::sendMessages()
{
QList <QTcpSocket *> socketList=tcpServer->findChildren<QTcpSocket *>();
qDebug()<<"tcpSocket 数量: "<<socketList.count()<<endl;
if(socketList.count()== 0){
ui->textBrowser->append("请先与客户端连接!");
return;
}
foreach(QTcpSocket *tmpTcpSocket,socketList){
tmpTcpSocket->write(ui->lineEdit->text().toUtf8());
}
ui->textBrowser->append("服务端 "+ui->lineEdit->text());
}
/* 连接状态改变槽函数 */
void Widget::socketStateChange(QAbstractSocket::SocketState state)
{
switch (state) {
case QAbstractSocket::UnconnectedState:
ui->textBrowser->append("scoket状态:客户端断开连接");
tcpSocket->deleteLater();
break;
case QAbstractSocket::ConnectedState:
ui->textBrowser->append("scoket状态:客户端已连接");
break;
case QAbstractSocket::ConnectingState:
ui->textBrowser->append("scoket状态:ConnectingState");
break;
case QAbstractSocket::HostLookupState:
ui->textBrowser->append("scoket状态:HostLookupState");
break;
case QAbstractSocket::ClosingState:
ui->textBrowser->append("scoket状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
ui->textBrowser->append("scoket状态:ListeningState");
break;
case QAbstractSocket::BoundState:
ui->textBrowser->append("scoket状态:BoundState");
break;
default:
break;
}
}
TCP 客户端应用实例
本例大体流程:
首先获取本地
IP
地址。创建一个
tcpSocket
套接字,然后用
tcpSocket
套接字使用
connectToHost 函数连接服务端的主机 IP
地址和端口,即可相互通信。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
/* 通信套接字 */
QTcpSocket *tcpSocket;
/* 存储本地的ip列表地址 */
QList<QHostAddress> IPlist;
/* 获取本地的所有ip */
void getLocalHostIP();
private slots:
/* 连接 */
void toConnect();
/* 断开连接 */
void toDisConnect();
/* 已连接 */
void connected();
/* 已断开连接 */
void disconnected();
/* 清除文本框时的内容 */
void clearTextBrowser();
/* 接收到消息 */
void receiveMessages();
/* 发送消息 */
void sendMessages();
/* 连接状态改变槽函数 */
void socketStateChange(QAbstractSocket::SocketState);
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
/* tcp套接字 */
tcpSocket = new QTcpSocket(this);
/* 设置端口号的范围,注意不要与主机的已使用的端口号冲突 */
ui->spinBox->setRange(10000, 99999);
/* 设置断开连接状态不可用 */
ui->pushButton_2->setEnabled(false);
/* 获取本地 ip */
getLocalHostIP();
/* 信号槽连接 */
connect(ui->pushButton, SIGNAL(clicked()),this, SLOT(toConnect()));
connect(ui->pushButton_2, SIGNAL(clicked()),this, SLOT(toDisConnect()));
connect(ui->pushButton_3, SIGNAL(clicked()),this, SLOT(clearTextBrowser()));
connect(ui->pushButton_4, SIGNAL(clicked()),this, SLOT(sendMessages()));
connect(tcpSocket, SIGNAL(connected()),this, SLOT(connected()));
connect(tcpSocket, SIGNAL(disconnected()),this, SLOT(disconnected()));
connect(tcpSocket, SIGNAL(readyRead()),this, SLOT(receiveMessages()));
connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,
SLOT(socketStateChange(QAbstractSocket::SocketState)));
}
Widget::~Widget()
{
delete ui;
}
void Widget::toConnect()
{
/* 如果连接状态还没有连接 */
if (tcpSocket->state() != tcpSocket->ConnectedState) {
/* 指定IP地址和端口连接 */
tcpSocket->connectToHost(IPlist[ui->comboBox->currentIndex()],ui->spinBox->value());
}
}
void Widget::toDisConnect()
{
/* 断开连接 */
tcpSocket->disconnectFromHost();
/* 关闭socket*/
tcpSocket->close();
}
void Widget::connected()
{
/* 显示已经连接 */
ui->textBrowser->append("已经连上服务端");
/* 设置按钮与下拉列表框的状态 */
ui->pushButton->setEnabled(false);
ui->pushButton_2->setEnabled(true);
ui->comboBox->setEnabled(false);
ui->spinBox->setEnabled(false);
}
void Widget::disconnected()
{
/* 显示已经断开连接 */
ui->textBrowser->append("已经断开服务端");
/* 设置按钮与下拉列表框的状态 */
ui->pushButton_2->setEnabled(false);
ui->pushButton->setEnabled(true);
ui->comboBox->setEnabled(true);
ui->spinBox->setEnabled(true);
}
/* 获取本地IP */
void Widget::getLocalHostIP()
{
// /* 获取主机的名称 */
// QString hostName = QHostInfo::localHostName();
// /* 主机的信息 */
// QHostInfo hostInfo = QHostInfo::fromName(hostName);
// /* ip列表,addresses返回ip地址列表,注意主机应能从路由器获取到
// * IP,否则可能返回空的列表(ubuntu用此方法只能获取到环回IP) */
// IPlist = hostInfo.addresses();
// qDebug()<<IPlist<<endl;
// /* 遍历IPlist */
// foreach (QHostAddress ip, IPlist) {
// if (ip.protocol() == QAbstractSocket::IPv4Protocol)
// comboBox->addItem(ip.toString());
// }
/* 获取所有的网络接口,
* QNetworkInterface类提供主机的IP地址和网络接口的列表 */
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();
/* 遍历list */
foreach (QNetworkInterface interface, list) {
/* QNetworkAddressEntry类存储IP地址子网掩码和广播地址 */
QList<QNetworkAddressEntry> entryList = interface.addressEntries();
/* 遍历entryList */
foreach (QNetworkAddressEntry entry, entryList) {
/* 过滤IPv6地址,只留下IPv4 */
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
ui->comboBox->addItem(entry.ip().toString());
/* 添加到IP列表中 */
IPlist<<entry.ip();
}
}
}
}
/* 清除文本浏览框里的内容 */
void Widget::clearTextBrowser()
{
/* 清除文本浏览器的内容 */
ui->textBrowser->clear();
}
/* 客户端接收消息 */
void Widget::receiveMessages()
{
/* 读取接收到的消息 */
QString messages = tcpSocket->readAll();
ui->textBrowser->append("服务端:" + messages);
}
/* 客户端发送消息 */
void Widget::sendMessages()
{
if(NULL == tcpSocket)
return;
if(tcpSocket->state() == tcpSocket->ConnectedState) {
/* 客户端显示发送的消息 */
ui->textBrowser->append("客户端:" + ui->lineEdit->text());
/* 发送消息 */
tcpSocket->write(ui->lineEdit->text().toUtf8().data());
}
}
/* 客户端状态改变 */
void Widget::socketStateChange(QAbstractSocket::SocketState state)
{
switch (state) {
case QAbstractSocket::UnconnectedState:
ui->textBrowser->append("scoket状态:与服务端未连接");
tcpSocket->deleteLater();
break;
case QAbstractSocket::ConnectedState:
ui->textBrowser->append("scoket状态:与服务端已连接");
break;
case QAbstractSocket::ConnectingState:
ui->textBrowser->append("scoket状态:ConnectingState");
break;
case QAbstractSocket::HostLookupState:
ui->textBrowser->append("scoket状态:HostLookupState");
break;
case QAbstractSocket::ClosingState:
ui->textBrowser->append("scoket状态:ClosingState");
break;
case QAbstractSocket::ListeningState:
ui->textBrowser->append("scoket状态:ListeningState");
break;
case QAbstractSocket::BoundState:
ui->textBrowser->append("scoket状态:BoundState");
break;
default:
break;
}
}
运行结果: