TCP服务端
TCP通信过程
三次握手 四次挥手
连接:
Client → Server:SYN
Server→Client:SYN、ACK Client→Server:ACK
断开:
主动断开方 :FIN、ACK
被动断开方:ACK
Server→Client:FIN、ACK Client→Server:ACK
要在pro中加入 network,才能使用对应的头文件
QtTcp通信模型
需要掌握的接口
- connectToHost(const QHostAddress &address, quint16 port,…
- bool waitForConnected(int msecs = 30000)
- bool waitForReadyRead(int msecs = 30000)
- void disconnectFromHost()
- void connected()
- void disconnected()
- void readyRead()
- void bytesWritten(qint64 bytes)
服务端Demo
- widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkAccessManager> // 找网卡
#include <QNetworkInterface> // 找网卡
#include <QMessageBox>
#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 slots:
void on_pushButtonListen_clicked();
void newConnectSlot();
void readyReadSlot();
private:
void enumIpAddress();
private:
Ui::Widget *ui;
QTcpServer *server;
QTcpSocket *socket; // socket不是new出来的
};
#endif // WIDGET_H
- Widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent),
ui(new Ui::Widget),
server(new QTcpServer)
{
ui->setupUi(this);
enumIpAddress();
connect(server,SIGNAL(newConnection()),
this,SLOT(newConnectSlot()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::enumIpAddress()
{
// 枚举网卡
QList<QHostAddress> list = QNetworkInterface::allAddresses();
QStringList ipAddress;
foreach (QHostAddress address, list) {
if(address.isNull())
{
continue;
}
// 网络层协议 过滤IPV6
QAbstractSocket::NetworkLayerProtocol protocol =
address.protocol();
if(protocol == QAbstractSocket::IPv4Protocol)
{
QHostAddress ipv4 = address;
qDebug()<< ipv4.toString();
ipAddress.append(address.toString());
// ui->comboBox->addItem(address.toString());
}
ui->comboBoxAddress->addItems(ipAddress);
}
}
void Widget::on_pushButtonListen_clicked()
{
QString serverAddressStr;
QHostAddress serverAddress; // 需要监听的网咖ip
quint16 serverPort;
serverAddressStr = ui->comboBoxAddress->currentText();
serverAddress = QHostAddress(serverAddressStr);
serverPort = ui->lineEditPort->text().toInt();
// 判断server的状态
if(!server->isListening())
{
// 已经监听的不能在监听
if(!server->listen(serverAddress,serverPort))
{
qDebug()<<"Tcp server listen error!" << server->errorString();
QMessageBox::warning(this,"",server->errorString());
}
else
{
qDebug()<<"listen OK!";
ui->pushButtonListen->setText(("停止监听"));
}
}
else
{
server->close();
ui->pushButtonListen->setText(("启用监听"));
}
}
void Widget::newConnectSlot()
{
QHostAddress clientIpAddress; // 客户段地址
quint16 clientPort;
// 得到与客户端通信的socket
socket = server->nextPendingConnection();
// 得到ip地址
clientIpAddress= socket->peerAddress();
// 得到端口
clientPort = socket->peerPort();
ui->textBrowserClientInfo->append("client Address" +
clientIpAddress.toString());
ui->textBrowserClientInfo->append("client Port"+
QString::number(clientPort));
connect(socket, SIGNAL(readyRead()),
this,SLOT(readyReadSlot()));
}
void Widget::readyReadSlot()
{
QByteArray recvData;
recvData = socket->readAll();
ui->textBrowserRecv->append(QString(recvData));
}
客户端Demo
- widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpSocket>
#include <QMessageBox>
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_date_clicked();
void on_pushButton_connect_clicked();
void connectedSlot();
void disconnectedSlot();
void readyReadSlot();
void bytesWrittenSlot(qint64 bytes);
private:
Ui::Widget *ui;
QTcpSocket * socket;
bool socketState;
};
#endif // WIDGET_H
- widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent),
ui(new Ui::Widget),
socket(new QTcpSocket),
socketState(false)
{
ui->setupUi(this);
connect(socket, SIGNAL(connected()),
this, SLOT(connectedSlot()));
connect(socket, SIGNAL(disconnected()),
this, SLOT(disconnectedSlot()));
connect(socket, SIGNAL(readyRead()),
this, SLOT(readyReadSlot()));
connect(socket, SIGNAL(bytesWritten(qint64)),
this, SLOT(bytesWrittenSlot(qint64)));
}
Widget::~Widget()
{
if(socket->isOpen())
socket->close();
delete socket;
delete ui;
}
void Widget::connectedSlot()
{
qDebug()<<"connectedSlot";
}
void Widget::disconnectedSlot()
{
qDebug()<<"disconnectedSlot";
ui->pushButton_connect->setText(QStringLiteral("连接服务器"));
socketState = false;
}
void Widget::readyReadSlot()
{
qDebug()<<"readyReadSlot";
QByteArray dataBa = socket->readAll();
int recvNum = dataBa.size();
ui->textBrowserRecv->append(QString(dataBa));
ui->lineEditRecvTotal->setText(QString::number(recvNum));
}
/* 统计写了多少数据 */
void Widget::bytesWrittenSlot(qint64 bytes)
{
qDebug()<<"bytesWrittenSlot";
ui->lineEditSendTotal->setText(QString::number(bytes));
}
void Widget::on_pushButton_date_clicked()
{
QString dataStr = ui->textEditSend->toPlainText();
QByteArray data = dataStr.toUtf8();
if(socket->isOpen() && socket->isValid())
{
socket->write(data);
}
else
{
QMessageBox::warning(this, "socket error", socket->errorString());
}
}
void Widget::on_pushButton_connect_clicked()
{
QString ipAddressStr = ui->lineEditAddress->text();
qint16 port = ui->lineEditPort->text().toInt();
if(!socketState)
{
socket->connectToHost(ipAddressStr, port);
if(socket->waitForConnected(1000))
{
qDebug()<<"Connect2Server Ok";
ui->pushButton_connect->setText(QStringLiteral("断开连接"));
socketState = true;
}
else
{
qDebug()<<socket->errorString();
//
return;
}
}
else
{
socket->close();
}
}
信号与槽的最后一个参数(信号与槽的连接方式)
方式 | 值 | 描述 |
---|---|---|
Qt::AutoConnection | 0 | 默认参数;自动连接, 自动选择是Direct或者Queued |
Qt::DirectConnection | 1 | 槽函数立即执行,槽与信号在同一线程中执行 |
Qt::QueuedConnection | 2 | 槽函数排队执行,槽函数在接收方的对象所在的线程中执行 |
Qt::BlockingQueuedConnection | 3 | 阻塞式排队连接 |
Qt::UniqueConnection | 4 | 一对一的自动连接 |
UDP
单播
发送数据不用绑定,只要知道接收端的IP和端口即可;
接收数据一定要绑定;
QString str = QString("[%1 : %2 : %3]")
.arg(sender.toString())
.arg(port)
.arg(dataBuf);
广播
255.255.255.255
组播Demo
服务端
组播地址是D类地址
1、绑定 2、加入组播 3、接收
- widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
#include <QNetworkAccessManager> //找网卡
#include <QNetworkInterface> //找网卡
#include <QHostAddress>
#include <QDebug>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
void enumIpAddress();
public slots:
void processRecvGroupSlot();
private slots:
void on_pushButtonBind_clicked();
private:
Ui::Widget *ui;
QUdpSocket *udpSocket;
QHostAddress localAddress;
quint16 localPort;
};
#endif // WIDGET_H
- widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
udpSocket(new QUdpSocket),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->enumIpAddress();
}
void Widget::enumIpAddress()
{
QList<QHostAddress> list = QNetworkInterface::allAddresses();
QStringList addrList;
foreach (QHostAddress address, list)
{
if(address.isNull())
continue;
//网络层协议 过滤只剩下IPV4的地址
QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
if(protocol != QAbstractSocket::IPv4Protocol)
continue;
QHostAddress ipAddress = address;
qDebug() << ipAddress.toString();
addrList.append(ipAddress.toString());
}
ui->comboBoxLocalAddress->addItems(addrList);
}
void Widget::processRecvGroupSlot()
{
QHostAddress remoteIpAddress;
quint16 remotePort;
QByteArray recvData;
while (udpSocket->hasPendingDatagrams())
{
remoteIpAddress = udpSocket->peerAddress();
remotePort = udpSocket->peerPort();
ui->lineEditRemoteIpAddress->setText(remoteIpAddress.toString());
ui->lineEditPort->setText(QString::number(remotePort));
recvData = udpSocket->readAll();
qDebug() << "data in..." << recvData;
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButtonBind_clicked()
{
localAddress = QHostAddress(ui->comboBoxLocalAddress->currentText());
localPort = ui->lineEditPort->text().toInt();
if(!udpSocket->bind(localAddress, localPort))
{
qDebug()<<"udpSocket->bind"<<udpSocket->errorString();
return;
}
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(processRecvGroupSlot()));
}
客户端
- 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_pushButton_clicked();
private:
Ui::Widget *ui;
QUdpSocket udpSocket;
};
#endif // WIDGET_H
- widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
udpSocket(new QUdpSocket),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//((const char *)&m_HeadBits,QHostAddress(MCAST_ADDR), STATUS_PORT);
udpSocket.writeDatagram("www.baidu.com",
QHostAddress(ui->lineEditSenderIpAddress->text()),
ui->lineEditSenderPort->text().toInt());
}