同時支持websocket和socket的輕量框架

前言:

通過之前的幾篇文章我們詳細(xì)的介紹到了一個socket框架應(yīng)該怎么架構(gòu),需要些什么模塊,可是美中不足的就是它只支持最簡單的socket協(xié)議,不能夠滿足實際生產(chǎn)情況蟀拷,于是我便對此框架進(jìn)行了改造,讓它能夠同時支持websocket 和 socket 萍聊,而且插件式 注冊问芬,當(dāng)需要別的長連接協(xié)議的時,完全可以自己定制寿桨。已經(jīng)把所有代碼整合了此衅,希望給個星星支持一下 microSocket

實現(xiàn)基礎(chǔ):

一切編程皆socket 亭螟,這話有點說的絕對挡鞍。但是仔細(xì)想想,確實也就是那么回事媒佣,網(wǎng)絡(luò)通信現(xiàn)在99%都是socket吧匕累。我們有n多種協(xié)議,但是都是socket的默伍,所以這些協(xié)議無非就是 握手 解包 封包 上面不同 欢嘿,那我們把這些 過程 單獨封裝 不就能夠 寫一個框架能夠隨意切換 協(xié)議了么!

代碼實現(xiàn):

廢話不多說 我們直接看代碼 也糊!

type SocketTypes interface{
    ConnHandle(msf *Msf,sess *Session)
    Pack(data []byte)[]byte
}

我們定義了一個接口 必須實現(xiàn)兩個 函數(shù)

  • .ConnHandle 函數(shù) 傳入一個session 對象 其實就是一個 socket握手成功的句柄炼蹦,我們在這個函數(shù)里面死循環(huán)不斷地讀取 句柄 的數(shù)據(jù) 并且 解包 處理粘包 和 解析數(shù)據(jù) 并 路由
  • . Pack 函數(shù) 負(fù)責(zé)把要發(fā)送的數(shù)據(jù) 打包成指定協(xié)議的 數(shù)據(jù)包

如此一來我們 server 代碼便 非常的 整潔

func NewMsf(msfEvent MsfEventer,socketType SocketTypes) *Msf {
    msf := &Msf{
        EventPool:     NewRouterMap(),
        MsfEvent:      msfEvent,
        SocketType   :socketType,
    }
    msf.SessionMaster = NewSessonM(msf)
    return msf
}

func (this *Msf) Listening(address string) {
    tcpListen, err := net.Listen("tcp", address)

    if err != nil {
        panic(err)
    }
    go this.SessionMaster.HeartBeat(2)
    fd := uint32(0)
    for {
        conn, err := tcpListen.Accept()
        if err != nil {
            log.Println(err)
            continue
        }

        //調(diào)用握手事件
        if this.MsfEvent.OnHandel(fd, conn) == false {
            continue
        }

        sess := NewSession(fd, conn)
        this.SessionMaster.SetSession(fd, sess)
        fd++
        //調(diào)用相應(yīng)協(xié)議的處理函數(shù)
        go this.SocketType.ConnHandle(this,sess)
    }
}

上面就是 server 的兩個主要 函數(shù)

  • . 第一個函數(shù) 不用多介紹,就是創(chuàng)建一個server 對象 狸剃,傳入一個框架事件對象掐隐,和一個 協(xié)議對象。
  • . 第二個函數(shù)實現(xiàn)的邏輯就是 監(jiān)聽一個端口 死循環(huán) 不斷的接收新連接 钞馁,一接到新連接 就 調(diào)用 協(xié)議對象 處理該連接 虑省,并且設(shè)置 心跳 還有 一些錯誤處理 。

websocket協(xié)議對象的實現(xiàn):

為了給大家做一個例子 我搜索了相關(guān)資料 除了 封裝了一個普通 socket 的協(xié)議對象 還封裝了一個 websocket 對象 希望能夠一起學(xué)習(xí)僧凰,

type WebSocket struct {
}

//ws接收消息
func (this *WebSocket) ConnHandle(msf *Msf, sess *Session) {
    defer func() {
        msf.SessionMaster.DelSessionById(sess.Id)
        //調(diào)用斷開鏈接事件
        msf.MsfEvent.OnClose(sess.Id)
    }()

    if this.Handshake(sess) == false {
        return
    }

    var (
        buf     []byte
        err     error
        fin     byte
        opcode  byte
        mask    byte
        mKey    []byte
        length  uint64
        l       uint16
        payload byte
        tembuf  []byte
    )

    for {
        buf = make([]byte, 2)
        _, err = io.ReadFull(sess.Con, buf)
        if err != nil {
            break
        }
        fin = buf[0] >> 7
        opcode = buf[0] & 0xf
        if opcode == 8 {
            break
        }
        mask = buf[1] >> 7
        payload = buf[1] & 0x7f
        switch {
        case payload < 126:
            length = uint64(payload)
        case payload == 126:
            buf = make([]byte, 2)
            io.ReadFull(sess.Con, buf)
            binary.Read(bytes.NewReader(buf), binary.BigEndian, &l)
            length = uint64(l)
        case payload == 127:
            buf = make([]byte, 8)
            io.ReadFull(sess.Con, buf)
            binary.Read(bytes.NewReader(buf), binary.BigEndian, &length)
        }
        if mask == 1 {
            mKey = make([]byte, 4)
            io.ReadFull(sess.Con, mKey)
        }
        buf = make([]byte, length)
        io.ReadFull(sess.Con, buf)
        if mask == 1 {
            for i, v := range buf {
                buf[i] = v ^ mKey[i%4]
            }
        }
        //更新最近接收到消息的時間
        sess.UpdateTime()
        if len(buf) == 0 {
            continue
        }
        tembuf = append(tembuf,buf...)
        if fin == 0 {
            continue
        }
        //把請求的到數(shù)據(jù)轉(zhuǎn)化為map
        requestData := util.String2Map(string(tembuf))
        tembuf = make([]byte,0)
        if requestData["module"] == "" || requestData["action"] == "" ||
msf.EventPool.ModuleExit(requestData["module"]) == false {
            log.Println("not find module ", requestData)
            continue
        }
        requestData["fd"] = fmt.Sprintf("%d", sess.Id)
        //調(diào)用接收消息事件
        if msf.MsfEvent.OnMessage(sess.Id, requestData) == false {
            return
        }
        //路由
        msf.EventPool.Hook(requestData["module"], requestData["action"], requestData)
    }
}

//websocket 打包事件
func (this *WebSocket) Pack(data []byte) []byte {
    length := len(data)
    frame := []byte{129}
    switch {
    case length < 126:
        frame = append(frame, byte(length))
    case length <= 0xffff:
        buf := make([]byte, 2)
        binary.BigEndian.PutUint16(buf, uint16(length))
        frame = append(frame, byte(126))
        frame = append(frame, buf...)
    case uint64(length) <= 0xffffffffffffffff:
        buf := make([]byte, 8)
        binary.BigEndian.PutUint64(buf, uint64(length))
        frame = append(frame, byte(127))
        frame = append(frame, buf...)
    default:
        return []byte{}
    }
    frame = append(frame, data...)
    return frame
}

//握手包
func (this *WebSocket) Handshake(sess *Session) bool {
    reader := bufio.NewReader(sess.Con)
    key := ""
    str := ""
    for {
        line, _, err := reader.ReadLine()
        if err != nil {
            log.Fatal(err)
            return false
        }
        if len(line) == 0 {
            break
        }
        str = string(line)
        if strings.HasPrefix(str, "Sec-WebSocket-Key") {
            key = str[19:43]
        }
    }
    sha := sha1.New()
    io.WriteString(sha, key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
    key = base64.StdEncoding.EncodeToString(sha.Sum(nil))

    header := "HTTP/1.1 101 Switching Protocols\r\n" +
        "Connection: Upgrade\r\n" +
        "Sec-WebSocket-Version: 13\r\n" +
        "Sec-WebSocket-Accept: " + key + "\r\n" +
        "Upgrade: websocket\r\n\r\n"
    sess.Con.Write([]byte(header))
    return true
}

此對象我已經(jīng)測試過了 完全沒有問題 探颈!
如果有什么 疑問的 歡迎 留言 一起討論 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末训措,一起剝皮案震驚了整個濱河市伪节,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绩鸣,老刑警劉巖怀大,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異呀闻,居然都是意外死亡化借,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門捡多,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓖康,“玉大人,你說我怎么就攤上這事局服〉霾t!?“怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵淫奔,是天一觀的道長山涡。 經(jīng)常有香客問我,道長唆迁,這世上最難降的妖魔是什么鸭丛? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮唐责,結(jié)果婚禮上鳞溉,老公的妹妹穿的比我還像新娘。我一直安慰自己鼠哥,他們只是感情好熟菲,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布看政。 她就那樣靜靜地躺著,像睡著了一般抄罕。 火紅的嫁衣襯著肌膚如雪允蚣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天呆贿,我揣著相機(jī)與錄音嚷兔,去河邊找鬼。 笑死做入,一個胖子當(dāng)著我的面吹牛冒晰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竟块,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼壶运,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了彩郊?” 一聲冷哼從身側(cè)響起前弯,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秫逝,沒想到半個月后恕出,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡违帆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年浙巫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刷后。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡的畴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尝胆,到底是詐尸還是另有隱情丧裁,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布含衔,位于F島的核電站煎娇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贪染。R本人自食惡果不足惜缓呛,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杭隙。 院中可真熱鬧哟绊,春花似錦、人聲如沸痰憎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炬称,卻和暖如春汁果,著一層夾襖步出監(jiān)牢的瞬間涡拘,已是汗流浹背玲躯。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留鳄乏,地道東北人跷车。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像橱野,于是被迫代替她去往敵國和親朽缴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容