QT5 网络通讯
QT5 TCP网络通讯
- 服务器与客户端建立连接listen() - connectToHost(); 触发newPendingConnect信号
- 实时数据通讯write(); read(); 触发readyRead信号
通讯主要使用的类:
QTcpServer Class
QTcpServer类提供了一个基于TCP的服务器。
这个类可以接受传入的TCP连接。您可以指定端口或让QTcpServer自动选择一个端口。您可以收听特定地址或所有机器的地址。
调用listen()让服务器侦听传入的连接。每次客户端连接到服务器时,都会发出newConnection()信号。
QTcpSocket Class
QTcpSocket类提供了一个TCP套接字。
TCP(传输控制协议)是一种可靠的,面向流的,面向连接的传输协议。 它特别适合连续传输数据。
QTcpSocket是QAbstractSocket的一个方便的子类,它允许你建立一个TCP连接并传输数据流。
建立连接:
服务器端以监听的方式监听客服端是否有连接请求
客户端以调用connectToHost()函数主动连接服务器端
tcp协议服务器端实现流程
建立服务器对象
listen服务器, 通过建立的服务器 监听指定地址/端口的客服端;判断是否有客户连接有连接就触发newConnection();
通过connect处理newConnection()信号;
server = new QTcpServer(this); //建立一个服务器对象
server->listen(QHostAddress::Any, );//通过建立的服务器监听指定ip地址及端口号的客服端,如不指定端口号,系统会随机分配
connect(server, QTcpServer::newConnection,
[=]()
{
qDebug() << "有连接进来";
}
);
tcp协议客户端实现流程
建立QTcpSocket套节字(ip,端口)
通过套节字connectToHost()函数主动连接服务器;连接成功则触发服务器QTcpServer::newConnection信号;并发送套节字到服务器端;
关闭连接;
QTcpSocket Sc(this);
Sc.connectToHost("127.0.0.1", );//实际代码中参数要进行类型转化
Sc.close();
实时通讯:
- 客户端到服务器端通讯
- 当客户端与服务器端建立连接后;
- 客户端与服务器端通讯在客户端通过套节字对象调用write()函数发送上传内容;
- 服务器端有客户端数据写入时服务器端会自动调用readyread信号
- 服务器端在connect中处理readyread信号,并由nextPendingConnection()函数接收客户端发送的套节字;
- 服务器端对接收的套节字进行相应处理,即完成一次客户端到服务器端的通讯
- 服务器端到客户端的通讯
- 当客户端与服务器端建立连接后;
- 服务器通过套节字对象调用write()函数发送上传内容;客户端会触发readyread信号
- 客户端在connect中处理readyread信号
客户端到服务器端实现代码:
//服务器端
connect(server, QTcpServer::newConnection,
[=]()
{
QTcpSocket socket = server->nextPendingConnection();
connect(socket, &QTcpSocket::readyRead, [=]()
{
tp = socket->readAll();
ui->textrc->append(tp);
});
}
);
//客户端
void socket::on_buttonsend_clicked()
{
QString temp = ui->textrc->toPlainText();
if(!temp.isEmpty())sock->write(temp.toUtf8()); }
服务器端到客户端实现代码:
//服务器端
void Widget::on_buttonsend_clicked()
{
socket->write(ui->textEdit->toPlainText().toUtf8());
}
//客户端
connect(sock, &QTcpSocket::readyRead,
[=]()
{
ui->textdis->append(sock->readAll());
});
完整代码:
服务器ui设计:
服务器端头文件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 = );
~Widget(); private slots:
void on_buttonsend_clicked(); private:
Ui::Widget *ui; QTcpServer *server; //建立服务器对象
QTcpSocket *socket; //套节字对象
QByteArray tp; //
}; #endif // WIDGET_H
服务器端cpp文件 widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug> Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("服务器");
tp = nullptr; server = new QTcpServer(this);
server->listen(QHostAddress::Any, );
connect(server, QTcpServer::newConnection,
[=]()
{
socket = server->nextPendingConnection();
connect(socket, &QTcpSocket::readyRead, [=]()
{
tp = socket->readAll();
ui->testdis->append(tp);
});
}
); } Widget::~Widget()
{
delete ui;
} void Widget::on_buttonsend_clicked()
{
socket->write(ui->textEdit->toPlainText().toUtf8());
}
客户端ui:
客户端头文件socket.h:
#ifndef SOCKET_H
#define SOCKET_H #include <QWidget>
#include <QTcpSocket>
#include <QHostAddress> namespace Ui {
class socket;
} class socket : public QWidget
{
Q_OBJECT public:
explicit socket(QWidget *parent = );
~socket(); private slots: void on_buttonLink_clicked(); void on_buttonsend_clicked(); void on_serverclose_clicked(); private:
Ui::socket *ui; QTcpSocket *sock;
QHostAddress adrs;
quint16 port;
}; #endif // SOCKET_H
客户端cpp文件socket.cpp
#include "socket.h"
#include "ui_socket.h" socket::socket(QWidget *parent) :
QWidget(parent),
ui(new Ui::socket)
{
ui->setupUi(this);
sock = new QTcpSocket(this);
setWindowTitle("张三");
connect(sock, &QTcpSocket::readyRead,
[=]()
{
ui->textdis->append(sock->readAll());
});
} socket::~socket()
{
delete ui;
} void socket::on_buttonLink_clicked()
{
QString ip = ui->serverIP->text();
QString p = ui->serverPort->text();
sock->connectToHost(ip, p.toUShort());
} void socket::on_buttonsend_clicked()
{
QString temp = ui->textEdit->toPlainText();
if(!temp.isEmpty())sock->write(temp.toUtf8());
} void socket::on_serverclose_clicked()
{
sock->close();
}
main.cpp文件
#include "widget.h"
#include <QApplication>
#include "socket.h" int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
socket w1;
w1.show(); return a.exec();
}
最终运行效果:
当然在具体的实现过程中还有很多很多的细节需要优化;
QT5对tcp协议基本的通讯总结:
- QTcpServer *p = new QTcpServer(this);//建立服务器对象 QTcpSocket *q = new QTcpSocket(this); //客户机建立套节字对象
- p.listen(监听的客户ip , 监听端口port);//监听客户机 q.conncetToHost(要连接的服务器ip, 要连接的服务器端口);
- connect(p, &QTcpServer::newConnection, )连接成功触发信号 q.write();//发送数剧 到服务器
- QTcpSocket skt = p.nextPendingConnection();//获取客户机套节字 connect(q, &QTcpSocket::readyRead, )//服务端发送数据客户端触发信号
- connect(skt, &QTcpSocket::readyRead, )//客户发送数据触发信号 q.readall();//读取客户端发送的数据;
- skt.readall();//读取客户端发送的数据; 客户端处理数据
- 服务器端处理数据
QT5 UDP网络通讯
UDP没有服务器与客户端之分;单纯通过writeDatagram发( 参数1, 参数2,参数3)送指定的内容(参数1)到指定的ip(参数2),端口(参数3)上;
当收取到网络中的数据发送,就会触发自己的readyRead信号;readDatagram(参数1, 参数2,参数3),保存读取的内容(参数1);保存对方ip(参数2);保存对方端口(参数3)
具体实现过程:
- 建立QUdpSocket套节字 QUdpSocket* p = new QUdpSocket(this);
- 绑定本程序端口号 bind() p.bind(8000);
- 通过writeDatagram()发送数据到指定目标 p.writeDatagram();
- 当有readyRead信号发生通过readDatagram()函数读取保存数据 p.readDatagram();
实现代码:
QUdpSocket *udp = new QUdpSocket(this);
udp->bind();
connect(udp, &QUdpSocket::readyRead, [=]()
{ char temp[] = {};
QHostAddress q;
quint16 p;
udp->readDatagram(temp, sizeof(temp), &q, &p);
ui->textdis->append(temp);
}); /......./ //按键确定发送
void Widget::on_buttonlink_clicked()
{
udp->writeDatagram(ui->textsend->toPlainText().toUtf8(), QHostAddress(ui->ip->text()), ui->port->text().toUShort());
}
整体代码:
ui设计:
widget.h头文件
#ifndef WIDGET_H
#define WIDGET_H #include <QWidget>
#include <QUdpSocket>
namespace Ui {
class Widget;
} class Widget : public QWidget
{
Q_OBJECT public:
explicit Widget(QWidget *parent = );
~Widget(); private slots:
void on_buttonlink_clicked(); private:
Ui::Widget *ui;
QUdpSocket *udp;
}; #endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QDialog> Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("");
udp = new QUdpSocket(this);
udp->bind();
udp->joinMulticastGroup(QHostAddress(""));
connect(udp, &QUdpSocket::readyRead, [=]()
{ char temp[] = {};
QHostAddress q;
quint16 p;
udp->readDatagram(temp, sizeof(temp), &q, &p);
ui->textdis->append(temp);
});
} Widget::~Widget()
{
delete ui;
} void Widget::on_buttonlink_clicked()
{
udp->writeDatagram(ui->textsend->toPlainText().toUtf8(), QHostAddress(ui->ip->text()), ui->port->text().toUShort());
}
main.cpp文件
#include "widget.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show(); return a.exec();
}
以便测试:我们先编译生成一份客户端;再在源文件中改变bind端口号为8888再生成一份客户端;最终就会有两份客户端以便相互通信测试
运行测试结果:
由于UDP不需要服务器,所以,UDP发送的数据,只要能接收到你的ip及端口的客户端就殾能收到信息;所以在局域网内,ip地址栏可输入
255.255.255.255 即整个局域网内的客户端都能收到信息;
UDP通讯组包
为了满足,发送的信息指定ip段内的客户收到信息可以用函数JoinMulticastGroup(IPAddress)加入到组;根据msdn记载,没错是同样的功能;
目前个人理解也不深;详细数据可查msdn;可用leaveMulticastGroup()函数离开组翻;
Tcp 与 Udp的比较:
Udp不需要服务器,只管发送数据,不对数据进行检查,也不对接收者检测;速度快,易丢包,做即时数据传送比较好;
Tcp方式总结:
服务器端:QTcpServer
ss->peerAddress();ss->peerName();ss->peerPort(); 当连接成功可能通过函数获取客户端ip,name,port;
QT5 TCP网络通讯综合案例之文件传送
最终效果图:
1.功能设计
服务器端:
- 有客户端连接进入就提示连接客户ip端口号等基本信息
- [选择文件]按钮被激活可在系统选取任意文件,显示选取的文件路径
- [发送]按钮被激活,点击[发送]向客户端发送文件请求;
- 文件发送完成后 显示发送完成提示
客户端:
- 连接服务器ip,port编辑框
- 按钮[连接服务器]成功后显示提示并激活断开服务器按钮
- 可选文件接收保存路径
- 当收到服务器发送文件请求,显示所要接收的文件名,文件大小等基本信息
- 仅当收到服务器发送的文件请求与选择了有效文件路径前提下,激活[接收文件]按钮
- 点击[接收文件]开始从服务器接收文件,文件接收进度条提示功能,并显示文件传送完成提示
实现原代码:
//main主函数
#include "widget.h"
#include "client.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
client w1;
w1.show();
return a.exec();
}
//服务器端头文件
#ifndef WIDGET_H
#define WIDGET_H #include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QFile> namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = );
~Widget();
private slots:
void on_buttoncheck_clicked();
void on_buttonsend_clicked();
private:
Ui::Widget *ui;
QTcpServer *sv;
QTcpSocket *ss; QString sendFileName;
qint64 sendFileSize;
qint64 sendedSize;
QFile sendFile;
}; #endif // WIDGET_H
//服务器cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QFile>
#include <QMessageBox> Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("服务端-端口:9527");
sv = new QTcpServer(this);
sv->listen(QHostAddress::Any, );
ui->buttonsend->setEnabled(false);
ui->buttoncheck->setEnabled(false);
sendedSize = ;
connect(sv, &QTcpServer::newConnection, [=]()
{
ss = sv->nextPendingConnection();
if(ss->isValid())
{
ui->buttoncheck->setEnabled(true);
QString pAd = ss->peerAddress().toString().remove(, );
QString pNa = ss->peerName();
quint16 pPo = ss->peerPort();
ui->textdis->setText(QString("已成功连接到用户IP:%1:%2:Name:%3").arg(pAd).arg(pPo).arg(pNa));
}else
{
ui->textdis->setText("连接套节字无效");
}
connect(ss, &QTcpSocket::readyRead,[=]()
{
if(ss->readAll() == "ok")
{
qint64 lenW = ;
if(sendFile.open(QIODevice::ReadOnly))
{
do{
char tempdata[] = {};
lenW = sendFile.read(tempdata, sizeof(tempdata));
ss->write(tempdata, lenW);
sendedSize += lenW;
}while(lenW > );
if(sendedSize >= sendFileSize)
{
ui->textdis->append("发送完成");
ss->disconnectFromHost();
sendFile.close();
}
}else
{
QMessageBox *abot = new QMessageBox(this);
abot->setText("打开文件失败");
}
}
}); });
} Widget::~Widget()
{
delete ui;
} void Widget::on_buttoncheck_clicked()
{
QString File = QFileDialog::getOpenFileName(this, QString("选择文件"), QString("../"));
if(!File.isEmpty())
{
ui->buttoncheck->setEnabled(false);
ui->textdis->append(File);
sendFile.setFileName(File);
QFileInfo info(File);
sendFileName = info.fileName();
sendFileSize = info.size();
ui->buttonsend->setEnabled(true);
}
} void Widget::on_buttonsend_clicked()
{
QString temp = QString("head@@%1@@%2").arg(sendFileName).arg(sendFileSize);
ss->write(temp.toUtf8());
ui->buttonsend->setEnabled(false);
}
//客户端头文件
#ifndef CLIENT_H
#define CLIENT_H #include <QWidget>
#include <QTcpSocket>
#include <QFile> namespace Ui {
class client;
}
class client : public QWidget
{
Q_OBJECT public:
explicit client(QWidget *parent = );
~client(); private slots:
void on_Buttonlink_clicked();
void on_Buttonclose_clicked();
void on_ButtonSelect_clicked();
void on_ButtonSave_clicked();
private:
Ui::client *ui;
QTcpSocket userSocket; QFile acpFile;
QString headFile;
QString savePath;
bool head;
bool openFile;
QString _name;
QString _size;
qint64 nowsize; }; #endif // CLIENT_H
//客户端cpp
#include "client.h"
#include "ui_client.h"
#include <QFile>
#include <QFileDialog>
#include <QMessageBox> client::client(QWidget *parent) :
QWidget(parent),
ui(new Ui::client)
{
ui->setupUi(this);
setWindowTitle("客户端");
head = true;
openFile =false;
nowsize = ;
ui->progressBar->setMinimum();
ui->progressBar->setValue();
ui->ButtonSave->setEnabled(false);
ui->Buttonclose->setEnabled(false);
savePath = nullptr;
connect(&userSocket, &QTcpSocket::connected,
[=](){
ui->Buttonlink->setEnabled(false);
ui->Buttonclose->setEnabled(true);
ui->lineStatus->setText("连接成功");});
connect(&userSocket, &QTcpSocket::readyRead,
[=](){
if(head)
{
headFile = QString(userSocket.readAll());
if(!headFile.isEmpty())
{
head = false;
_name = headFile.section("@@", , );
_size = headFile.section("@@", , );
ui->progressBar->setMaximum(_size.toLongLong());
ui->lineStatus->setText(QString("文件名:[%1]总大小:[%2]等待接收").arg(_name).arg(_size));
acpFile.setFileName(QString("%1/%2").arg(savePath).arg(_name));
if(!savePath.isEmpty())
{
ui->ButtonSave->setEnabled(true);
}
}
}
else
{
if(openFile)
{
nowsize += acpFile.write(userSocket.readAll());
ui->progressBar->setValue(nowsize);
}
else
{
QMessageBox *abot = new QMessageBox(this);
abot->setText("打开文件失败");
}
if(acpFile.size() >= _size.toLongLong())
{
nowsize = ;
acpFile.close();
ui->lineStatus->setText("文件传送完成");
head = true;
}
} });
} client::~client()
{
delete ui;
} void client::on_Buttonlink_clicked()
{
QString linkIp = ui->lineIp->text();
quint16 linkPort = ui->linePort->text().toUShort();
userSocket.connectToHost(linkIp, linkPort);
} void client::on_Buttonclose_clicked()
{
userSocket.disconnectFromHost();
userSocket.close();
ui->lineStatus->setText("断开连接");
head = true;
ui->Buttonlink->setEnabled(true);
ui->Buttonclose->setEnabled(false);
ui->progressBar->setValue();
} void client::on_ButtonSelect_clicked()
{
savePath = QFileDialog::getExistingDirectory(this, QString("保存路径"),QString("../"));
if(!savePath.isEmpty())
{
ui->linePath->setText(savePath);
if(!head)ui->ButtonSave->setEnabled(true);
}
} void client::on_ButtonSave_clicked()
{
ui->ButtonSave->setEnabled(false);
userSocket.write("ok");
openFile = acpFile.open(QIODevice::WriteOnly);
}
主要运用函数:
QString QFileDialog::getExistingDirectory() //静态函数 获取文件路径
QString QFileDialog::getOpenFileName()//静态函数 获取要打开文件的文件路径文件名
QTcpSocket -> isValid(); //如果套节字有效返回true,否则返回false;
void setenable(); //设置控件激活状态
注意事项:
在服务器端发送文件,采取的是分段读取文件,边读边发的方式发送,而在客户端采取每次接收每次发送的全部内容方式保存文件
起初有采取过在服务器端all = readAall();write(all);一次性读取发送;但在测试中发现,实际发送大文件时还是会自动被分成多次(只是简单测试,不排除受其他因素影响)
QT5 网络通讯的更多相关文章
- dicom网络通讯入门(3)
接下来可以进行消息传递了 ,也就是dimse ,再来复习下 什么是dimse .n-set n-create c-echo 这些都是dimse 他们都是属于一种结构的pdu 那就是tf-pdu(传 ...
- dicom网络通讯入门(2)
第二篇,前面都是闲扯 其实正文现在才开始,这次是把压箱底的东西都拿出来了. 首先我们今天要干的事是实现一个echo响应测试工具 也就是echo 的scu,不是实现打印作业管理么.同学我告诉你还早着呢. ...
- 《连载 | 物联网框架ServerSuperIO教程》-4.如开发一套设备驱动,同时支持串口和网络通讯。附:将来支持Windows 10 IOT
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
- Windows 网络通讯开发
Windows 网络通讯开发 一.Windows网络开发API 由于C++标准库中没有网络库,所以进行网络开发的时候要调用系统API.Windows通讯开发API包括以下几个基本函数及成员类型: 1. ...
- 文档:网络通讯包结构(crc校验,加解密)
一直想把这个流程整理一下. 包结构: 包 对(datacrc+protoID+dataSize)组成的byte[] 进行crc计算而得到 对(数据内容)进行crc计算而得到 协议号 数据内容的字节长度 ...
- 网络--三种网络通讯方式及Android的网络通讯机制
Android平台有三种网络接口可以使用,他们分别是:java.net.*(标准Java接口).Org.apache接口和Android.net.*(Android网络接口).下面分别介绍这些接口的功 ...
- DIOCP网络通讯流程
DIOCP 运作核心探密 原文连接: http://blog.qdac.cc/?p=2362 原作者: BB 天地弦的DIOCP早已经广为人知了,有很多的同学都用上了它,甚至各种变异.修改版本也出 ...
- Socket网络通讯开发总结之:Java 与 C进行Socket通讯 + [备忘] Java和C之间的通讯
Socket网络通讯开发总结之:Java 与 C进行Socket通讯 http://blog.sina.com.cn/s/blog_55934df80100i55l.html (2010-04-08 ...
- 如何在Windows系统上用抓包软件Wireshark截获iPhone等网络通讯数据
http://www.jb51.net/os/windows/189090.html 今天给大家介绍一种如何在Windows操作系统上使用著名的抓包工具软件Wireshark来截获iPhone.iPa ...
随机推荐
- 【Python基础】05_Python中的while循环
1.程序的三大流程介绍 顺序 —— 从上到下,顺序执行 分支 —— 根据条件判断,决定代码的分支 循环 —— 让特定代码执行 2.while 基本语法 while 条件(判断 计数器 是否达到 目标次 ...
- Timezone offset does not match system offset: 0 != -32400. Please, check your config files
apscheduler使用uWSGI的mule模块部署的时候报错, 因为系统时区和代码运行时区不一样导致. 解决办法:在初始化的时候指定上海的时区 scheduler = BlockingSchedu ...
- X86驱动:挂接SSDT内核钩子
SSDT 中文名称为系统服务描述符表,该表的作用是将Ring3应用层与Ring0内核层,两者的API函数连接起来,起到承上启下的作用,SSDT并不仅仅只包含一个庞大的地址索引表,它还包含着一些其它有用 ...
- C# Base64 操作类
using System; using System.Text; namespace VWFC.IT.CUP.BLL.Util { /// <summary> /// Base64 too ...
- springboot+eureka+mybatis+mysql环境下报504 Gateway Time-out
1.test环境下的数据库配置的 driver和url有问题, 在工程日志中的表现是不能打印出最新的日志,在部署前的日志能看到报错:
- c#将电脑时间同步到网络时间
最近遇到个项目,需要控制软件使用时间,由于电脑不联网可以修改时间,故需要联网使电脑同步网络时间 网上寻找了很多解决方案,代码如下: //Forproc_Win32.cs//对常用Win32 API函数 ...
- Mac命令行提示
之前看到一个大神的终端主题好炫,所以自己也想弄一个.看了很多中文的教程都不是很靠谱,效果并没有实现.不能说人家的不对,只能说自己水平有限.后来直接去看 github 上的官方教程,因为是官方嘛~所以肯 ...
- 二元变量图形的pandas方法
数据加载: 1.散点图 上图使用下采样的方法选取了100个样本点,因为把所有的数据加载进来太多了. 2.Hexplot图 上图是一个散点图再加上热力标注的形式,可以更准确的帮助我们看出数据集中在哪些区 ...
- Oracle和MySql的分页查询区别和PL/SQL的基本概念
Oracle和MySql的分页查询区别: Oracle的分析查询,之前Oracle的分页是使用伪列 ROWNUM 结合子查询实现,mysql的分页更简单,直接使用 LIMIT 关键字就可以实现 ...
- Linux内核的arch目录