参考:
socket(套接字):四元组:客户端ip+端口号port+服务端ip+端口号port,保证这是绝对唯一的连接(这是一个整体)。端口号最多有65535个。
客户端包括:
①应用层
②传输控制层:TCP、UDP。TCP是面向连接的、可靠的传输。TCP包括:三次握手、数据传输、四次分手
③网络层
④链路层
⑤物理层
1.数据传输结束后,客户端的应用进程发出连接释放报文段,并停止发送数据,客户端进入FIN_WAIT_1状态,此时客户端依然可以接收服务器发送来的数据。
2.服务器接收到FIN后,发送一个ACK给客户端,确认序号为收到的序号+1,服务器进入CLOSE_WAIT状态。客户端收到后进入FIN_WAIT_2状态。
3.当服务器没有数据要发送时,服务器发送一个FIN报文,此时服务器进入LAST_ACK状态,等待客户端的确认
4.客户端收到服务器的FIN报文后,给服务器发送一个ACK报文,确认序列号为收到的序号+1。此时客户端进入TIME_WAIT状态,等待2MSL(MSL:报文段最大生存时间),然后关闭连接。
四层TCP/IP模型如下:
HTTP协议和HTTPS协议区别如下:
1.HTTP协议是以明文的方式在网络中传输数据,而HTTPS协议传输的数据则是经过TLS加密后的,HTTPS具有更高的安全性
2.HTTPS在TCP三次握手阶段之后,还需要进行SSL 的handshake,协商加密使用的对称加密密钥
3.HTTPS协议需要服务端申请证书,浏览器端安装对应的根证书
4.HTTP协议端口是80,HTTPS协议端口是443
HTTPS优点:
1.HTTPS传输数据过程中使用密钥进行加密,所以安全性更高
2.HTTPS协议可以认证用户和服务器,确保数据发送到正确的用户和服务器
HTTPS缺点:
1.HTTPS握手阶段延时较高:由于在进行HTTP会话之前还要进行SSL握手,因此HTTPS协议握手阶段延时增加
2.HTTPS部署成本高:一方面HTTPS协议需要使用证书来验证自身的安全性,所以需要购买CA证书;另一方面由于采用HTTPS协议需要进行加解密的计算,占用CPU资源较多,需要的服务器配置或数目高。
TCP客户端:首先创建套接字socket(),然后连接服务器connect(),连接之后和服务器进行通信,发送数据send()和接收数据rev(),最后关闭套接字close()。
示例场景:TCP客户端和服务端,通过子线程处理将客户端的文件发送给服务器
环境:Ubuntu16.04 + QT5.12.9 + CMake3.21
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(tcp_client LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Widgets REQUIRED Network)
if(ANDROID)
add_library(tcp_client SHARED
main.cpp
tcpclient.cpp
tcpclient.h
tcpclient.ui
resources.qrc
)
else()
add_executable(tcp_client
main.cpp
tcpclient.cpp
tcpclient.h
tcpclient.ui
resources.qrc
)
endif()
target_link_libraries(tcp_client PRIVATE Qt5::Widgets Qt5::Network)
tcpclient.ui
#include "tcpclient.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TcpClient w;
w.show();
w.setWindowTitle("TCP - 客户端");
return a.exec();
}
tcpclient.h
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QHostAddress>
#include <QLabel>
QT_BEGIN_NAMESPACE
namespace Ui {
class TcpClient;
}
QT_END_NAMESPACE
class TcpClient : public QMainWindow
{
Q_OBJECT
public:
TcpClient(QWidget *parent = nullptr);
~TcpClient();
private slots:
void on_pbn_connect_clicked();
void on_pbn_disconnect_clicked();
void on_pbn_send_message_clicked();
private:
Ui::TcpClient *ui;
QTcpSocket *tcp_{ nullptr };
QLabel *status_{ nullptr };
};
#endif // TCPCLIENT_H
tcpclient.cpp
#include "tcpclient.h"
#include "./ui_tcpclient.h"
TcpClient::TcpClient(QWidget *parent)
: QMainWindow(parent), ui(new Ui::TcpClient)
{
ui->setupUi(this);
ui->le_port->setText("8000");
ui->le_ip->setText("127.0.0.1");
ui->pbn_disconnect->setEnabled(false);
// 创建通信的套接字对象
tcp_ = new QTcpSocket(this);
// 检测服务器是否回复了数据
connect(tcp_, &QTcpSocket::readyRead, [=] {
// 接收服务器发送的数据
QByteArray recv_msg = tcp_->readAll();
ui->txe_message->append("服务器Say: " + recv_msg);
});
// 检测是否和服务器是否连接成功了
connect(tcp_, &QTcpSocket::connected, this, [=]() {
ui->txe_message->append("恭喜, 连接服务器成功!!!");
status_->setPixmap(QPixmap(":/resources/connect.png").scaled(20, 20));
ui->pbn_connect->setEnabled(false);
ui->pbn_disconnect->setEnabled(true);
});
// 检测服务器是否和客户端断开了连接
connect(tcp_, &QTcpSocket::disconnected, this, [=]() {
ui->txe_message->append("服务器已经断开了连接...");
ui->pbn_connect->setEnabled(true);
ui->pbn_disconnect->setEnabled(false);
status_->setPixmap(
QPixmap(":/resources/disconnect.png").scaled(20, 20));
});
// 设置连接状态的状态栏
status_ = new QLabel(this);
status_->setPixmap(QPixmap(":/resources/disconnect.png").scaled(20, 20));
ui->statusbar->addWidget(new QLabel("连接状态:"));
ui->statusbar->addWidget(status_);
}
TcpClient::~TcpClient()
{
delete ui;
}
void TcpClient::on_pbn_connect_clicked()
{
QString ip = ui->le_ip->text();
unsigned short port = ui->le_port->text().toInt();
// 连接服务器
tcp_->connectToHost(QHostAddress(ip), port);
ui->pbn_connect->setEnabled(false);
ui->pbn_disconnect->setEnabled(true);
}
void TcpClient::on_pbn_disconnect_clicked()
{
tcp_->close();
ui->pbn_connect->setEnabled(true);
ui->pbn_disconnect->setEnabled(false);
}
void TcpClient::on_pbn_send_message_clicked()
{
QString send_msg = ui->txe_send_message->toPlainText();
tcp_->write(send_msg.toUtf8());
ui->txe_message->append("客户端Say: " + send_msg);
ui->txe_send_message->clear();
}
运行效果:
CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(tcp_server LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Widgets REQUIRED Network)
if(ANDROID)
add_library(tcp_server SHARED
main.cpp
tcpserver.cpp
tcpserver.h
tcpserver.ui
resources.qrc
)
else()
add_executable(tcp_server
main.cpp
tcpserver.cpp
tcpserver.h
tcpserver.ui
resources.qrc
)
endif()
target_link_libraries(tcp_server PRIVATE Qt5::Widgets Qt5::Network)
tcpserver.ui
main.cpp
#include "tcpserver.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TcpServer w;
w.show();
w.setWindowTitle("TCP - 服务器");
return a.exec();
}
tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QLabel>
QT_BEGIN_NAMESPACE
namespace Ui {
class TcpServer;
}
QT_END_NAMESPACE
class TcpServer : public QMainWindow
{
Q_OBJECT
public:
TcpServer(QWidget *parent = nullptr);
~TcpServer();
private slots:
void on_pbn_set_listen_clicked();
void on_pbn_send_data_clicked();
private:
Ui::TcpServer *ui;
QTcpServer *server_{ nullptr };
QTcpSocket *tcp_{ nullptr };
QLabel *status_{ nullptr };
};
#endif // TCPSERVER_H
tcpserver.cpp
#include "tcpserver.h"
#include "./ui_tcpserver.h"
TcpServer::TcpServer(QWidget *parent)
: QMainWindow(parent), ui(new Ui::TcpServer)
{
ui->setupUi(this);
ui->le_port->setText("");
// 第一步:创建监听的服务对象
server_ = new QTcpServer(
this); // 指定实例化父类this,即QMainWindow,待页面析构时,server_也被析构
// 第三步:通过 QTcpServer::newConnection()信号检测是否有新的客户端连接
// 如果有新的客户端连接调用 QTcpSocket *QTcpServer::nextPendingConnection()
// 得到通信的套接字对象
connect(server_, &QTcpServer::newConnection, this, [=]() {
tcp_ = server_->nextPendingConnection();
ui->txe_record->append("成功和客户端建立了新的连接...");
status_->setPixmap(QPixmap(":/resources/connect.png").scaled(20, 20));
// 检测是否有客户端数据
connect(tcp_, &QTcpSocket::readyRead, this, [=]() {
// 接收数据
QByteArray data = tcp_->readAll();
ui->txe_record->append("客户端Say:" + data);
});
// 检测客户端是否断开了连接
connect(tcp_, &QTcpSocket::disconnected, this, [=]() {
ui->txe_record->append("客户端已经断开了连接...");
tcp_->deleteLater();
status_->setPixmap(
QPixmap(":/resources/disconnect.png").scaled(20, 20));
});
});
// 设置连接状态的状态栏
status_ = new QLabel(this);
status_->setPixmap(QPixmap(":/resources/disconnect.png").scaled(20, 20));
ui->statusbar->addWidget(new QLabel("连接状态:"));
ui->statusbar->addWidget(status_);
}
TcpServer::~TcpServer()
{
delete ui;
}
// 第二步:通过 QTcpServer 对象设置监听,即:QTcpServer::listen()
void TcpServer::on_pbn_set_listen_clicked()
{
unsigned short port = ui->le_port->text().toUShort();
// 设置服务器监听
server_->listen(QHostAddress::Any, port);
ui->pbn_set_listen->setEnabled(false);
}
void TcpServer::on_pbn_send_data_clicked()
{
// 将txe_send_message中输入内容转为纯文本的QString形式
QString msg = ui->txe_send_message->toPlainText();
// 将QSting类型转为QByteArray类型
tcp_->write(msg.toUtf8());
ui->txe_record->append("服务端Say:" + msg);
ui->txe_send_message->clear();
}
运行效果:
1.打开TCP-服务端和TCP-客户端
2.点击TCP-服务器中启动监听服务器,打开端口的监听,修改TCP-客户端中服务端端口,改为,点击连接服务器。完成客户端和服务端的连接。
3.客户端和服务端之间可进行通讯。
因篇幅问题不能全部显示,请点此查看更多更全内容