進位制之間轉換
1. 二進位制轉八進位制 %b -> %o
2. 二進位制轉十進位制 %b -> %d
3. 二進位制轉十六進位制 %b -> %x
4. 八進位制轉二進位制 %o -> %b
5. 八進位制轉十進位制 %o -> %d
6. 八進位制轉十六進位制 %o -> %x
7. 十進位制轉二進位制 %d -> %b
8. 十進位制轉八進位制 %d -> %o
9. 十進位制轉十六進位制 %d -> %x
10. 十六進位制轉二進位制 %x -> %b
11. 十六進位制轉八進位制 %x -> %o
12. 十六進位制轉十進位制 %x -> %d
// 例
fmt.Printf("十進位制%d轉成八進位制%o",num1,num2)
%b 表示為二進位制
%c 該值對應的unicode碼值
%d 表示為十進位制
%o 表示為八進位制
%q 該值對應的單引號括起來的go語法字元字面值,必要時會採用安全的轉義表示
%x 表示為十六進位制,使用a-f
%X 表示為十六進位制,使用A-F
%U 表示為Unicode格式:U+1234,等價於"U+%04X"
%E 用科學計數法表示
%f 用浮點數表示
hex := fmt.Sprintf("%08x", i) // hex 16進位制
開啟bilibili 彈幕協議分析
#協議內容如下
相關協議連結地址 blog.csdn.net/xfgryujk/article/det...
協議的頭部是 4 2 2 4 4 總共16個位元組,加上傳送的長度,
頭部入參說明
- 第一個4個位元組 包總長度 00ff = 255
- 第二個2個位元組, 頭部長度 換算10進位制 0010 = 16
- 第三個2個位元組, 協議版本 0001
- 第四個4個位元組,0000 0007 = 7 ,7代表進入彈幕
- 第五個4個位元組 固定常數 0000 0001 = 1
返回引數:
第7到8個位元組:返回協議引數,int16型別
0:解析json值
1: 人氣值,int32
2: zip壓縮
3: brotli壓縮
00000000: 0000 00ff 0010 0001 0000 0007 0000 0001 ................
00000001: 7b22 7569 6422 3a33 3335 3230 3037 3434 {"uid":335200744
00000002: 2c22 726f 6f6d 6964 223a 3233 3230 3230 ,"roomid":232020
00000003: 3831 2c22 7072 6f74 6f76 6572 223a 332c 81,"protover":3,
00000004: 2270 6c61 7466 6f72 6d22 3a22 7765 6222 "platform":"web"
00000005: 2c22 7479 7065 223a 322c 226b 6579 223a ,"type":2,"key":
00000006: 2232 5279 5173 6f35 3432 4c77 4756 7774 "2RyQso542LwGVwt
00000007: 536f 7379 7533 2d64 436c 4e73 5843 3051 Sosyu3-dClNsXC0Q
00000008: 3938 3046 4779 6874 4847 7062 4d39 7654 980FGyhtHGpbM9vT
00000009: 4734 6566 396f 7150 5935 4c39 6772 7470 G4ef9oqPY5L9grtp
0000000a: 4b44 7153 6568 3647 3634 7667 3145 5644 KDqSeh6G64vg1EVD
0000000b: 4378 5972 566f 2d47 4171 7267 6d6e 6370 CxYrVo-GAqrgmncp
0000000c: 4147 5054 3341 6c4b 6d57 7536 326d 666e AGPT3AlKmWu62mfn
0000000d: 4772 3567 6564 4732 6677 4168 365f 7834 Gr5gedG2fwAh6_x4
0000000e: 5f59 6b49 3867 4a69 6f31 3648 5439 6542 _YkI8gJio16HT9eB
0000000f: 4873 6351 594f 4953 4a7a 773d 3d22 7d HscQYOISJzw=="}
{"uid":335200743,"roomid":23202081,"protover":3,"platform":"web","type":2,"key":"2RyQso542LwGVwtSosyu3-dClNsXC0Q980FGyhtHGpbM9vTG4ef9oqPY5L9grtpKDqSeh6G64vg1EVDCxYrVo-GAqrgmncpAGPT3AlKmWu62mfnGr5gedG2fwAh6_x4_YkI8gJio16HT9eBHscQYOISJzr=="}
驗證頭部協議
handshake := "000000ff001000010000000700000001"
等價 16位元組
byteArr := []byte{
0x00, 0x00,0x00,0xff, 0x00,0x10, 0x00,0x01, 0x00,0x00, 0x00,0x07, 0x00,0x00, 0x00,0x01,
}
t.Log(len(byteArr))
buf, err := hex.DecodeString(handshake)
t.Log("長度",len(buf))
t.Log(buf)
t.Log("二進位制" + fmt.Sprintf("%08b",buf))
t.Log("八進位制" + fmt.Sprintf("%08o",buf))
t.Log("十六進位制" + fmt.Sprintf("%08x",buf))
t.Log("十進位制" + fmt.Sprintf("%d",buf))
輸出結果:
qimiao_test.go:30: 長度 16位元組
qimiao_test.go:31: [0 0 0 255 0 16 0 1 0 0 0 7 0 0 0 1]
qimiao_test.go:32: 二進位制[00000000 00000000 00000000 11111111 00000000 00010000 00000000 00000001 00000000 00000000 00000000 00000111 00000000 00000000 00000000 00000001]
qimiao_test.go:33: 八進位制[00000000 00000000 00000000 00000377 00000000 00000020 00000000 00000001 00000000 00000000 00000000 00000007 00000000 00000000 00000000 00000001]
qimiao_test.go:34: 十六進位制000000ff001000010000000700000001
qimiao_test.go:35: 十進位制[0 0 0 255 0 16 0 1 0 0 0 7 0 0 0 1]
// "github.com/imroc/biu"
var b int32
//入參二進位制字串
err = biu.ReadBinaryString("[00000000 00000000 00000000 11111111]", &b)
fmt.Println(b,err) //255 總長度
完整程式碼:
package main
import (
"bytes"
"compress/zlib"
"encoding/binary"
"encoding/json"
"fmt"
"github.com/gorilla/websocket"
"github.com/andybalholm/brotli"
"io"
"log"
"net/http"
)
// bilibili 彈幕demo
func main() {
ws, _, err := websocket.DefaultDialer.Dial("wss://tx-gz-live-comet-02.chat.bilibili.com/sub", nil)
if err != nil {
log.Println(err)
return
}
// 根據抓包獲取
str := `{"uid":335200741,"roomid":23202081,"protover":3,"platform":"web","type":2,"key":"2RyQso542LwGVwtSosyu3-dClNsXC0Q980FGyhtHGpbM9vTG4ef9oqPY5L9grtpKDqSeh6G64vg1EVDCxYrVo-GAqrgmncpAGPT3AlKmWu62mfnGr5gedG2fwAh6_x4_YkI8gJio16HT9eBHscQYOISJzw=="}`
totalLen := 16 + len([]byte(str))
strByte := []byte(str)
dataBuff := bytes.NewBuffer([]byte{})
// 總長度 4位元組
if err := binary.Write(dataBuff,binary.BigEndian,int32(totalLen));err != nil {
log.Println(err)
return
}
// 頭部長度16 2位元組
if err := binary.Write(dataBuff,binary.BigEndian,int16(16));err != nil {
log.Println(err)
return
}
// 協議版本號 固定1 2位元組
if err := binary.Write(dataBuff,binary.BigEndian,int16(1));err != nil {
log.Println(err)
return
}
// 加入彈幕 固定協議7, 4位元組
if err := binary.Write(dataBuff,binary.BigEndian,int32(7));err != nil {
log.Println(err)
return
}
// 常量 1固定 , 4位元組
if err := binary.Write(dataBuff,binary.BigEndian,int32(1));err != nil {
log.Println(err)
return
}
err = ws.WriteMessage(websocket.BinaryMessage,append(dataBuff.Bytes(),strByte...))
if err != nil {
log.Println(err)
return
}
for {
// 等待資訊返回
_, message, err := ws.ReadMessage()
if err != nil {
log.Println(err,111)
return
}
log.Println(message[0:16],fmt.Sprintf("%08x",message[0:16]))
//0 1 2 3 4 5 6 7 8
headByte := bytes.NewBuffer(message[0:4])
var headint32 int32
err = binary.Read(headByte,binary.BigEndian,&headint32)
if err != nil {
log.Println(err,444)
return
}
log.Println("長度",headint32,headint32 - 16)
// 對第八位 解碼規則判斷 0 為正常可見的字串 2 為需要zlib解碼的關鍵資訊
buf := message[6:8]
bytesBuffer := bytes.NewBuffer(buf)
var ageen int16
err = binary.Read(bytesBuffer,binary.BigEndian,&ageen)
if err != nil {
log.Println(err,222)
return
}
switch ageen {
case 2:
b := bytes.NewReader(message[16:])
r, _ := zlib.NewReader(b)
bs, _ := io.ReadAll(r)
log.Printf("zip壓縮: %s", string(bs))
case 0:
b := message[16:]
log.Println("json彈幕", string(b))
case 1:
b := message[16:]
var renqiInt int32
bytesBuffers := bytes.NewBuffer(b)
err = binary.Read(bytesBuffers,binary.BigEndian,&renqiInt)
if err != nil {
log.Println(err,222)
return
}
log.Println("人氣:",renqiInt)
case 3:
b := bytes.NewReader(message[16:])
r := brotli.NewReader(b)
bytess,_ := io.ReadAll(r)
log.Println("brotli壓縮", string(bytess),"解壓前長度:" ,len(message[16:]) ,"解壓長度:",len(bytess))
default:
log.Println("其他:",ageen)
}
}
}
type BiRes struct {
Code int `json:"code"`
Message string `json:"message"`
Ttl int `json:"ttl"`
Data struct {
Group string `json:"group"`
BusinessId int `json:"business_id"`
RefreshRowFactor float64 `json:"refresh_row_factor"`
RefreshRate int `json:"refresh_rate"`
MaxDelay int `json:"max_delay"`
Token string `json:"token"`
HostList []struct {
Host string `json:"host"`
Port int `json:"port"`
WssPort int `json:"wss_port"`
WsPort int `json:"ws_port"`
} `json:"host_list"`
} `json:"data"`
}
type BiliReq struct {
Uid int `json:"uid"`
Roomid int `json:"roomid"`
Protover int `json:"protover"`
Platform string `json:"platform"`
Type int `json:"type"`
Key string `json:"key"`
}
func GetInfoHttpUrl() *BiRes {
var bili = new(BiRes)
res, err := http.Get("https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id=23202081&type=0")
if err != nil {
log.Println(err)
return nil
}
defer res.Body.Close()
b, err := io.ReadAll(res.Body)
err = json.Unmarshal(b, &bili)
if err != nil {
return nil
}
return bili
}
可以借鑑bilibili的資料壓縮,通過brotli,解壓前和解壓後的資料大小還是差別蠻大的
其他
int 和 []byte 互轉
//isSymbol表示有無符號
func BytesToInt(b []byte, isSymbol bool) (int, error){
if isSymbol {
return bytesToIntS(b)
}
return bytesToIntU(b)
}
//位元組數(大端)組轉成int(無符號的)
func bytesToIntU(b []byte) (int, error) {
if len(b) == 3 {
b = append([]byte{0},b...)
}
bytesBuffer := bytes.NewBuffer(b)
switch len(b) {
case 1:
var tmp uint8
err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
return int(tmp), err
case 2:
var tmp uint16
err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
return int(tmp), err
case 4:
var tmp uint32
err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
return int(tmp), err
default:
return 0,fmt.Errorf("%s", "BytesToInt bytes lenth is invaild!")
}
}
//位元組數(大端)組轉成int(有符號)
func bytesToIntS(b []byte) (int, error) {
if len(b) == 3 {
b = append([]byte{0},b...)
}
bytesBuffer := bytes.NewBuffer(b)
switch len(b) {
case 1:
var tmp int8
err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
return int(tmp), err
case 2:
var tmp int16
err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
return int(tmp), err
case 4:
var tmp int32
err := binary.Read(bytesBuffer, binary.BigEndian, &tmp)
return int(tmp), err
default:
return 0,fmt.Errorf("%s", "BytesToInt bytes lenth is invaild!")
}
}
//整形轉換成位元組
func IntToBytes(n int,b byte) ([]byte,error) {
switch b {
case 1:
tmp := int8(n)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, &tmp)
return bytesBuffer.Bytes(),nil
case 2:
tmp := int16(n)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, &tmp)
return bytesBuffer.Bytes(),nil
case 3,4:
tmp := int32(n)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, &tmp)
return bytesBuffer.Bytes(),nil
}
return nil,fmt.Errorf("IntToBytes b param is invaild")
}
hex string 和 []byte 互轉
轉 []byte
import "hex"
// 省略部分程式碼....
hexStr := "fee9ecaadafeee72d2eb66a0bd344cdd"
data, err := hex.DecodeString(hexStr)
if err != nil {
// handle error
}
轉 hex string
import (
"fmt"
"crypto/md5"
)
// 省略部分程式碼
data := "test string"
// md5.Sum() return a byte array
h := md5.Sum([]byte(data))
// with "%x" format byte array into hex string
hexStr := fmt.Sprintf("%x", h)
位元組大小端介紹
大端模式:高位位元組排放在記憶體的低地址端,低位位元組排放在記憶體的高地址端;
小端模式:低位位元組排放在記憶體的低地址端,高位位元組排放在記憶體的高地址端;
如下 彈幕協議前4個位元組如圖
十進位制: 255
大端16進位制: 000000ff
小端16進位制: ff000000
var value uint32 = 255
by := make([]byte,4)
binary.LittleEndian.PutUint32(by,value)
sixStr := hex.EncodeToString(by)
resSmall := 255 // 小端
log.Println(resSmall,sixStr,*(*byte)(unsafe.Pointer(&resSmall)) == 0xff)
// 輸出結果
2022/05/09 10:29:00 255 ff000000 true
相關連結:
github.com/lovelyyoshino/Bilibili-...
blog.csdn.net/xfgryujk/article/det...
大小端https://segmentfault.com/a/1190000039738719?utm_source=sf-similar-article#comment-area
本作品採用《CC 協議》,轉載必須註明作者和本文連結