Websocket协议解析与QT代码示例

2023-11-04

1.Websocket是什么?

    了解网络编程的朋友如果是第一次听说Websocket,但是我相信你对于socket一定不陌生。socket的中文名叫做套接字,如果两个应用程序想要进行全双工通信(每个客户端可以同时收发数据)就需要使用到socket。socket本质上就是对TCP/IP的应用进行了一层封装,可以理解为处于应用层和传输层中间。为了使应用程序可以直接调用socket API进行通信, 举个例子:服务端会首先创建一个socket,接着调用Bind()绑定本地的地址,最后调用Listen()开启监听。这个时候客户端就可以创建一个socket并且使用Connect()函数建立连接,当建立连接成功之后,服务端或者客户端就可以使用send()/recv()来收发数据,一旦建立了连接就没有客户端和服务器之分,这样就可以实现双向通信了。
    Websocket是html5规范的一个部分,它借鉴了socket的思想,为web应用程序客户端和服务端之间提供了一种全双工通信机制。

2.Websocket诞生的原因

    在Websocket出现之前,浏览器和服务器之间的通信往往采用的是HTTP协议,交互流程是浏览器发出一个请求,然后服务端接收请求后进行处理并返回结果给浏览器,最后浏览器将数据进行渲染呈现到网页上。但是HTTP有一个缺陷就是只能由客户端发起,服务端不具备推送能力。为了获取服务端最新的状态,客户端只能采取“轮询”的方式,每隔一段时间发起一个请求,但是这会导致 1.服务端被迫维持来自各个客户端的大量的不同的连接。2.大量的轮询请求会造成高开销,比如会带上多余的Header,造成无用的数据传输为了解决这些问题,Websocket由此诞生。

3.Websocket与HTTP的相同点与不同点

相同点

  • 都是基于TCP协议
  • 都是可靠的传输协议
  • 都是应用层协议
  • 默认端口也是80和443

不同点

  • Websocket是双向通信协议,HTTP是单向的
  • Websocket需要浏览器和服务器握手进行连接建立,而HTTP是浏览器发起向服务器的连接
  • 虽然HTTP/2也具备服务器推送功能,但是HTTP/2只能推送静态资源,无法推送动态内容

4.Websocket实现原理

    在讲Websocket的原理之前,首先得先了解一下HTTP短连接和长连接

  • 短链接,在HTTP1.0 客户端每次发送请求都需要重新建立TCP连接。即在一个生命周期内只有一个Request和一个Response.
  • 长连接,在HTTP1.1中默认使用长连接,在一定期限内保持TCP连接。即在一个生命周期内可以发送多个Request和接收多个Response.

    前面说了HTTP和Websocket都是基于TCP协议的,所以Websocket首先会借助于HTTP1.1协议建立通道,然后在此基础上使用Websocket进行通信。
    我们来看RFC6455文档中给出的一个WebSocket握手的例子,浏览器会发送

Get /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-Websocket-key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

    这里的Upgrade: websocket 和 Cnnection:Upgrade 是websocket的核心。
    upgrade: 是HTTP1.1中用于定义转换协议的header域。这里表示,如果服务器支持的话,客户端希望使用现有的建立好的连接,升级到websocket协议。
    Connection: HTTP1.1中规定Upgrade只能应用在“直接连接”中,所以带有Upgrade头的HTTP1.1消息必须含有Connection头,Connection的意义是任何接收到此消息的人都要在转发消息之前处理掉Connection中指定的域,不转发Upgrade域。
    Sec-Websocket-key: 是一个Base64 encode的值,是浏览器随机生成的,用来发送给服务器,服务器会使用此字段组成另一个key放在首部Sec-WebSocket-Accept 返回给客户端,用于提供基本的防护,如恶意连接。
     Sec-WebSocket-Protocol: 标识了客户端可支持的子协议(基于Websocket的应用程序协议)的列表。这里的chat,superchat只是占位符,并不存在这样的协议,子协议可以从IANA WebSocket 子协议名称注册表中选择,也可以是客户端和服务器共同理解的自定义名称。
    Sec-WebSocket-Version:标识了客户端支持的Websocket协议的版本。
    Origin: 用来指明请求的来源,Origin头部主要用于保护Websocket服务器免受非授权的跨域脚本调用Websocket API的请求。也就是不想没被授权的跨域访问与服务器建立连接,服务器可以通过这个字段来判断来源的域并有选择的拒绝。

服务端如果决定升级协议,则会向客户端返回如下响应,至此客户端和服务器连接握手成功,后续就可以进行TCP通信了。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

服务器也可以出于任何原因选择忽略该请求,在这种情况下,它只是响应,忽略Upgrade标头(例如,使用200 OK)

5.Websocket代码示例

    一直想要了解一下QML,然后就借这次机会熟悉了一下QML的语法,基于Qt-6.3.1官方的示例qmlwebsocketclient和qmlwebsocketserver两个Demo开发了两个支持websocket连接,断开连接,发送文字,发送图片功能的客户端和服务器。

效果图

  • 连接和断开连接,客户端和服务器会实时更新状态

图5.1 服务端监听


在这里插入图片描述

图5.2 websocket连接图


在这里插入图片描述

图5.3 websocket断开图


  • 客户端和服务器互发文字消息
    在这里插入图片描述
图5.3 客户端和服务器互发消息
  • 发送图片消息
    在这里插入图片描述
    图5.4 客户端选择图片


在这里插入图片描述

图5.5 服务端接收图片


核心代码

辅助类:文件读写类

//fileoperate.h
#ifndef FILEOPERATE_H
#define FILEOPERATE_H

#include <QObject>

class FileOperate : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
public:
    explicit FileOperate(QObject *parent = nullptr);

    Q_INVOKABLE QByteArray read();
    Q_INVOKABLE bool write(const QByteArray& data);

    void setSource(const QString& source);
    QString source(){return m_source;}
signals:
    void sourceChanged(const QString& source);

private:
    QString m_source;
};

#endif // FILEOPERATE_H
//fileoperate.cpp
#include "fileoperate.h"
#include <QFile>


FileOperate::FileOperate(QObject *parent)
    : QObject{parent}
{

}

QByteArray FileOperate::read()
{
   QByteArray content;
   QFile file(m_source);
   if(file.open(QIODevice::ReadOnly)){
       content = file.readAll();
       file.close();
   }
   return content;
}

bool FileOperate::write(const QByteArray &data)
{
   QFile file(m_source);
   if(file.open(QFile::WriteOnly | QFile::Truncate)){
       file.write(data);
       file.close();
       return true;
   }else{
       return false;
   }
}

void FileOperate::setSource(const QString &source)
{
    if(source!= m_source){
        m_source = source;
        emit sourceChanged(source);
    }
}

qmlwebsocketclient相关文件

//main.cpp
#include <QtGui/QGuiApplication>
#include <QQuickView>
#include "fileoperate.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    qmlRegisterType<FileOperate>("FileOperate",1,0,"FileOperate");

    QQuickView view;
    view.setSource(QUrl(QStringLiteral("qrc:/qml/qmlwebsocketclient/main.qml")));
    view.show();

    return app.exec();
}
//main.qml
import QtQuick 2.15
import QtWebSockets 1.0
import QtQuick.Controls 2.5
import Qt.labs.platform 1.1
import FileOperate 1.0

Rectangle {
    width: 500
    height: 600
    property var content: null
    WebSocket {
        id: socket
        url: "ws://127.0.0.1:80"
        onTextMessageReceived: {
            recvTextArea.append(message)
        }
        onStatusChanged: if(socket.status === WebSocket.Open){
                             messageBox.text = "Connected"
                         }else if(socket.status === WebSocket.Error) {
                             messageBox.text = "Error: " + socket.errorString
                         } else if (socket.status === WebSocket.Closed) {
                             messageBox.text = qsTr("Disconnected")
                         }
        active: false
    }

    Text {
        id: messageBox
        anchors.left: disconnectBtn.right
        anchors.top: disconnectBtn.top
        anchors.leftMargin: 5
        topPadding: 5
        color: "red"
        text: socket.status == WebSocket.Open ? qsTr("Connected") : qsTr("Welcome!")
    }

    Button{
        id: connectBtn
        x: 10
        y: 10
        text:"Connect"
        onClicked: {
            socket.active = true;
        }
    }

    Button{
        id: disconnectBtn
        anchors.left: connectBtn.right
        anchors.top: connectBtn.top
        anchors.leftMargin: 5
        text: "Disconnect"
        onClicked:{
            socket.active = false;
        }
    }



    Column{
        x: 10
        y: 50
        spacing: 2

        Text{
            id: sendLab
            color: "red"
            text: qsTr("Send message:")
        }

        TextArea{
            background: Rectangle{
                color: "lightgray"
            }
            id: sendTextArea
            width: 400
            height: 200
            Image {
                id: img
                width: 400
                height: 200
                fillMode: Image.PreserveAspectFit
            }
        }

        Row{
            spacing: 2
            Button{
                id: fileBtn
                text: "open file"
                onClicked: fileDialog.open()
            }

            Button{
                id: sendBtn
                text: "send message"
                onClicked: {
                    if (socket.status == WebSocket.Open) {
                        if(content){
                            socket.sendBinaryMessage(content);
                            img.source = "";
                            content = null;
                        }else{
                            socket.sendTextMessage(sendTextArea.text);
                            sendTextArea.clear();
                        }
                    }else{
                        messageBox.text = "Error: "+ socket.errorString;
                    }
                }
            }
        }

        Text{
            id: recvLab
            color: "blue"
            topPadding: 10
            text: qsTr("Received message:")
        }

        TextArea{
            background: Rectangle{
                color: "lightgray"
            }
            id: recvTextArea
            width: 400
            height: 200
        }

    }

    FileDialog {
        id: fileDialog
        nameFilters: ["Image Files (*.jpg *.png *.gif *.bmp *.ico)", "*.*"]
        onAccepted: {
            img.source = file
            fileOperate.source = new URL(file).pathname.substring(1);
        }
    }

    FileOperate{
        id: fileOperate
        onSourceChanged: function(source){
            console.log(source);   // D:/wallpicture/taylor.jpg
            content = read();
        }
    }
}

qmlwebsocketserver相关文件

//main.cpp
#include <QtGui/QGuiApplication>
#include <QQuickView>
#include "../qmlwebsocketclient/fileoperate.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    qmlRegisterType<FileOperate>("FileOperate",1,0,"FileOperate");
    QQuickView view;
    view.setSource(QUrl(QStringLiteral("qrc:/qml/qmlwebsocketserver/main.qml")));
    view.show();

    return app.exec();
}

//
import QtQuick 2.15
import QtWebSockets 1.0
import QtQuick.Controls 2.5
import Qt.labs.platform 1.1
import FileOperate 1.0

Rectangle {
    width: 500
    height: 600
    property var filePath: "file:///D:/wallpicture/temp/taylor.jpg"
    property var socketIns: null
    function appendMessage(message) {
        recvTextArea.append(message)
    }

    WebSocketServer {
        id: server
        listen: true
        host: "127.0.0.1"
        port: 80
        onClientConnected:function(webSocket) {
            socketIns = webSocket
            messageBox.text = qsTr("Client Connected");
            webSocket.onTextMessageReceived.connect(function(message) {
                appendMessage(message);
            });

            webSocket.onBinaryMessageReceived.connect(function(message) {
                fileOperate.source = new URL(filePath).pathname.substring(1);
                if(fileOperate.write(message)){
                    img.source = filePath;
                }
            });

            webSocket.onStatusChanged.connect(function(status){
                if(status === WebSocket.Open){
                    webSocket.sendTextMessage(qsTr("Hello Client!"));
                }else if(status === WebSocket.Error) {
                    messagebox.text = "Error: " + webSocket.errorString
                } else if (status === WebSocket.Closed) {
                    messageBox.text = qsTr("Disconnected")
                }
            });
        }
        onErrorStringChanged: {
            messageBox.text = qsTr("Server error: %1").arg(errorString);
        }

    }

    Text {
        id: messageBox
        text: qsTr("Listening.....")
        color: "red"
        anchors.fill: parent
    }

    Column{
        x: 10
        y: 30
        spacing: 2

        Text{
            id: sendLab
            color: "red"
            text: qsTr("Send message:")
        }

        TextArea{
            background: Rectangle{
                color: "lightgray"
            }
            id: sendTextArea
            width: 400
            height: 200
        }

        Row{
            spacing: 2
            Button{
                id: fileBtn
                text: "open file"
                onClicked: fileDialog.open()
            }

            Button{
                id: sendBtn
                text: "send message"
                onClicked: {
                    socketIns.sendTextMessage(sendTextArea.text)
                    sendTextArea.clear();
                }
            }
        }

        Text{
            id: recvLab
            color: "blue"
            topPadding: 10
            text: qsTr("Received message:")
        }

        TextArea{
            background: Rectangle{
                color: "lightgray"
            }
            id: recvTextArea
            width: 400
            height: 200
            Image {
                id: img
                width: 400
                height: 200
                fillMode: Image.PreserveAspectFit
            }
        }
        FileOperate{
            id: fileOperate
        }
    }
}

6.Wireshark抓包分析

Websocket 协议

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

抓包分析

在这里插入图片描述

图6.1 wireshark抓包图
  1. TCP建立连接三次握手就不多说了
  2. 发送HTTP请求升级协议
    在这里插入图片描述
图6.2 发送HTTP请求
  1. Server响应
    在这里插入图片描述
图6.3 Server响应

这里稍微提下http响应中Acess-Control-Allow-XX 字段表示了服务器对于客户端请求的方法,请求标头等字段的限制,具体含义可查 MDN标头

  1. 客户端发送文本在这里插入图片描述
图6.4 发送文本协议组成
  1. 客户端发送图片
图6.5 发送图片协议组成
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Websocket协议解析与QT代码示例 的相关文章

  • 完全彻底卸载QT Creator

    问题 如何从 Linux 机器上卸载 QT Creator 我的 Debian Jessie 机器上的安装已损坏 我尝试过重新安装 修复等 但没有成功 建议我完全卸载 获取最新版本并重新安装 问题是我不确定如何执行此操作 每次我尝试时 QT
  • 如何在Qt 5中的paintEvent上使用mouseMoveEvent?

    我是 Qt 和 c 的新手 所以我遇到了一些困难 我正在尝试创建一个小部件 它可以获取 mouseMoveEvent 位置并在鼠标位置的像素图上绘制椭圆 下面你可以看到代码 include myimage h include
  • 将 jstring 转换为 QString

    我正在调用一个返回字符串的 Java 函数 QAndroidJniObject obj QAndroidJniObject callStaticObjectMethod
  • QTableView 并双击一个单元格

    我正在开发测试用例编辑器 该编辑器包含 USART 传输和接收数据包格式 编辑器是一个表格视图 发送和接收数据包的长度为八个字节 例如 0x01 0x02 0x03 0x08 它在我的第五和第六栏中 现在 我希望此列中的单元格为只读 但是当
  • 了解 Qt3D 创建的网格

    我创建了一个 Qt3D 网格 如下所示 Qt3DCore QEntity newEntity new Qt3DCore QEntity Qt3DExtras QConeMesh mesh new Qt3DExtras QConeMesh m
  • 如何销毁角度工厂实例

    一方面 我有几个工厂 每个工厂都控制一个 websocket 另一方面 其中一个工厂应该在客户端登录时启动 因此 if user isLogged injector get NotificationsWebsocket 这就是我动态初始化工
  • 即使我在单独的线程中运行,QT GUI 也会冻结

    我有一个小型聊天应用程序 其中使用 SQLite 数据库来存储所有对话 我注意到该应用程序随机冻结 然后我必须最小化和最大化它才能使其再次工作 我认为问题可能是 SQLite 选择 插入导致 gui 冻结 我决定尝试将所有 SQLite 方
  • QFileSystemModel setRootPath

    我正在尝试创建一个 Qt 应用程序来显示文件夹 Mac OS 中的 Users 文件夹 的内容 这是代码 QFileSystemModel dirModel new QFileSystemModel dirModel gt setRootP
  • Q风格所有权

    在 Qt 应用程序中使用样式时 我遇到了一个有趣的问题QStyle所有权 QStyle继承自QObject 通常接受QObject parent作为构造函数参数来管理其子级的生命周期 但QStyle的构造函数没有此构造函数参数 第一个问题
  • 为什么 QT 设计器重新调整大小或不允许我缩小或展开小部件或按钮?

    很多时候 在使用 QT 设计器时 我发现自己需要通过缩小或扩展来调整事物的大小 每当我尝试这样做时 程序都不允许我这样做 而只是恢复到将对象放置在窗口中时给我的原始默认大小 无论我的布局如何 为什么要这样做 是否有可能改变这一点 以便我可以
  • 在 Qt 中自动调整标签文本大小 - 奇怪的行为

    在 Qt 中 我有一个复合小部件 它由排列在 QBoxLayouts 内的多个 QLabels 组成 当小部件调整大小时 我希望标签文本缩放以填充标签区域 并且我已经在 resizeEvent 中实现了文本大小的调整 这可行 但似乎发生了某
  • 如何从 Spring 4 stomp websocket 方法获取/设置主体和会话属性

    我正在做实验Spring 4 websocket 和 stomp http docs spring io spring docs 4 0 x spring framework reference htmlsingle websocket s
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • 在 Qt 中,许多插槽连接到同一信号,它们在发出信号时是否按顺序调用?

    In the Qt文件说 如果多个插槽连接到一个信号 则这些插槽将 按照它们连接的顺序一个接一个地执行 当信号发出时 但在connect 功能 设置Qt ConnectionType输入为Qt QueuedConnection意思是 当控制
  • 使 QLabel 的像素图透明

    我有一个带有 QLabel 和像素图的主窗口 我想让它透明 或不那么不透明 我正在使用下面的代码 ui gt label gt setAttribute Qt WA TranslucentBackground ui gt label gt
  • Qt:更改 Mac OS X 上的应用程序 QMenuBar 内容

    我的应用程序对多个 页面 使用 QTabWidget 其中顶级菜单根据用户所在的页面而变化 我的问题是 尝试重新创建菜单栏的内容会导致严重的显示问题 它在除 Mac OS X 之外的所有平台上按预期使用第一种和第三种样式 尚未测试第二种 但
  • 如何为 Windows 安装开源 Qt 库 5 二进制版本

    这个问题具体是关于Qt libraries 5 0 0 for Windows VS 2010 406 MB at http qt project org downloads http qt project org downloads 但我
  • 使用 QWT 构建时出错

    我收到一个错误 undefined reference to QwtPlot QwtPlot QWidget 当我尝试构建我的项目时 即使设置中一切看起来都很好 在我的 CmakeLists txt 中我有 include director
  • QWebEngineView 在 load() 或 page() 方法上崩溃

    我正在致力于将 Qt 5 5 QWebView 项目移植到 Qt 5 6 测试版 QWebEngine 我已经阅读了移植指南here https wiki qt io Porting from QtWebKit to QtWebEngine
  • 如何在不声明 32 个插槽的情况下将 32 个按钮的 pressed() 信号连接到单个函数?

    我有一个小部件 里面有 32 个按钮 我需要将每个按钮的 Pressed 信号连接到一个插槽 以便调用一个函数 该函数的参数取决于我按下的按钮 现在我通过以 on QPushButtonName pressed 的形式添加 32 个插槽来做

随机推荐

  • Invalid configuration `x86_64-unknown-linux-gnu': machine

    checking host system type Invalid configuration x86 64 unknown linux gnu machine x86 64 unknown not recognized 在做 config
  • Gamemaker studio2经验(4)——打字机效果

    问题概述 在很多游戏中 算了实在不好意思写引言了 就直说啦 如果你是UT粉 想用gm搞搞UT的同人作品但是又无从下手 那么请看过来 对于RPG类游戏 文字交流系统是不可或缺的 但是gm的文字系统 实在有些一言难尽 那没办法 谁让gm是真爱呢
  • 本地计算机架设http服务器,Http File Server(简易Http服务器服务端)

    如果您感觉配置IIS和apache等web服务端太麻烦的话 不妨试试Http File Server Http File Server是一套简易的Http服务器服务端系统 它无需安装 运行后简单配置一下就可以开放80端口了 与传统Web服务
  • iframe嵌入本地视频或者http链接视频禁止自动播放

    本地视频禁止自动播放 http链接视频禁止自动播放 在视频链接后加上 autoplay 0即可
  • windows连网疑问

    如果我的笔记本连接一台无线AP 当我不更改SSID 而只去更改密码的时候 为什么会连不上 当我把电脑里关于这个AP的连接信息删掉之后 就能够连接上了 所以 是跟之前保存的信息有关系的 但其中的作用原理不是很清楚 不过 我想 这必定是wind
  • ubuntu16.04下teamviewer启动不显示界面

    导读 在Ubuntu下使用teamviewer的时候 通过命令行输入 teamviewer 不会出现界面 就像这样 没有显示teamviewer的界面 adminuser adminuser pc teamviewer Init Check
  • linux 系统调用

    5 1 5 如何使用系统调用 如图5 2所示 用户应用可以通过两种方式使用系统调用 第一种方式是通过C库函数 包括系统调用在C库中的封装函数和其他普通函数 图5 2 使用系统调用的两种方式 第二种方式是使用 syscall宏 2 6 18版
  • VUE路由的hash模式与history模式的区别

    hash模式url带 号 history模式不带 号 通过history api 我们丢掉了丑陋的 但是它也有个问题 不怕前进 不怕后退 就怕刷新 f5 如果后端没有准备的话 因为刷新是实实在在地去请求服务器的 不玩虚的 在hash模式下
  • 使用fio测试磁盘I/O性能

    测试准备 工具 fio Flexible IO Tester 官方网站 http freecode com projects fio http brick kernel dk snaps 注意 性能测试建议直接通过写裸盘的方式进行测试 会得
  • JavaScript之js对象终极序列化(可序列化函数)

    案例官方地址 http www rainx org 2017 01 04 javascript js E5 AF B9 E8 B1 A1 E7 BB 88 E6 9E 81 E5 BA 8F E5 88 97 E5 8C 96 你是否遇到了
  • PTA 习题3.6 一元多项式的乘法与加法运算

    文章目录 题目 1 加法多项式 2 乘法多项式 代码 题目 设计函数分别求两个一元多项式的乘积与和 输入格式 输入分2行 每行分别先给出多项式非零项的个数 再以指数递降方式输入一个多项式非零项系数和指数 绝对值均为不超过1000的整数 数字
  • win11安装linux子系统配置C++开发环境的系列教程

    本期主要记录在Win11系统下安装ubuntu20 04LTS并完成相关配置 在整个过程中 博主也踩过了不少坑 参考了许多优秀作者的资源 将配置过程整理总结如下 第一步 配置安装ubuntu 其实主要做的事情就是 调整 Win 配置 下载配
  • nginx参数tcp_nopush和tcp_nodelay

    参数说明 你的数据传输并不需要总是准确地遵守某一选项或者其它选择 在那种情况下 你可能想要采取更为灵活的措施来控制网络连接 在发送一系列当作单一消息的数据之前设置TCP CORK 而且在发送应立即发出的短消息之前设置TCP NODELAY
  • Mybatis 个人总结

    目录 MyBatis 框架 第一章 框架的概述 1 三层架构 2 三层架构处理的流程 3 为什么使用三层架构 4 三层框架模式和框架 5 框架 6 框架解决的问题 7 jdbc访问数据库的优缺点 8 Mybatis框架 第二章 Mybati
  • H3C配置ssh密码认证登录

    注 在用户使用SSH登录交换机时 交换机对所要登录的用户使用密码对其进行身份验证 生成RSA和DSA密钥对 H3C rsa local key pair create H3C public key local create dsa 设置用户
  • MinGW-w64 C/C++编译器下载和安装

    目录 1 安装包下载 方法一 下载Installer在线安装 费时 方法二 下载离线包 较快 2 环境变量配置 3 小试牛刀 如果电脑没有安装MinGW w64 C C 编译器 在Windows的命令窗口键入gcc会提示 gcc 不是内部或
  • 单元测试_接口测试用例设计思路

    下图是 单元测试之道Java版 的脑图笔记 加上在工作中的总结
  • 重置标准输出

    import org apache log4j Appender import org apache log4j Logger import org apache log4j WriterAppender import java io Bu
  • 配置IDEA下springboot项目显示Run Dashboard面板

    在 idea workspace xml 中搜索
  • Websocket协议解析与QT代码示例

    文章目录 1 Websocket是什么 2 Websocket诞生的原因 3 Websocket与HTTP的相同点与不同点 相同点 不同点 4 Websocket实现原理 5 Websocket代码示例 效果图 核心代码 6 Wiresha