0%

NSIS(Nullsoft Scriptable Install System)

Macros are used to insert code at compile time, depending on defines and using the values of the defines. The macro’s commands are inserted at compile time. This allows you to write general code only once and use it a lot of times but with few changes

如上所述,宏的作用是“insert”代码,和通常的编程语言中的#define是一样的。insert的锚点标识是!insertmacro

宏指令

1
2
3
4
5
6
7
!macro Hello
DetailPrint "Hello world"
!macroend

Section Test
!insertmacro Hello
SectionEnd

含参宏

1
2
3
4
5
6
7
8
9
!macro Hello What
DetailPrint "Hello ${What}"
!macroend

Section Test
!insertmacro Hello "World"
!insertmacro Hello "Tree"
!insertmacro Hello "Flower"
SectionEnd

node-ffi (Node.js Foreign Function Interface)

install:

1
npm i ffi

如果install的ffi有问题,可以拉source下来编译
compile:
1
2
3
4
npm i -g node-gyp
git clone git://github.com/node-ffi/node-ffi.git
cd node-ffi
node-gyp rebuild

Node FFI Tutorial

node-ffi 停止更新当前不支持最新版本node.js,事实上 经QQs实践基于node10的electron6无法与之集成构建,Github上有替代方案 node-ffi-napi

ws: a Node.js WebSocket library

Version npm
Linux Build
Windows Build
Coverage Status

ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
server implementation.

Passes the quite extensive Autobahn test suite: server,
client.

Note: This module does not work in the browser. The client in the docs is a
reference to a back end with the role of a client in the WebSocket
communication. Browser clients must use the native
WebSocket
object(QQs: browser client should call the Websocket constructor provided by browser, see the example at bottom of this article). To make the same code work seamlessly on Node.js and the browser, you
can use one of the many wrappers available on npm, like
isomorphic-ws.

Table of Contents

Protocol support

  • HyBi drafts 07-12 (Use the option protocolVersion: 8)
  • HyBi drafts 13-17 (Current default, alternatively option
    protocolVersion: 13)

Installing

1
npm install ws

Opt-in for performance and spec compliance

There are 2 optional modules that can be installed along side with the ws
module. These modules are binary addons which improve certain operations.
Prebuilt binaries are available for the most popular platforms so you don’t
necessarily need to have a C++ compiler installed on your machine.

  • npm install --save-optional bufferutil: Allows to efficiently perform
    operations such as masking and unmasking the data payload of the WebSocket
    frames.
  • npm install --save-optional utf-8-validate: Allows to efficiently check if a
    message contains valid UTF-8 as required by the spec.

API docs

See /doc/ws.md for Node.js-like documentation of ws classes and
utility functions.

WebSocket compression

ws supports the permessage-deflate extension which enables
the client and server to negotiate a compression algorithm and its parameters,
and then selectively apply it to the data payloads of each WebSocket message.

The extension is disabled by default on the server and enabled by default on the
client. It adds a significant overhead in terms of performance and memory
consumption so we suggest to enable it only if it is really needed.

Note that Node.js has a variety of issues with high-performance compression,
where increased concurrency, especially on Linux, can lead to catastrophic
memory fragmentation
and slow performance. If you intend to use
permessage-deflate in production, it is worthwhile to set up a test
representative of your workload and ensure Node.js/zlib will handle it with
acceptable performance and memory usage.

Tuning of permessage-deflate can be done via the options defined below. You can
also use zlibDeflateOptions and zlibInflateOptions, which is passed directly
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].

See [the docs][ws-server-options] for more options.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const WebSocket = require('ws');

const wss = new WebSocket.Server({
port: 8080,
perMessageDeflate: {
zlibDeflateOptions: {
// See zlib defaults.
chunkSize: 1024,
memLevel: 7,
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
// Other options settable:
clientNoContextTakeover: true, // Defaults to negotiated value.
serverNoContextTakeover: true, // Defaults to negotiated value.
serverMaxWindowBits: 10, // Defaults to negotiated value.
// Below options specified as default values.
concurrencyLimit: 10, // Limits zlib concurrency for perf.
threshold: 1024 // Size (in bytes) below which messages
// should not be compressed.
}
});

The client will only use the extension if it is supported and enabled on the
server. To always disable the extension on the client set the
perMessageDeflate option to false.

1
2
3
4
5
const WebSocket = require('ws');

const ws = new WebSocket('ws://www.host.com/path', {
perMessageDeflate: false
});

Usage examples

Sending and receiving text data

1
2
3
4
5
6
7
8
9
10
11
const WebSocket = require('ws');

const ws = new WebSocket('ws://www.host.com/path');

ws.on('open', function open() {
ws.send('something');
});

ws.on('message', function incoming(data) {
console.log(data);
});

Sending binary data

1
2
3
4
5
6
7
8
9
10
11
12
13
const WebSocket = require('ws');

const ws = new WebSocket('ws://www.host.com/path');

ws.on('open', function open() {
const array = new Float32Array(5);

for (var i = 0; i < array.length; ++i) {
array[i] = i / 2;
}

ws.send(array);
});

Simple server

1
2
3
4
5
6
7
8
9
10
11
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});

ws.send('something');
});

External HTTP/S server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');

const server = https.createServer({
cert: fs.readFileSync('/path/to/cert.pem'),
key: fs.readFileSync('/path/to/key.pem')
});
const wss = new WebSocket.Server({ server });

wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});

ws.send('something');
});

server.listen(8080);

Multiple servers sharing a single HTTP/S server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const http = require('http');
const WebSocket = require('ws');
const url = require('url');

const server = http.createServer();
const wss1 = new WebSocket.Server({ noServer: true });
const wss2 = new WebSocket.Server({ noServer: true });

wss1.on('connection', function connection(ws) {
// ...
});

wss2.on('connection', function connection(ws) {
// ...
});

server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;

if (pathname === '/foo') {
wss1.handleUpgrade(request, socket, head, function done(ws) {
wss1.emit('connection', ws, request);
});
} else if (pathname === '/bar') {
wss2.handleUpgrade(request, socket, head, function done(ws) {
wss2.emit('connection', ws, request);
});
} else {
socket.destroy();
}
});

server.listen(8080);

Client authentication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const http = require('http');
const WebSocket = require('ws');

const server = http.createServer();
const wss = new WebSocket.Server({ noServer: true });

wss.on('connection', function connection(ws, request, client) {
ws.on('message', function message(msg) {
console.log(`Received message ${msg} from user ${client}`);
});
});

server.on('upgrade', function upgrade(request, socket, head) {
authenticate(request, (err, client) => {
if (err || !client) {
socket.destroy();
return;
}

wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit('connection', ws, request, client);
});
});
});

server.listen(8080);

Also see the provided example using express-session.

Server broadcast

A client WebSocket broadcasting to all connected WebSocket clients, including
itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});

A client WebSocket broadcasting to every other connected WebSocket clients,
excluding itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});

echo.websocket.org demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const WebSocket = require('ws');

const ws = new WebSocket('wss://echo.websocket.org/', {
origin: 'https://websocket.org'
});

ws.on('open', function open() {
console.log('connected');
ws.send(Date.now());
});

ws.on('close', function close() {
console.log('disconnected');
});

ws.on('message', function incoming(data) {
console.log(`Roundtrip time: ${Date.now() - data} ms`);

setTimeout(function timeout() {
ws.send(Date.now());
}, 500);
});

Use the Node.js streams API

1
2
3
4
5
6
7
8
9
10
const WebSocket = require('ws');

const ws = new WebSocket('wss://echo.websocket.org/', {
origin: 'https://websocket.org'
});

const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' });

duplex.pipe(process.stdout);
process.stdin.pipe(duplex);

Other examples

For a full example with a browser client communicating with a ws server, see the
examples folder.

Otherwise, see the test cases.

FAQ

How to get the IP address of the client?

The remote IP address can be obtained from the raw socket.

1
2
3
4
5
6
7
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws, req) {
const ip = req.connection.remoteAddress;
});

When the server runs behind a proxy like NGINX, the de-facto standard is to use
the X-Forwarded-For header.

1
2
3
wss.on('connection', function connection(ws, req) {
const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
});

How to detect and close broken connections?

Sometimes the link between the server and the client can be interrupted in a way
that keeps both the server and the client unaware of the broken state of the
connection (e.g. when pulling the cord).

In these cases ping messages can be used as a means to verify that the remote
endpoint is still responsive.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const WebSocket = require('ws');

function noop() {}

function heartbeat() {
this.isAlive = true;
}

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
ws.isAlive = true;
ws.on('pong', heartbeat);
});

const interval = setInterval(function ping() {
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate();

ws.isAlive = false;
ws.ping(noop);
});
}, 30000);

wss.on('close', function close() {
clearInterval(interval);
});

Pong messages are automatically sent in response to ping messages as required by
the spec.

Just like the server example above your clients might as well lose connection
without knowing it. You might want to add a ping listener on your clients to
prevent that. A simple implementation would be:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const WebSocket = require('ws');

function heartbeat() {
clearTimeout(this.pingTimeout);

// Use `WebSocket#terminate()`, which immediately destroys the connection,
// instead of `WebSocket#close()`, which waits for the close timer.
// Delay should be equal to the interval at which your server
// sends out pings plus a conservative assumption of the latency.
this.pingTimeout = setTimeout(() => {
this.terminate();
}, 30000 + 1000);
}

const client = new WebSocket('wss://echo.websocket.org/');

client.on('open', heartbeat);
client.on('ping', heartbeat);
client.on('close', function clear() {
clearTimeout(this.pingTimeout);
});

How to connect via a proxy?

Use a custom http.Agent implementation like https-proxy-agent or
socks-proxy-agent.

Changelog

We’re using the GitHub releases for changelog entries.

License

MIT

[node-zlib-deflaterawdocs]:
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options

[ws-server-options]:
https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback

Browser Client

1
2
3
4
5
6
7
8
9
10
11
12
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});

Branch

  • master
  • dev
  • feature/xxxx
  • release/1.x.x
  • hotfix/1.x.x

    Tag

    hotfix 合并到master 以及 dev
    1
    2
    git checkout master
    git merge --no-ff hotfix/1.x.x

    关于QA的讨论

    曾认为feature代码在开发确认功能,过静态检查、单元测试、code review后合并至dev,即feature所在的生命周期由开发团队维护,从dev做release交QA进行全量测试
    实际上在本团队,feature的DOD(defination of done)规定在合并到dev前需要通过QA测试,对合并后再发的’版本’也肯定要测的。
    对于本团队的实践,是否存在QA过早介入,测试是否重复的问题,fcc大佬如是说:多数情况下new feature的业务/逻辑上的确认只能人工进行,这里的人是QA的人是合理的,该‘确认’过程不包含在code review(主要针对编码质量)中,若无QA的确认,则存在合并后被QA拒绝的风险,而拆解合并后的代码可能代价颇高。
    另:了解gitlab

change main without

使用Git Parameter简化GitFlow工作方式的持续集成

  • 安装Git Parameter Plugin
  • 设置 Jenkins Job ‘this project is parameterized’
  • 设置变量BRANCH_NAME,变量类型为Branch or Tag, 默认为master
  • 设置 Source Code Management - Branches to build 为 $BRANCH_NAME

完成后可使用该项目的build with Parameters功能,即选择特定分支进行构建

日常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git config --global user.email "QQs@xxxx.xxx"
git config --global user.name "QQs"
git init
git remote add origin git@xxxx.xxx:projectX.git
// 添加远程主机命名为origin
git branch -r
// 查看远程分支
git pull origin dev:develop
// 拉取origin上的dev分支 命名为develop
git add .
git commit -m "blablabla"
git push origin dev
// 将修改推送到origin上的dev分支上
// clone and rename
git clone git@xxxxxx YourFolderName

clone指定分支

1
git clone -b release git@xxxx.xxx:projectX.git

创建分支

1
git checkout -b bug/fixXXXissue

忽略本地修改拉取远程分支

1
2
git fetch --all
git reset --hard origin/master

可以认为git pull是git fetch和git merge两个步骤的结合

git pull <远程主机名> <远程分支名>:<本地分支名>

//取回远程主机某个分支的更新,再与本地的指定分支合并。

1
2
3
4
5
6
7
8
git fetch origin master:temp 
//在本地新建一个temp分支,并将远程origin仓库的master分支代码下载到本地temp分支
git diff tmp
//来比较本地代码与刚刚从远程下载下来的代码的区别
git merge tmp
//合并temp分支到本地的master分支
git branch -d temp
//删除temp分支

回退

1
git reset --hard cd8462xxxx

即将HEAD游标指向到之前的一次commit

git clean -fxd

-f —force
-x 移除包括被gitignore忽略的文件 支持-e参数(if have)
-X 移除所有git ignore的文件 用于从头开始重建一切
-d 递归并移除untrack目录

部分检出

1
2
git config core.sparsecheckout true
echo /db/* >> .git/info/sparse-checkout

reset revert

1
2
3
4
git reset --soft <commit> // 重置到<commit>版本,修改内容放入缓存区(unstage)
git reset --hard <commit> // 重置到<commit>版本,修改内容永久删除

git revert <commit-last> .. <commit-somewhere> // 提交一个记录来撤销所罗列出的<commit>

rebase

1
2
3
4
git checkout feature/new_featueXXX
git rebase develop
// resolve merge conflict
git rebase --continue
1
git rebase -i <start-commit> <end-commit> //(start-commit, end-commit] 前开后闭区间,合并到当前分支,默认 end-commit 为当前 HEAD

把 develop rebase 进 feature/new_featueXXX 分支,develop为上游(Upstream), checkout 的new_featueXXX 分支为Currnet Branch.

每将一次develop的commit rebase进feature 都合并为一个中间版本commit,然后 git rebase —continue。实际中,rebase过程中可能产生冲突,如果两条分支都含有多次commit,且修改内容相互渗透,产生很多冲突,continue时是个中间版本 很难保证复合变基的逻辑吧 那将使这种”规范”失去意义 索性直接merge算了

rebase的取消
rebase完成后没有与该操作对应的commit记录,即不改变前后commit的个数(只调整顺序)但是git本身是有所有操作的记录的,因此任何操作都可以回退, 使用git reflog显示这些记录 并选择标记头进行回退

1
2
3
git reflog
---显示action历史---
git reset --hard HEAD{10}

git rebase 的撤销

包括git reset到之前的版本,此时HEAD会指向到旧版本,较新的commit不在git log中可见了,可以通过git reflog查看 tip: git reflog —date=iso查看操作时间
找到commit的SHA号码 git reset到它即可

git cherry-pick

1
2
git checkout develop
git cherry-pick f2ef69d 9839b06

例如f2ef69d 9839b06是release上刚修好的bug,可以使用上述命令将两处修改直接复制到develop分支

repository 迁移

1
2
3
4
5
git clone --bare git@old-repo.git 
cd old-repo
git remote add bitbucket git@bitbucket-repo.git
git push --all bitbucket
git push --tags bitbucket

stale branches 和 remote tracking branch

remote tracking branch是一个引用(reference),表示是远端分支的状态,不应被修改

stale branch是远端已经移除的remote tracking branchStackOverflow:What is a “stale” git branch?

git log

退出日志文本是按q,同vim

查看所有分支对当前目录的修改,并显示所修改文件:
git log —stat —graph —all

stage

git status
git add/rm
git reset ./temp.txt
git checkout —

tag

标签tag用于标记一个commit

1
git tag -a v1.0.3 -m "bump version to v1.0.3"

使用git tag命令查看所有标签,使用git checkout检出指定标签版本
移除尚未推送到远端的标签:
1
git tag -d v1.0.3

git blame

submodule

为项目添加子模块

1
git submodule add https://example.com/demo/lib1 lib1

关联了子模块的项目含有.gitmoudles文件 形如
1
2
3
4
5
6
7
8
9
[submodule "lib1"]
path = lib1
url = https://example.com/demo/lib1
[submodule "lib2"]
path = lib2
url = https://example.com/demo/lib2
[submodule "lib3"]
path = lib3
url = https://example.com/demo/lib3

拉取项目后源码中不包含这些子项目 使用git submodule命令更新
1
2
3
git submodule init lib1 lib2 #init命令可以按需初始化 lib1 lib2写入项目config
git submodule update


大型项目递归拉取submodule
1
git submodule update --init --recursive

ssh协议改https

1
2
3
git remote add temp_remote_name https://xxxxxxx.git
git remote remove origin
git remote rename temp_remote_name origin

troubleshooting

error: object file .git/objects/61/9151e2619bc36c3c4f5f0c86432b2ca651706d is empty fatal: loose object 619151e2619bc36c3c4f5f0c86432b2ca651706d (stored in .git/objects/61/9151e2619bc36c3c4f5f0c86432b2ca651706d) is corrupt

尝试用下列方法修复

1
2
3
# 删除.git/objects/*/目录下的空文件
git fsck --full
git gc --auto

git fsck命令用于检查文件有效性和连贯性
git gc 清理不必要的文件并优化本地存储库

Postman
postform-data-auth
postform-data
Node.js Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const express = require('express');
const app = express();
var multer = require('multer');
var upload = multer();

// Parse URL-encoded bodies (as sent by HTML forms)
app.use(express.urlencoded());

// for parsing multipart/form-data
app.use(upload.array());
app.use(express.static('public'));
app.post('/', function(request, response){
console.log(request.body);
response.json({res:'ok',data:request.body})
});

const listener = app.listen(process.env.PORT || 3000, function () {
console.log('Your app is listening on port ' + listener.address().port);
});

Node.js client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const http = require('http')
const FormData = require('form-data')

function btoa(str) {
return Buffer.from(str).toString('base64')
}
const formData = new FormData();
formData.append('username', 'QQs');
formData.append('password', btoa('*****'));
const authorizationData = 'Basic ' + btoa('authorizationID:authorizationPassword');
const options = {
hostname: 'localhost',
port: 3000,
path: '',
method: 'POST',
headers: formData.getHeaders(),
auth: authorizationData
};
const req = http.request(options, (res) => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
formData.pipe(req);

Angular httpclient
1
2
3
4
5
6
7
8
9
10
11
12
13
const formData = new FormData();
formData.append('username', 'QQs');
formData.append('password', btoa('*****'));
const postUrl = 'http://loacalhost:3000/';
const authorizationData = 'Basic ' + btoa('authorizationID:authorizationPassword');
const headers = new HttpHeaders({
'Authorization': authorizationData
});
return this.http.post(postUrl, postData, { headers: headers }).pipe(
map(response => {

})
)

html form
1
2
3
4
5
<form action="http://authorizationID:authorizationPassword@localhost:3000/" method="post" enctype="multipart/form-data">
<input type="text" name="username" id="username">
<input type="text" name="password" id="password">
<input type="submit" value="Post">
</form>