一、TCP/IP通信
- 在Qt中实现TCP/IP服务器端通信的流程:
1. 创建套接字
2. 将套接字设置为监听模式
3. 等待并接受客户端请求
可以通过QTcpServer提供的void newConnection()信号来检测是否有连接请求,如果有可以在对应的槽函数中调用nextPendingConnection函数获取到客户端的Socket信息(返回值为QTcpSocket*类型指针),通过此套接字与客户端之间进行通信。
4. 接收或者向客户端发送数据
接收数据:使用read()或者readAll()函数
发送数据:使用write()函数
- 客户端通信流程:
1. 创建套接字
2. 连接服务器
可以使用QTcpSocket类的connectToHost()函数来连接服务器。
3. 向服务器发送或者接受数据
linuxTCP通信流程和QtTCP通信流程对比:
TCP/IP通信的实现
服务器端:
首先.pro文件中加上netxwok:
QT += core gui network
serverwidget.ui:
选中按钮,右击转到槽可直接对该按钮进行程序设置
serverwidget.h:
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#include <QWidget>
#include<QTcpServer>//监听套接字
#include<QTcpSocket>//通信套接字
QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE
class ServerWidget : public QWidget
{
Q_OBJECT
public:
ServerWidget(QWidget *parent = nullptr);
~ServerWidget();
private slots:
void on_buttonSend_clicked();
void on_buttonClose_clicked();
private:
Ui::ServerWidget *ui;
QTcpServer *tcpServer;//监听套接字
QTcpSocket *tcpSocket;//通信套接字
};
#endif // SERVERWIDGET_H
serverwidget.cpp:
#include "serverwidget.h"
#include "ui_serverwidget.h"
ServerWidget::ServerWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ServerWidget)
{
ui->setupUi(this);
tcpServer = NULL;
tcpSocket = NULL;
//监听套接字,指定父对象,让其自动回收
tcpServer = new QTcpServer(this);
tcpServer->listen(QHostAddress::Any,8888);
setWindowTitle("服务器:8888");
connect(tcpServer,&QTcpServer::newConnection,[=](){
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的IP和端口
QString ip = tcpSocket->peerAddress().toString();
qint16 port = tcpSocket->peerPort();
QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);
ui->textEditRead->setText(temp);
connect(tcpSocket,&QTcpSocket::readyRead,[=](){
//从通信套接字中取出内容
QByteArray array = tcpSocket->readAll();
ui->textEditRead->append(array);
});
});
}
ServerWidget::~ServerWidget()
{
delete ui;
}
void ServerWidget::on_buttonSend_clicked()
{
if (NULL == tcpSocket)
{
return;
}
//获取编辑器内容
QString str = ui->textEditWrite->toPlainText();
//给对方发送数据,使用套接字是tcpSocket
tcpSocket->write(str.toUtf8().data());
}
void ServerWidget::on_buttonClose_clicked()
{
if (NULL == tcpSocket)
{
return;
}
//主动和客户端断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
tcpSocket = NULL;
}
运行服务器之后直接点击send,会出现错误
解决:
对套接字在连接之前和断开连接之后置为空,如上代码所示
客户端:
首先修改main.cpp增加一个窗口如下:
#include "serverwidget.h"
#include <QApplication>
#include"clientwidget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ServerWidget w;
w.show();
ClientWidget w2;
w2.show();
return a.exec();
}
clientwidget.ui:
clientwidget.h:
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H
#include <QWidget>
#include<QTcpSocket>//通信套接字
namespace Ui {
class ClientWidget;
}
class ClientWidget : public QWidget
{
Q_OBJECT
public:
explicit ClientWidget(QWidget *parent = nullptr);
~ClientWidget();
private slots:
void on_buttonConnect_clicked();
void on_buttonSend_clicked();
void on_buttonClose_clicked();
private:
Ui::ClientWidget *ui;
QTcpSocket *tcpSocket;//通信套接字
};
#endif // CLIENTWIDGET_H
clientwidget.cpp:
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include<QHostAddress>
ClientWidget::ClientWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWidget)
{
ui->setupUi(this);
tcpSocket = NULL;
//分配空间,指定父对象
tcpSocket = new QTcpSocket(this);
setWindowTitle("客户端");
connect(tcpSocket,&QTcpSocket::connected,[=](){
ui->textEditRead->setText("成功和服务器建立连接");
});
connect(tcpSocket,&QTcpSocket::readyRead,[=](){
//获取对方发送的内容
QByteArray array = tcpSocket->readAll();
//追加到编辑区中
ui->textEditRead->append(array);
});
}
ClientWidget::~ClientWidget()
{
delete ui;
}
void ClientWidget::on_buttonConnect_clicked()
{
//获取服务器IP和端口
QString ip = ui->lineEditIP->text();
qint16 port = ui->lineEditPort->text().toInt();
//主动和服务器建立连接
tcpSocket->connectToHost(QHostAddress(ip),port);
}
void ClientWidget::on_buttonSend_clicked()
{
//获取编辑框内容
QString str = ui->textEditWrite->toPlainText();
//发送数据
tcpSocket->write(str.toUtf8().data());
}
void ClientWidget::on_buttonClose_clicked()
{
//主动和对方断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
输出如下所示:
二、UDP通信
使用Qt提供的QUdpSocket进行UDP通信。在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发送数据。类似地,服务器也不从客户端接收连接,只负责调用接收函数,等待来自客户端的数据的到达。
在UDP通信中,服务器端和客户端的概念已经显得有些淡化,两部分做的工作都大致相同:
1. 创建套接字
2. 绑定套接字
在UDP中如果需要接收数据则需要对套接字进行绑定,只发送数据则不需要对套接字进行绑定。通过调用bind()函数将套接字绑定到指定端口上。
3. 接收或者发送数据
接收数据:使用readDatagram()接收数据
函数声明如下:
qint64 readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0)
参数:
data: 接收数据的缓存地址
maxSize: 缓存接收的最大字节数
address: 数据发送方的地址(一般使用提供的默认值)
port: 数据发送方的端口号(一般使用提供的默认值)
发送数据:使用writeDatagram()函数发送数据
函数声明如下:qint64 writeDatagram(const QByteArray & datagram, const QHostAddress & host, quint16 port)
参数:
datagram:要发送的字符串
host:数据接收方的地址
port:数据接收方的端口号
linuxUDP通信流程和QtUDP通信流程对比:
UDP通信的实现
服务端和客户端代码类似
widget.ui
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QUdpSocket>//UDP套接字
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void dealMsg();//槽函数处理对方发送过来的数据
private slots:
void on_bottonSend_clicked();
private:
Ui::Widget *ui;
QUdpSocket *udpSocket;//UDP套接字
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//分配空间,指定父对象
udpSocket = new QUdpSocket(this);
//绑定
udpSocket->bind(9999);
// udpSocket->bind(QHostAddress::AnyIPv4,9999);
// //加入某个组播
// //组播地址是D类地址
// udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2"));
// //udpSocket->leaveMulticastGroup()//退出组播
setWindowTitle("服务器端口为:9999");
//当对方成功发送数据过来
//自动触发readyRead()
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
}
void Widget::dealMsg()
{
//读取对方发送的内容
char buf[1024] = {0};
QHostAddress cliAddr;//对方地址
quint16 port;//对方端口
qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
if (len > 0)
{
//格式化[192.68..2:8888]aaaa
QString str = QString("[%1:%2] %3").arg(cliAddr.toString())
.arg(port)
.arg(buf);
//给编辑区设置内容
ui->textEdit->setText(str);
}
}
Widget::~Widget()
{
delete ui;
}
//发送按钮
void Widget::on_bottonSend_clicked()
{
//先获取对方的IP和端口
QString ip = ui->lineEditIP->text();
qint16 port = ui->lineEditPort->text().toInt();
//获取编辑区内容
QString str = ui->textEdit->toPlainText();
//给指定的IP发送数据
udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
}
同理,再写一个客户端进行通信,输出如下所示:
三、TCP/IP 和 UDP的区别
TCP/IP | UDP | |
是否连接 | 面向连接 | 无连接 |
传输方式 | 基于流 | 基于数据报 |
传输可靠性 | 可靠 | 不可靠 |
传输效率 | 效率低 | 效率高 |
能否广播 | 不能 | 能 |