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, 8000);//通过建立的服务器监听指定ip地址及端口号的客服端,如不指定端口号,系统会随机分配 connect(server, QTcpServer::newConnection, [=]() { qDebug() << "有连接进来"; } );
tcp协议客户端实现流程
建立QTcpSocket套节字(ip,端口)
通过套节字connectToHost()函数主动连接服务器;连接成功则触发服务器QTcpServer::newConnection信号;并发送套节字到服务器端;
关闭连接;
QTcpSocket Sc(this); Sc.connectToHost("127.0.0.1", 8888);//实际代码中参数要进行类型转化 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 = 0); ~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, 8000); 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 = 0); ~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(8000); connect(udp, &QUdpSocket::readyRead, [=]() { char temp[1024] = {0}; 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 = 0); ~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("8000"); udp = new QUdpSocket(this); udp->bind(8000); udp->joinMulticastGroup(QHostAddress("")); connect(udp, &QUdpSocket::readyRead, [=]() { char temp[1024] = {0}; 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
1】基本用法:
创建一个QTcpServer,然后调用listen函数监听相应的地址和端口。当有客户端链接到服务器时,会有信号newConnection()产生。
调用nextPendingConnection()接受一个挂起的TcpSocket连接,该函数返回一个指向QTcpSocket的指针,同时进入到QAbstractSocket::ConnectedState状态。这样就可以和客户端进行通信了。如果错误发生,可以用函数serverError()返回错误类型,用errorString()返回错误提示字符串。
调用close使得QTcpServer停止监听连接请求。尽管QTcpServer使用了事件循环,但是可以不这么使用。利用waitForNewConnection(),该函数阻塞直到有连接可用或者时间超时。
2】重要函数:
void incomingConnection (int socketDescriptor);
当一个连接可以用时,QTcpServer调用该函数。其基本过程是现创建一个QTcpSocket,设置描述符和保存到列表,最后发送newConnection() 事件消息。
QTcpSocket* QTcpServer::nextPendingConnection();
返回下一个将要连接的QTcpSocket对象,该返回对象是QTcpServer的子对象,意味着如果删除了QTcpSServer,则删除了该对象。也可以在你不需要该对象时,将他删除掉,以免占用内存。
客户端:QTcpSocket,QAbstractSocket
1】基本用法:
在客户端创建一个QTcpSocket,然后用connectToHost函数向对应的主机和端口建立连接。
任何时候,可以用state()查询状态,初始为UnconnectedState,然后调用连接函数之后,HostLookupState,如果连接成功进入ConnectedState,并且发送hostFound()信号。
当连接建立,发送connected(),在任何状态下如果在错误发生error()信号发送。状态改变发送stateChanged()信号。如果QTcpSocket准备好可读可写,则isValid() 函数范围为真。
用read()和write()来读写,或者使用readLine()和readAll.当有数据到来的时候,系统会发送readyRead()信号。
bytesAvailable()返回包的字节数,如果你不是一次性读完数据,新的数据包到来的时候将会附加到内部读缓存后面。setReadBufferSize()可以设置读缓存的大小。
用disconnectFromHost()关闭连接,进入ClosingState。当所有数据写入到socket,QAbstractSocket会关闭该台socket,同时发送disconnected()消息。
如果想立即终止一个连接,放弃数据发送,调用abort().
如果远程主机关闭连接,QAbstractSocket发送QAbstractSocket::RemoteHostClosedError错误,但是状态还停留在ConnectedState,然后发送disconnected()信号。
QAbstractSocket提供几个函数用来挂起调用线程,知道一定的信号发送,这些函数可以用来阻塞socket:
waitForConnected() 阻塞知道一个连接建立。
waitForReadyRead() 阻塞知道有新的数据可以读取。
waitForBytesWritten() 阻塞直到发送数据写道socket中。
waitForDisconnected() 阻塞知道链接关闭。
QT5 TCP网络通讯之文件传送
原文地址:https://www.cnblogs.com/flowingwind/p/8348519.html