同时支持websocket和socket的轻量框架

前言:

通过之前的几篇文章我们详细的介绍到了一个socket框架应该怎么架构,需要些什么模块,可是美中不足的就是它只支持最简单的socket协议,不能够满足实际生产情况,于是我便对此框架进行了改造,让它能够同时支持websocket 和 socket ,而且插件式 注册,当需要别的长连接协议的时,完全可以自己定制。已经把所有代码整合了,希望给个星星支持一下 microSocket

实现基础:

一切编程皆socket ,这话有点说的绝对。但是仔细想想,确实也就是那么回事,网络通信现在99%都是socket吧。我们有n多种协议,但是都是socket的,所以这些协议无非就是 握手 解包 封包 上面不同 ,那我们把这些 过程 单独封装 不就能够 写一个框架能够随意切换 协议了么!

代码实现:

废话不多说 我们直接看代码 !

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

我们定义了一个接口 必须实现两个 函数

  • .ConnHandle 函数 传入一个session 对象 其实就是一个 socket握手成功的句柄,我们在这个函数里面死循环不断地读取 句柄 的数据 并且 解包 处理粘包 和 解析数据 并 路由
  • . Pack 函数 负责把要发送的数据 打包成指定协议的 数据包

如此一来我们 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
        }

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

        sess := NewSession(fd, conn)
        this.SessionMaster.SetSession(fd, sess)
        fd++
        //调用相应协议的处理函数
        go this.SocketType.ConnHandle(this,sess)
    }
}

上面就是 server 的两个主要 函数

  • . 第一个函数 不用多介绍,就是创建一个server 对象 ,传入一个框架事件对象,和一个 协议对象。
  • . 第二个函数实现的逻辑就是 监听一个端口 死循环 不断的接收新连接 ,一接到新连接 就 调用 协议对象 处理该连接 ,并且设置 心跳 还有 一些错误处理 。

websocket协议对象的实现:

为了给大家做一个例子 我搜索了相关资料 除了 封装了一个普通 socket 的协议对象 还封装了一个 websocket 对象 希望能够一起学习,

type WebSocket struct {
}

//ws接收消息
func (this *WebSocket) ConnHandle(msf *Msf, sess *Session) {
    defer func() {
        msf.SessionMaster.DelSessionById(sess.Id)
        //调用断开链接事件
        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
        }
        //把请求的到数据转化为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)
        //调用接收消息事件
        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
}

此对象我已经测试过了 完全没有问题 !
如果有什么 疑问的 欢迎 留言 一起讨论 。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容