close
因為是Golang的新手,因此想試著寫一個Socket Server(整體來說是給遊戲用的)來練習。
網路上找到的Socket Server在介紹Packet這塊都相當簡易,像聊天室那類的大都是拿換行符號來當Packet的切割做例子,不太符合遊戲的需求,因此寫了一個簡易的PacketHelper來處理一個完整來回的Packet。
一個完整的Packet描述如下
+------------------------------------+
+ Header | Payload | CRC----+
+ 4bytes | n bytes | 4bytes +
+------------------------------------+
只要將Accept到的net.Conn傳入給PacketHelper,後續只透過Helper來代理進行封包的Read/Write (Recv/Send)
package network
import (
"fmt"
"hash/crc32"
"net"
"io"
util "../utility"
)
//一個Packet接收的三個階段
const (
waitHead = 1
waitPayload = 2
waitCRC = 3
)
//自訂的Header大小
const HeaderSize = 4
//自訂的Crc CheckSum大小
const TailCrcSize = 4
type PacketHelper struct {
_conn net.Conn
_payloadSize int
}
//在一個Circle (head>payload>crc)完成之前,處於blocking的狀態
func (p *PacketHelper) Read() ([]byte, int, error) {
recvBuff := []byte{}
curState := waitHead
for {
switch curState {
case waitHead:
{
buf, err := ReadLimitedLength(p._conn, HeaderSize)
if err != nil {
return nil, 0, err
}
p._payloadSize = util.BytesToInt(buf)
curState = waitPayload
}
case waitPayload:
{
buf, err := ReadLimitedLength(p._conn, p._payloadSize)
if err != nil {
return nil, 0, err
}
recvBuff = append(recvBuff, buf...)
curState = waitCRC
}
case waitCRC:
{
buf, err := ReadLimitedLength(p._conn, TailCrcSize)
if err != nil {
return nil, 0, err
}
ClientCrcCheckSum := util.BytesToUInt(buf)
ServerCrcCheckSum := crc32.ChecksumIEEE(recvBuff)
if ClientCrcCheckSum != ServerCrcCheckSum {
return nil, 0, fmt.Errorf("CRC CheskSum Error!")
}
return recvBuff, p._payloadSize, nil
}
}
}
}
func (p *PacketHelper) Write(payload []byte) {
writer := util.NewBinaryWriter()
//因為自製BinaryWriter在WriteBytes時已經會寫入Size(等於是這個Packet的Header),因此不用再二次寫入Header
//writer.WriteInt(len(payload))
writer.WriteBytes(payload)
writer.WriteUInt(crc32.ChecksumIEEE(payload))
packet := writer.ToBytes()
p._conn.Write(packet)
}
func NewPacketHelper(conn net.Conn) *PacketHelper {
reader := &PacketHelper{
_conn: conn,
_payloadSize: 0,
}
return reader
}
//自訂的payload最大長度
const maximunBufferSize = 65535
//frag的Size可以限制conn讀取的長度,所以不會有讀超過的問題,但一次的Read可能低於需求量(封包特大的時候),所以必須對frag resize
func ReadLimitedLength(conn net.Conn, length int) ([]byte, error) {
if length > maximunBufferSize {
//return nil, 0, errors.New(fmt.Sprintf("Require size(%d) too long.", length))
return nil, fmt.Errorf("Require size(%d) too long.", length)
}
buffer := []byte{}
frag := make([]byte, length)
totalSize := 0
for {
size, err := conn.Read(frag)
if err != nil {
if err != io.EOF {
fmt.Println("read error:", err)
}
return nil, err
}
buffer = append(buffer, frag[:size]...)
totalSize += size
if totalSize == length {
return buffer[:totalSize], nil
}
frag = make([]byte, length-totalSize)
}
}
*Read在收完一個完整的Packet之前處於Blocking狀態
*這裡沒有做timeout等相關處理
*BinaryReader/BinaryWriter是土法煉鋼寫的utility,下篇再附上
下一篇:
[Golang] 學習筆記(2) BinaryReader/BinaryWriter
文章標籤
全站熱搜