7个月前 0 1 243
这一篇继续上一篇文章的之后, 进行帧包的解析,当然是简洁的实现文本消息, 因为帧包里面的信息很多,就不一一弄了, 有兴趣的可以试试
//定义一个64位的长度整形
var datalength int64
//定义个结构体
type webSocket struct {
Mask []byte
Conn net.Conn
}
func WebSocket(conn net.Conn) *webSocket {
return &webSocket{Conn:conn}
}
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/63) |
|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 |
+----------------------------------------------------------------
下面一一解释包的说明
FIN 长度1位,表示是否是最后一帧,为1则表示最后一帧,为0则表示还有后续帧
RSV 长度三位 默认都是0 如果服务端与客户端没有协商,那么非0则认为是一个错误的帧
opcode 表示帧格式,占4位,格式如下
0x00,表示继续帧
0x01,表示文本帧
0x02,表示二进制帧
0x03-0x07,保留的未定义非控制帧
0x08,连接关闭帧
0x09,表示ping
0xA,表示pong
0xB-0xF,用于保留的控制帧
MASK,1位,定义负载数据是否使用掩码,1为使用掩码,0为不使用掩码
Payload Length,7位,7+16位,7+64位,定义负载数据的长度,以字节为单位。这部分如果为0-125,则负载长度则就是这段定义的长度,如果为126,之后的 Extend payload Length 16位将作为负载长度,如果为127,那么之后的Extend payload Length 64位将作为负载长度。
Masking-key 0 或者 32位,mask位设为0,则该字段缺失(不过协议要求,所有的帧都需要使用mask)
Payload data 负载数据=扩展数据+应用数据
//读取
func (this *webSocket)readFrame() string {
//解析第一个字节位
first := make([]byte, 1)
this.Conn.Read(first)
//获取FIN值,0代表数据未结束 1 代表数据结束
FIN := first[0] >> 7
RSV1 := first[0] >> 6 & 1
RSV2 := first[0] >> 5 & 1
RSV3 := first[0] >> 4 & 1
log.Println(FIN, RSV1, RSV2, RSV3)
OPCODE := first[0] & 0xF
log.Println(OPCODE)
//解析第二个字节位
second := make([]byte, 1)
this.Conn.Read(second)
//获取MASK值
MASK := second[0] >> 7
log.Println(MASK)
payLength := second[0] & 0x7F
fmt.Println(int(payLength))
datalength = int64(payLength)
//如果payload 长度为126则读取后面的两个字节 数据长度
if payLength == 126 {
extendByte := make([]byte, 2)
this.Conn.Read(extendByte)
datalength = uint16(binary.BigEndian.Uint16(extendByte))
}
//如果payload 长度为127则读取后面的两个字节 数据长度
if payLength == 127 {
extendByte := make([]byte, 8)
this.Conn.Read(extendByte)
datalength = unit64(binary.BigEndian.Uint64(extendByte))
}
// 读取Masking-key
maskSec := make([]byte, 4)
if MASK == 1 {
this.Conn.Read(maskSec)
}
//读取数据
data := make([]byte, datalength)
this.Conn.Read(data)
if MASK == 1 {
var i int64
for i = 0; i < datalength; i++ {
data[i] ^= maskSec[i % 4]
}
}
fmt.Println(FIN)
// 如果FIN 为 1 表示是最后一个数据帧
if FIN == 1 {
return string(data)
}
getNextData := this.readFrame()
data = append(data, getNextData...)
return string(data)
}
//发送包
/**
给客户端发送数据, 也要以帧格式发送, 这里以不做掩码处理, 如果需要就自行改变帧的二进制格式
*/
func (this *webSocket) writeFrame(data []byte) {
length := len(data)
buf := make([]byte, 10+length)
// 数据开始和结束的位置
payloadStart := 2
// 数据帧的第一个字节, 不支持分片,且值能发送文本类型数据 二进制格式为 1000 0001
buf[0] = 0x81
// 数据帧第二个字节,服务器发送的数据不需要进行掩码处理
if length < 125 {
buf[1] = byte(0x00) | byte(length)
} else if (length > 125 && length < 65536) {
buf[1] = byte(0x00) | 126
binary.BigEndian.PutUint16(buf[payloadStart:], uint16(length))
payloadStart += 2
} else {
buf[1] = byte(0x00) | 127
binary.BigEndian.PutUint64(buf[payloadStart:], uint64(length))
payloadStart += 8
}
// 复制
copy(buf[payloadStart:], data)
this.Conn.Write(buf)
}
package main
import (
"net"
"fmt"
"log"
"encoding/binary"
"strings"
"crypto/sha1"
"io"
"encoding/base64"
)
const (
WEBSOCKET_KEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
// 文本数据帧类型
)
var datalength int64
type webSocket struct {
Mask []byte
Conn net.Conn
}
func WebSocket(conn net.Conn) *webSocket {
return &webSocket{Conn:conn}
}
func main() {
connect()
}
func connect() {
ln, err := net.Listen("tcp", ":8000")
if err != nil {
fmt.Println(err)
}
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err)
}
for {
handConnect(conn)
}
}
}
func handConnect(conn net.Conn) {
content := make([]byte, 1024)
n, err := conn.Read(content)
if err != nil {
fmt.Println(err)
}
fmt.Println(fmt.Sprintf("读取%d个字节", n))
header := parseHeaders(string(content))
fmt.Println(header["Sec-WebSocket-Key"])
secret := getSecret(header["Sec-WebSocket-Key"])
response := "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
response += "Upgrade: websocket\r\n"
response +="Connection: Upgrade\r\n"
response +="Sec-WebSocket-Accept: " + secret +"\r\n"
response += "\r\n"
conn.Write([]byte(response))
for {
ws := WebSocket(conn)
data := ws.readFrame()
fmt.Println("data is :", data)
ws.writeFrame([]byte("i recive a message as you konw"))
}
}
func (this *webSocket)readFrame() string {
//解析第一个字节位
first := make([]byte, 1)
this.Conn.Read(first)
//获取FIN值,0代表数据未结束 1 代表数据结束
FIN := first[0] >> 7
RSV1 := first[0] >> 6 & 1
RSV2 := first[0] >> 5 & 1
RSV3 := first[0] >> 4 & 1
log.Println(FIN, RSV1, RSV2, RSV3)
OPCODE := first[0] & 0xF
log.Println(OPCODE)
//解析第二个字节位
second := make([]byte, 1)
this.Conn.Read(second)
//获取MASK值
MASK := second[0] >> 7
log.Println(MASK)
payLength := second[0] & 0x7F
fmt.Println(int(payLength))
datalength = int64(payLength)
//如果payload 长度为126则读取后面的两个字节 数据长度
if payLength == 126 {
extendByte := make([]byte, 2)
this.Conn.Read(extendByte)
datalength = int64(binary.BigEndian.Uint16(extendByte))
}
//如果payload 长度为127则读取后面的两个字节 数据长度
if payLength == 127 {
extendByte := make([]byte, 8)
this.Conn.Read(extendByte)
datalength = int64(binary.BigEndian.Uint16(extendByte))
}
// 读取Masking-key
maskSec := make([]byte, 4)
if MASK == 1 {
this.Conn.Read(maskSec)
}
//读取数据
data := make([]byte, datalength)
this.Conn.Read(data)
if MASK == 1 {
var i int64
for i = 0; i < datalength; i++ {
data[i] ^= maskSec[i % 4]
}
}
fmt.Println(FIN)
// 如果FIN 为 1 表示是最后一个数据帧
if FIN == 1 {
return string(data)
}
getNextData := this.readFrame()
data = append(data, getNextData...)
return string(data)
}
/**
给客户端发送数据, 也要以帧格式发送, 这里以不做掩码处理, 如果需要就自行改变帧的二进制格式
*/
func (this *webSocket) writeFrame(data []byte) {
length := len(data)
buf := make([]byte, 10+length)
// 数据开始和结束的位置
payloadStart := 2
// 数据帧的第一个字节, 不支持分片,且值能发送文本类型数据 二进制格式为 1000 0001
buf[0] = 0x81
// 数据帧第二个字节,服务器发送的数据不需要进行掩码处理
if length < 125 {
buf[1] = byte(0x00) | byte(length)
} else if (length > 125 && length < 65536) {
buf[1] = byte(0x00) | 126
binary.BigEndian.PutUint16(buf[payloadStart:], uint16(length))
payloadStart += 2
} else {
buf[1] = byte(0x00) | 127
binary.BigEndian.PutUint64(buf[payloadStart:], uint64(length))
payloadStart += 8
}
// 复制
copy(buf[payloadStart:], data)
this.Conn.Write(buf)
}
func parseHeaders(content string) map[string]string {
h := strings.Split(content, "\r\n")
header := make(map[string]string, 0)
for _, value := range h {
v := strings.Split(value, ":")
if len(v) >= 2 {
header[strings.Trim(v[0], " ")] = strings.Trim(v[1], " ")
}
}
return header
}
func getSecret(key string) string{
key += WEBSOCKET_KEY
res := sha1.New()
io.WriteString(res, key)
return base64.StdEncoding.EncodeToString(res.Sum(nil))
}
试一试吧,前台
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
yanwenwu
#1 6个月前