diff --git a/PROTOCOL.md b/PROTOCOL.md
new file mode 100644
index 0000000..6f3398d
--- /dev/null
+++ b/PROTOCOL.md
@@ -0,0 +1,146 @@
+# B 站直播弹幕 websocket 协议分析
+
+## 包格式
+
+包为 byte 数据,由头部和数据组成,字节序均为大端模式
+
+头部长度为 16
+
+| 偏移量 | 长度 | 含义                      |
+| ------ | ---- | ------------------------- |
+| 0      | f    | 包总大小 package_size     |
+| 4      | 2    | 头部大小 header_size      |
+| 6      | 2    | 协议版本 protocol_version |
+| 8      | 4    | 操作码 operation          |
+| 12     | 4    | 包序列 sequence_id        |
+
+操作码含义
+
+| 操作码 | 含义                                                |
+| ------ | --------------------------------------------------- |
+| 2      | 心跳 HEARTBEAT (标记该包为心跳包)                   |
+| 3      | 心跳回应 HEARTBEAT_REPLY (有时候带着人气值数据返回) |
+| 5      | 消息 NOTIFY (B 站的弹幕或业务消息都属于这个操作码)  |
+| 7      | 认证 AUTH (标记该包为认证包)                        |
+| 8      | 认证回复 AUTH_REPLY                                 |
+
+协议版本
+
+| 协议版本 | 含义                    |
+| -------- | ----------------------- |
+| 0        | NORMAL 未压缩的正常消息 |
+| 1        | HEARTBEAT 心跳          |
+| 2        | DEFLATE zlib 压缩包     |
+| 3        | BROTLI brotil 压缩包    |
+
+## 连接建立流程
+
+1. 根据直播间 id 得到 websocket 连接地址
+
+   B 站直播间 id 分短 id 和真正的 id,如 510,605,1314,7777 等都属于短 id,需要请求
+
+   `GET https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=房间号`
+
+   得到真正的房间号,再次请求
+
+   `GET https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=真正的房间号&platform=pc&player=web`
+
+   系统会分配 3 个 websocket 连接地址 (负载均衡考虑) 和 连接 token,按照 `wss://{host}:{wss_port}/sub` (TLS WS 连接) 或 `ws://{host}:{ws_port}/sub` (WS 连接格式) 的格式拼接,即可得到 websocket 连接地址
+
+2. 建立连接后,发送认证包
+
+   建立连接后需要立即发送认证包,否则服务器会断开连接
+
+   认证包数据载荷为 json 格式的字符串,内容如下,和头部拼接时候需要 使用 utf-8 编码转化为 bytes
+
+   ```json
+   {
+     "uid": 0, // 用户id,0 代表游客
+     "roomid": 123, //你要收取弹幕的房间号
+     "protover": 3, //后面通信要使用的协议版本,主要影响处理粘包时的解码选择,选3代表后面粘包使用 brotil 压缩,
+     "platform": "web", // 平台选择web端
+     "type": 2, //不明确,填2
+     "clientver": "1.4.3", //客户端版本,填1.4.3
+     "key": "your token" //填写上一步系统给的token值
+   }
+   ```
+
+   头部操作码选择 7 (AUTH 认证) ,版本为 0 (NORMAL 未压缩的消息), sequence_id 填 1,包大小和头部大小请自行计算填写
+
+3. 定时发送心跳包
+
+   B 站服务器设置的心跳间隔约为 70s 左右,所以连接建立后每隔 30s 发送一次心跳维持连接基本稳妥。心跳包内容随意填写无影响,头部操作码选择 2 (HEARTBEAT 心跳), 版本选择 0 (NORMAL 未压缩的消息)
+
+## 收到包的消息处理
+
+服务器推送的包只会有 3 种操作码,分别为 3(心跳回应),5(消息),8(认证回复),所以我们只需要对这三种包进行处理
+
+- ### 心跳回应
+
+  心跳回应数据都是未压缩的,所以可以跳过协议判断,直接 `data[16+4:]`截取有效载荷再使用 utf-8 编码转化为字符串即可,16 是头部长度,+4 是因为数据段前面还有 4 位无效数据?(含义不明),直接去掉
+
+- ### 认证回复
+
+  认证回复为发送认证包后收到的回复,数据包无压缩,直接 `data[16:]`截取有效载荷再使用 utf-8 编码转化为字符串即可
+
+- ### 消息
+
+  首先说明,B 站对 消息 这一数据包出于服务器效率考虑会存在粘包的情况(目前的情况是一定会),在认证包发送阶段选择的 协议版本 值会在这一阶段产生影响,如果在认证时选择 0 (NORMAL 未压缩的消息) 或者 1 (HEARTBEAT),则默认用 zlib 压缩多个包,若选择 3(Brotil 压缩),则会使用 Brotil 压缩多个包。首先说明服务端的粘包逻辑,这有利于编写解包代码
+
+  - 服务器正常打包每一个数据包
+
+  ```txt
+  ----------
+  | header |
+  |--------|
+  |        |
+  |  data  |
+  |        |
+  ----------
+  ----------
+  | header |
+  |--------|
+  |        |
+  |  data  |
+  |        |
+  ----------
+
+  ```
+
+  - 使用选择的压缩协议压缩两个数据包,并封装在一个包里,包头写明使用的压缩协议
+
+  ```txt
+  ----------------
+  |    header    |
+  ----------------
+  |  ----------  |
+  |  | header |  |
+  |  |--------|  |
+  |  |        |  |
+  |  |  data  |  |
+  |  |        |  |
+  |  ----------  |
+  |  ----------  |
+  |  | header |  |
+  |  |--------|  |
+  |  |        |  |
+  |  |  data  |  |
+  |  |        |  |
+  |  ----------  |
+  ----------------
+  ```
+
+  所以判断是否粘贴包的逻辑变为
+
+  ```txt
+                      --------->数据载荷为多个包粘贴---->对每个包进行拆包提取数据
+                    /
+             zlib/brotil压缩
+                 /
+    判断头部协议版本
+                 \
+                 正常消息
+                     \
+                       -------->数据载荷为单个数据---->直接提取数据
+
+  ```
diff --git a/README.md b/README.md
index 62bbaf0..ef0c8a1 100644
--- a/README.md
+++ b/README.md
@@ -5,14 +5,17 @@
 - 简单,只需房间号即可监听
 - 异步,io 不阻塞,及时获取消息
 
+## B 站直播弹幕 websocket 协议分析
+
+[PROTOCOL 分析](./PROTOCOL.md)
+
 ## 快速开始
 
 1. 创建 app
 
-> 目前请克隆该代码仓库,并执行pip install -r rquirements.txt
+> 目前请克隆该代码仓库,并执行 pip install -r rquirements.txt
 
 ```python
-
 from blive import  BLiver
 
 app = BLiver(123) #123为房间号
@@ -21,19 +24,17 @@ app = BLiver(123) #123为房间号
 2. 创建处理器
 
 ```python
-
 from blive import  BLiver, Events, BLiverCtx
 
 app = BLiver(123)
 
-# 标记该方法监听弹幕消息,更多消息类型请参考Events类源代码
+# 标记该方法监听弹幕消息,更多消息类型请参考 Events 类源代码
 @app.on(Events.DANMU_MSG)
 async def listen_danmu(ctx: BLiverCtx):
-    danmu = DanMuMsg(ctx.body) #ctx.body套上相应的消息操作类即可得到消息的基本内容,也可直接操作ctx.body
-    print(danmu.content())
-    print(danmu.sender())
-    print(danmu.timestamp())
-
+danmu = DanMuMsg(ctx.body) #ctx.body 套上相应的消息操作类即可得到消息的基本内容,也可直接操作 ctx.body
+print(danmu.content)
+print(danmu.sender)
+print(danmu.timestamp)
 ```
 
 3. 运行
@@ -46,11 +47,11 @@ app = BLiver(123)
 @app.on(Events.DANMU_MSG)
 async def listen_danmu(ctx: BLiverCtx):
     danmu = DanMuMsg(ctx.body)
-    print(danmu.content())
-    print(danmu.sender())
-    print(danmu.timestamp())
+    print(danmu.content)
+    print(danmu.sender)
+    print(danmu.timestamp)
 
-app.run() # 运行代码!
+app.run() # 运行app!
 
 ```
 
diff --git a/app.py b/app.py
index 1f2b5ca..29e8cb0 100644
--- a/app.py
+++ b/app.py
@@ -1,7 +1,7 @@
 from blive import BLiver, Events, BLiverCtx
 from blive.msg import DanMuMsg, HotRankChangeV2Msg, InteractWordMsg, SendGiftMsg
 
-app = BLiver(605)
+app = BLiver(7777, log_level="DEBUG")
 
 
 @app.on(Events.DANMU_MSG)
diff --git a/blive/core.py b/blive/core.py
index b393258..0d090a2 100644
--- a/blive/core.py
+++ b/blive/core.py
@@ -55,7 +55,7 @@ def get_blive_dm_history(roomid):
     return resp.json()
 
 
-def certification(roomid, token, uid=0, protover=3, platform="web"):
+def certification(roomid, token, uid=0, protover=1, platform="web"):
     return {
         "uid": uid,
         "roomid": roomid,