1. UDP通信
1.1 udp通信的基本流程
创建套接字
绑定套接字
进行通信
关闭套接字
涉及到的类和信号
QUdpSocket:Udp套接字类,类对象就是一个udp套接字对象
QHostAddress:ip地址类
void readyRead():信号,当有数据到达可读,就会产生这个信号
1.2 举例
通信端1
udp1
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
//当有数据到达时的槽
void readdate();
private:
Ui::Widget *ui;
//创建udp对象
QUdpSocket* socket;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
//udp通信
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//1. 创建udp套接字
socket = new QUdpSocket;
//2. 绑定
//ip地址类,直接构造设置ip地址
QHostAddress addr("192.168.124.33");
//addr.setAddress();//函数设置ip地址
socket->bind(addr,10000);
//绑定readyRead信号,当有数据到达时,就会触发信号,去接收数据
connect(socket,SIGNAL(readyRead()),this,SLOT(readdate()));
}
Widget::~Widget()
{
delete ui;
}
//发送数据
void Widget::on_pushButton_clicked()
{
//发送数据
//定义缓冲区
QByteArray data = ui->textEdit_2->toPlainText().toLocal8Bit();//toLocal8Bit()将数据转换为QByteArray类型
socket->writeDatagram(data,QHostAddress("192.168.124.33"),10001);
}
//接收数据
void Widget::readdate(){
//定义缓冲区
QByteArray data;
data.resize(1024);
//读发来的数据存,储到data中
QHostAddress addr;
quint16 port;
//size是收到的数据大小
int size = socket->readDatagram(data.data(),data.size(),&addr,&port);//参数addrr,port是发送方的ip和端口
data.resize(size);
//展示数据
ui->textEdit->append("发送端的ip:"+addr.toString()+" port:"+QString::number(port));
ui->textEdit->append(data);
}
通信端2
udp2
widget.h
widget.cpp
运行
2. TCP通信
2.1 客户端通信流程 QTcpSocket
1. 创建套接字
2. 绑定套接字
3. 连接服务器
4. 进行通信
5. 关闭套接字
2.1.1 涉及的信号
connected():信号,当连接服务器且连接成功
readyRead():信号,当发送给数据到套接字,套接字可读
disconnected():信号,只要套接字断开连接,就会产生
2.2 服务端通信流程 QTcpServer
1. 创建套接字
2. 绑定套接字
3. 监听套接字---套接字类型改变改为监听套接字
4. 连接客户端---得到与客户端进行通信的套接字
5. 进行通信
6. 关闭套接字
相关函数
nextPendingConnection():服务器建立与客户端连接,返回值 QTcpScket 类对象:通信套接字对象
2.2.1 涉及的信号
newConnection():信号,当有新的客户端连接时,会产生这个信号
readyRead():信号,当发送给数据到套接字,套接字可读
disconnected():信号,只要套接字断开连接,就会产生
2.2 举例:模拟客户端和服务端通信
2.2.1 客户端
tcp_client
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_connect_clicked();
//当连接服务器,且连接成功的槽
void socket_conn();
void on_pushButton_send_clicked();
//当有数据发来时,触发该信号
void readdata();
void on_pushButton_duankai_clicked();
//当连接断开,触发该信号
void socket_disconn();
private:
Ui::Widget *ui;
//创建tcp对象
QTcpSocket* socket;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
//tcp通信客户端
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//1.创建套接字对象
socket = new QTcpSocket;
//2.绑定,这里其实可以不用绑定,系统会自动给你分配
socket->bind(QHostAddress("192.168.124.33"),9999);
//设置连接按钮可点击,发送和点击不可点
ui->pushButton_connect->setEnabled(true);
ui->pushButton_send->setEnabled(false);
ui->pushButton_duankai->setEnabled(false);
}
Widget::~Widget()
{
delete ui;
}
//连接服务器
void Widget::on_pushButton_connect_clicked()
{
//3. 连接服务器,参数1服务端ip,参数2服务端端口
socket->connectToHost(ui->lineEdit_ip->text(),ui->lineEdit_port->text().toUShort());//toUShort() 字符串转为数字
//提示,注意这里的信号和槽的绑定写在连接按钮里,后面会有个问题,就是每连接一次信号和槽都会再绑定一次,造成多次重复绑定
//要解决问题,就需要在断开连接哪里把绑定的信号和槽断开
//如果把信号和槽的绑定写在上面的构造里就不会有这个问题了
//当连接服务器,且连接成功,就会触发该信号
connect(socket,SIGNAL(connected()),this,SLOT(socket_conn()));
//当有数据发来时,触发该信号
connect(socket,SIGNAL(readyRead()),this,SLOT(readdata()));
//当连接断开,触发该信号
connect(socket,SIGNAL(disconnected()),this,SLOT(socket_disconn()));
}
//当连接服务器,且连接成功,触发的信号对应的槽
void Widget::socket_conn(){
//接收框里提示连接成功
ui->textEdit_receice->append(ui->lineEdit_ip->text()+":"+ui->lineEdit_port->text()+" connect ok");
//设置连接按钮不可点击,断开和发送可点击
ui->pushButton_connect->setEnabled(false);
ui->pushButton_send->setEnabled(true);
ui->pushButton_duankai->setEnabled(true);
}
//点击发送数据给服务器
void Widget::on_pushButton_send_clicked()
{
//4.将数据发送给服务器
//toStdString().c_str()先转为c++标准字符串,再转为c字符串
socket->write(ui->textEdit_write->toPlainText().toStdString().c_str());
}
//当有数据发来时,接收数据
void Widget::readdata(){
//5.读取数据
QByteArray data = socket->readAll();
ui->textEdit_receice->append(data);
}
//客户端断开与服务器的连接
void Widget::on_pushButton_duankai_clicked()
{
//6.断开与服务端的连接
socket->disconnectFromHost();
}
//连接断开后要做到处理 对应的槽(只要连接断开就会进入这个槽函数)
void Widget::socket_disconn(){
//接收框里提示连接断开
ui->textEdit_receice->append(ui->lineEdit_ip->text()+":"+ui->lineEdit_port->text()+" disconnect");
//把绑定的信号和槽断开
disconnect(socket,SIGNAL(connected()),this,SLOT(socket_conn()));
disconnect(socket,SIGNAL(readyRead()),this,SLOT(readdata()));
disconnect(socket,SIGNAL(disconnected()),this,SLOT(socket_disconn()));
//设置连接按钮可点击,发送和点击不可点
ui->pushButton_connect->setEnabled(true);
ui->pushButton_send->setEnabled(false);
ui->pushButton_duankai->setEnabled(false);
}
测试使用网络调试助手E:\peixunqianrushi_ziliao\网络调试助手
连接
发送数据
点击断开连接
2.2.2 服务端
服务器设置为多线程---并发服务器,每个客户端在线程中进行操作
tcp_server
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <thread_tcp.h>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_start_clicked();
//当有新的客户端连接时的槽
void new_conn_arrive();
void on_pushButton_end_clicked();
private:
Ui::Widget *ui;
//实例化tcp对象
QTcpServer* server;
//存储的socket就是与客户端的通信套接字
QTcpSocket* socket1;
//存储所有的通信套接字
QList<QTcpSocket*> list;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
//tcp通信服务端
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//1.创建tcp服务端套接字
server = new QTcpServer;
//2.绑定,这里提示,在qt中绑定和监听写在一起了,都在listen函数中
//当有新的客户端连接时,就会触发该信号
connect(server,SIGNAL(newConnection()),this,SLOT(new_conn_arrive()));
}
Widget::~Widget()
{
delete ui;
}
//启动服务器
void Widget::on_pushButton_start_clicked()
{
//3.监听,这里提示,在qt中绑定和监听写在一起了
server->listen(QHostAddress("192.168.124.33"),8888);
ui->textEdit->append("服务器启动成功~~~");
}
//当有新的客户端连接请求时,触发该信号对应的槽函数
//建立连接
void Widget::new_conn_arrive(){
//4.服务器建立与客户端的连接
//现在的socket1就是与客户端的通信套接字
socket1 = server->nextPendingConnection();
//提示客户端连接成功
socket1->write("connect success~~~~~~~");
//将通信套接字添加进链表
list.append(socket1);
//从这里使用线程
//把与客户端通信的套接字,放入线程中,使用线程来操作套接字与客户端通信
//创建线程,有一个客户端就创建一个线程
thread_tcp* tcp = new thread_tcp;
//把通信套接字给线程
tcp->socket = socket1;
//当客户端发来消息,就会触发 在线程中的 写的槽函数,去读取客户端消息
connect(tcp->socket,SIGNAL(readyRead()),tcp,SLOT(readdata()));
//客户端断开连接,触发信号调用 线程中的槽,使线程关闭
connect(tcp->socket,SIGNAL(disconnected()),tcp,SLOT(dis_conn()));
//启动线程
tcp->start();
}
//关闭服务器
void Widget::on_pushButton_end_clicked()
{
for(int i=0;i<list.size();i++){
//服务端关闭通信套接字的连接
list.at(i)->disconnectFromHost();
}
list.clear();
//关闭监听
server->close();
ui->textEdit->append("服务端已经关闭连接~~~~~~~");
}
thread_tcp.h
#ifndef THREAD_TCP_H
#define THREAD_TCP_H
#include <QThread>
#include <QTcpSocket>
#include <QDebug>
class thread_tcp : public QThread
{
Q_OBJECT
public:
thread_tcp();
//socket就是与客户端的通信套接字
QTcpSocket* socket;
//执行线程的run
void run();
public slots:
//当客户端发来消息的槽
void readdata();
//只要客户端断开,就关闭线程
void dis_conn();
};
#endif // THREAD_TCP_H
thread_tcp.cpp
#include "thread_tcp.h"
thread_tcp::thread_tcp()
{
}
//当客户端发来消息,读数据
void thread_tcp::readdata(){
//读出数据
QByteArray data = socket->readAll();
//给客户端返回数据
socket->write(data);
}
//一直执行线程
void thread_tcp::run(){
qDebug()<<"线程执行";
//阻塞执行
exec();
}
//客户端断开连接时,关闭线程
void thread_tcp::dis_conn(){
qDebug()<<"线程关闭";
exit(0);
}
测试
运行服务端
启动客户端和网络调试助手,分别充当两个客户
分别连接,成功
发送数据,成功
分别断开连接,成功
再次分别连接,测试服务端关闭功能
关闭成功