go-iris-websocket 簡單聊天通信

基於go的websocket大多使用gorilla/websocket

iris也提供了websoket的封裝,github.com/kataras/iris/v12/websocket

不過iris官方給的示例基本上都是依賴官方的js庫實現的neffos.js

Neffos.js對websocket進行了封裝,主要是房間進入和離開等事件的綁定,

對於消息的傳遞也使用了自己定義的格式,不同的字段使用分號進行分割

例如:;;;;0;0;helloworld,查看iris中源碼看到Message的定義:


type Message struct {
wait string
// The Namespace that this message sent to/received from.
Namespace string
// The Room that this message sent to/received from.
Room string
// The Event that this message sent to/received from.
Event string
// The actual body of the incoming/outcoming data.
Body []byte
// The Err contains any message's error, if any.
// Note that server-side and client-side connections can return an error instead of a message from each event callbacks,
// except the clients's force Disconnect which its local event doesn't matter when disconnected manually.
Err error
// if true then `Err` is filled by the error message and
// the last segment of incoming/outcoming serialized message is the error message instead of the body.
isError bool
isNoOp  bool
isInvalid bool
// the CONN ID, filled automatically if `Server#Broadcast` first parameter of sender connection's ID is not empty,
// not exposed to the subscribers (rest of the clients).
// This is the ID across neffos servers when scale.
from string
// When sent by the same connection of the current running server instance.
// This field is serialized/deserialized but it's clean on sending or receiving from a client
// and it's only used on StackExchange feature.
// It's serialized as the first parameter, instead of wait signal, if incoming starts with 0x.
FromExplicit string // the exact Conn's pointer in this server instance.
// Reports whether this message is coming from a stackexchange.
// This field is not exposed and it's not serialized at all, ~local-use only~.
//
// The "wait" field can determinate if this message is coming from a stackexchange using its second char,
// This value set based on "wait" on deserialization when coming from remote side.
// Only server-side can actually set it.
FromStackExchange bool
// To is the connection ID of the receiver, used only when `Server#Broadcast` is called, indeed when we only need to send a message to a single connection.
// The Namespace, Room are still respected at all.
//
// However, sending messages to a group of connections is done by the `Room` field for groups inside a namespace or just `Namespace` field as usual.
// This field is not filled on sending/receiving.
To string
// True when event came from local (i.e client if running client) on force disconnection,
// i.e OnNamespaceDisconnect and OnRoomLeave when closing a conn.
// This field is not filled on sending/receiving.
// Err does not matter and never sent to the other side.
IsForced bool
// True when asking the other side and fire the respond's event (which matches the sent for connect/disconnect/join/leave),
// i.e if a client (or server) onnection want to connect
// to a namespace or join to a room.
// Should be used rarely, state can be checked by `Conn#IsClient() bool`.
// This field is not filled on sending/receiving.
IsLocal bool
// True when user define it for writing, only its body is written as raw native websocket message, namespace, event and all other fields are empty.
// The receiver should accept it on the `OnNativeMessage` event.
// This field is not filled on sending/receiving.
IsNative bool
// Useful rarely internally on `Conn#Write` namespace and rooms checks, i.e `Conn#DisconnectAll` and `NSConn#RemoveAll`.
// If true then the writer's checks will not lock connectedNamespacesMutex or roomsMutex again. May be useful in the future, keep that solution.
locked bool
// if server or client should write using Binary message or if the incoming message was readen as binary.
SetBinary bool
}

注意到有個IsNative的控制參數,使用OnNativeMessage事件和IsNative字段就可以使用自己的格式通過websoket傳遞數據:

// True when user define it for writing, only its body is written as raw native websocket message, namespace, event and all other fields are empty.
// The receiver should accept it on the `OnNativeMessage` event.
// This field is not filled on sending/receiving.
IsNative bool
msg:=websocket.Message{
    Body:[]byte("helloworld"),
    IsNative:true,
}

接下來使用iris/websocket實現簡單的聊天通信,go主要代碼如下:

ws := websocket.New(websocket.DefaultGorillaUpgrader, websocket.Events{
websocket.OnNativeMessage: func(nsConn *websocket.NSConn, msg websocket.Message) error {
log.Printf("Server got: %s from [%s]", msg.Body, nsConn.Conn.ID())

ping := string(msg.Body)

pong := strings.Replace(ping,"?", "!", len(ping))
pong = strings.Replace(pong, "麼","",len(pong))

mg := websocket.Message{
Body:[]byte(pong),
IsNative:true,
}
nsConn.Conn.Write(mg)
return nil
},
})

完整go代碼:

package main

import (
	"log"
	"strings"

	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/websocket"
)


func main() {
	ws := websocket.New(websocket.DefaultGorillaUpgrader, websocket.Events{
		websocket.OnNativeMessage: func(nsConn *websocket.NSConn, msg websocket.Message) error {
			log.Printf("Server got: %s from [%s]", msg.Body, nsConn.Conn.ID())

			ping := string(msg.Body)
			pong := strings.Replace(ping,"?", "!", len(ping))
			pong = strings.Replace(pong, "麼","",len(pong))

			mg := websocket.Message{
				Body:[]byte(pong),
				IsNative:true,
			}

			nsConn.Conn.Write(mg)

			return nil
		},
	})

	ws.OnConnect = func(c *websocket.Conn) error {
		log.Printf("[%s] Connected to server!", c.ID())
		return nil
	}

	ws.OnDisconnect = func(c *websocket.Conn) {
		log.Printf("[%s] Disconnected from server", c.ID())
	}

	ws.OnUpgradeError = func(err error) {
		log.Printf("Upgrade Error: %v", err)
	}


	app := iris.New()
	app.HandleDir("/","./html")
	app.Get("/msg", websocket.Handler(ws))

	app.Run(iris.Addr(":8080"))
}

html 代碼:

<html>
<head>
    <title>Client Page</title>
</head>
<body style="padding:10px;">
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<div id="messages" style="width: 375px;margin:10 0 0 0px;border-top: 1px solid black;">
</div>
<script type="text/javascript">
    var HOST = "localhost:8080"
    var messageTxt = document.getElementById("messageTxt");
    var messages = document.getElementById("messages");
    var sendBtn = document.getElementById("sendBtn")

    w = new WebSocket("ws://" + HOST + "/msg");
    w.onopen = function () {
        console.log("Websocket connection enstablished");
    };

    w.onclose = function () {
        appendMessage("<div><center><h3>Disconnected</h3></center></div>");
    };
    w.onmessage = function (message) {
        appendMessage("<div> srv: " + message.data + "</div>");
    };

    sendBtn.onclick = function () {
        myText = messageTxt.value;
        messageTxt.value = "";

        appendMessage("<div style='color: red'> me: " + myText + "</div>");
        w.send(myText);
    };

    messageTxt.addEventListener("keyup", function (e) {
        if (e.keyCode === 13) {
            e.preventDefault();
            sendBtn.click();
        }
    });

    function appendMessage(messageDivHTML) {
        messages.insertAdjacentHTML('afterbegin', messageDivHTML);
    }
</script>
</body>
</html>

效果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章