websocket协议简介

概念介绍

  • 单工通信:数据传输只允许在一个方向上传输,只能一方发送数据,另一方接收数据并发送。
  • 半双工:数据传输允许两个方向上的传输,但在同一时间内,只可以有一方发送或接收数据。
  • 全双工:同时可进行双向数据传输。

websocket介绍

  • WebSocket协议在2008年诞生,2011年成为国际标准。
  • WebSocket是一种在单个TCP连接上进行全双工通信的协议,位于 OSI 模型的应用层。
  • WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。

出现背景

  • HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。
  • 这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。很多网站为了实现推送技术,所用的技术都是轮询。即在特定得时间间隔,由浏览器对服务器发出http请求。

websocket与HTTP比较

http不同版本简介

相同点

  • 都是一样基于TCP的,都是可靠性传输协议。
  • 都是应用层协议。

不同点

  • websocket 是持久连接,http 是短连接;
  • websocket 的协议是以 ws/wss 开头,http 对应的是 http/https;
  • websocket 是有状态的,http 是无状态的;
  • websocket 连接之后服务器和客户端可以双向发送数据,http 只能是客户端发起一次请求之后,服务器才能返回数据;
  • websocket 连接建立之后,不需要再发送request请求,数据直接从TCP通道传输。

联系

  • WebSocket在建立握手时,数据是通过HTTP传输的。

websocket协议简介_第1张图片

websocket握手过程

  • 1、浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
  • 2、TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)
  • 3、服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
  • 4、当收到了连接成功的消息后,通过TCP通道进行传输通信。

HTTP协议头

http协议头详解

请求

  • Accept: text/html,application/xhtml+xml,application/xml
  • Accept-Encoding: gzip, deflate, br
  • Accept-Language: zh-CN,zh;q=0.9
  • Connection: keep-alive
  • Host: www.baidu.com
  • User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36

响应

  • Connection: keep-alive
  • Content-Encoding: gzip
  • Content-Type: text/html;charset=utf-8
  • Date: Sat, 16 Apr 2022 10:43:46 GMT
  • Server: BWS/1.1

websocket协议头

请求

  • *Accept-Encoding: gzip, deflate
  • *Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
  • Connection: Upgrade
  • Host: 192.168.1.2:8080
  • Sec-WebSocket-Key: 821VqJT7EjnceB8m7mbwWA==
  • Sec-WebSocket-Version: 13
  • Upgrade: websocket
  • User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.39

响应

  • Connection: Upgrade
  • Date: Sat, 16 Apr 2022 10:49:05 GMT
  • Sec-WebSocket-Accept: paFykwJusIMnfpohWxA5HVpjD1Q=
  • Server: Server
  • Upgrade: websocket

websocket头详解

  • 请求头详解
  • Upgrade: 向服务器指定协议类型,告诉web服务器当前使用的是websocket协议
  • Sec-WebSocket-Key:是一个 Base64 encode 的值,这个是浏览器随机生成的
  • Sec-WebSocket-Version:websocket协议版本
  • 响应头详解(web服务返回状态码101表示协议切换成功)
  • Sec-WebSocket-Accept: 是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。用来证明客户端和服务器之间能进行通信了。

代码展示

服务端

服务端程序是通过QT实现

  • websocketservice.h
#ifndef WEBSOCKETSERVER_H
#define WEBSOCKETSERVER_H

#include 
#include 
#include 
#include 
#include 

namespace Ui {
class WebSocketServer;
}

class WebSocketServer : public QWidget
{
    Q_OBJECT

public:
    explicit WebSocketServer(QWidget *parent = 0);
    ~WebSocketServer();

private slots:
    void on_pushButton_startListen_clicked();
    void onNewConnection();
    void onTextMessageReceived(QString msg);
    void onDisconnected();


    void on_pushButton_send_clicked();

private:
    Ui::WebSocketServer *ui;
    QWebSocketServer *server;
    QWebSocket *socket;
    QList clientList;

    QString mAddr;
    int mPort;
};

#endif // WEBSOCKETSERVER_H
  • websocketservice.cpp
#include "websocketserver.h"
#include "ui_websocketserver.h"

WebSocketServer::WebSocketServer(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WebSocketServer)
{
    ui->setupUi(this);
    ui->pushButton_send->setEnabled(false);


    //获取本机IP和端口
    QString hostName = QHostInfo::localHostName();   //获取主机名
    QHostInfo hostInfo = QHostInfo::fromName(hostName); //获取主机信息
    QList addList = hostInfo.addresses(); //获取IP地址列表

    QString localIP;
    if(!addList.isEmpty())
    {
        for(int i = 0; i < addList.count();i++)
        {
            QHostAddress aHost = addList.at(i);
            if(QAbstractSocket::IPv4Protocol == aHost.protocol())
            {
                localIP = aHost.toString();
                break;
            }
        }
    }

    ui->lineEdit_url->setText(localIP);
    ui->lineEdit_port->setText("8080");


    //构造:QWebSocketServer(const QString& serverName,QWebSocketServer::SslMode secureMode,QObject *parent=nullptr)
    //使用给定的serverName构造一个新的QWebSocketServer。
    //该服务器名称将在HTTP握手阶段被用来识别服务器。它可以为空,此时不会将服务器名称发送给客户端。
    //SslMode指示服务器是通过wss(SecureMode)还是ws(NonSecureMode)运行
    //QWebSocketServer::SecureMode服务器以安全模式运行(通过wss)
    //QWebSocketServer::NonSecureMode服务器以非安全模式运行(通过ws)

    server=new QWebSocketServer("Server",QWebSocketServer::NonSecureMode,this);

    //有新的连接
    connect(server,&QWebSocketServer::newConnection,this,&WebSocketServer::onNewConnection);
}

WebSocketServer::~WebSocketServer()
{
    delete ui;
}

void WebSocketServer::on_pushButton_startListen_clicked()
{
    QHostAddress address = QHostAddress(ui->lineEdit_url->text());
    if(server->listen(address, ui->lineEdit_port->text().toInt())){
        ui->pushButton_startListen->setEnabled(false);
        ui->pushButton_startListen->setText("disListen");
    }
}

void WebSocketServer::onNewConnection()
{
    socket=server->nextPendingConnection();
    mAddr = socket->peerAddress().toString();
    mPort = socket->peerPort();

    ui->plainTextEdit_clientStatus->appendPlainText("[" + mAddr + ":" + QString::number(mPort) + "]" + " connected...");

    ui->pushButton_send->setEnabled(true);

    connect(socket,&QWebSocket::textMessageReceived, this, &WebSocketServer::onTextMessageReceived);

    ui->plainTextEdit_sendMsg->clear();
    ui->plainTextEdit_sendMsg->appendPlainText("welcome to connect Server!");

    //断开连接时
    connect(socket,&QWebSocket::disconnected, this, &WebSocketServer::onDisconnected);
}

void WebSocketServer::onTextMessageReceived(QString msg)
{
    ui->plainTextEdit_recvMsg->appendPlainText(msg);
}

void WebSocketServer::onDisconnected()
{
    ui->plainTextEdit_clientStatus->appendPlainText("[" + mAddr + ":" + QString::number(mPort) + "]" + " disConnected!");
}

void WebSocketServer::on_pushButton_send_clicked()
{
    socket->sendTextMessage(ui->plainTextEdit_sendMsg->toPlainText());
}

客户端





    
    
    Qt WebSocket Demo



    
    
    

Recv:



Send:



演示

  • 可以看到只需发一次request请求,后续就不需要再发request请求,可以直接进行数据的传输,并且服务端也能主动发送消息给客户端
    websocket协议简介_第2张图片
    websocket协议简介_第3张图片

抓包分析

  • 本地无法进行抓包,因此我下载了一个简易的网络库mongoose,可以把这个传到Linux服务器上,它可以实现websocket服务。
  • mongoose-master/examples/websocket-server 这个目录下就是websocket服务程序,修改main.c中的ip和端口,直接make就可以启动。同时该目录下有test.html的测试文件,我们可以拷贝到本地进行测试。
  • 客户端给服务端发送一个hello word,同时服务端也回复了一个hello word
    websocket协议简介_第4张图片
  • 可以看到,客户端发送请求时,先通过TCP建立连接,然后发送了一个HTTP的GET请求,服务端通过HTTP返回一个101,101表示的就是协议转换,告诉客户端下面要转换协议了。再往下就是通过websocket协议进行的一次数据交互。
  • 客户端GET请求详细信息。客户端通过Upgrade字段告诉服务端要使用websocket协议。
    websocket协议简介_第5张图片
  • 服务端响应详细信息。服务端返回101,表示进行协议的切换。
    websocket协议简介_第6张图片
  • 协议协商完成后,后面就是通过websocket协议进行数据传输
    websocket协议简介_第7张图片

你可能感兴趣的:(网络协议,网络协议,websocket)