1
0
mirror of https://github.com/kataras/iris.git synced 2026-03-07 00:45:58 +00:00

Implement the websocket adaptor, a version of kataras/go-websocket, and refactor all of the previous websocket examples.

https://github.com/kataras/go-websocket/issues/27

Former-commit-id: 0b7e52e0a61150a8bba973ef653986d8b3ddd26b
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-02-15 08:40:43 +02:00
parent 26a5f9f3b0
commit 82afcc5aa6
30 changed files with 2692 additions and 330 deletions

View File

@@ -0,0 +1,99 @@
package main
import (
"fmt" // optional
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
"gopkg.in/kataras/iris.v6/adaptors/websocket"
)
type clientPage struct {
Title string
Host string
}
func main() {
app := iris.New()
app.Adapt(iris.DevLogger()) // enable all (error) logs
app.Adapt(httprouter.New()) // select the httprouter as the servemux
app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates
ws := websocket.New(websocket.Config{
// the path which the websocket client should listen/registed to,
Endpoint: "/my_endpoint",
// the client-side javascript static file path
// which will be served by Iris.
// default is /iris-ws.js
// if you change that you have to change the bottom of templates/client.html
// script tag:
ClientSourcePath: "/iris-ws.js",
//
// Set the timeouts, 0 means no timeout
// websocket has more configuration, go to ../../config.go for more:
// WriteTimeout: 0,
// ReadTimeout: 0,
// by-default all origins are accepted, you can change this behavior by setting:
// CheckOrigin: (r *http.Request ) bool {},
//
//
// IDGenerator used to create (and later on, set)
// an ID for each incoming websocket connections (clients).
// The request is an argument which you can use to generate the ID (from headers for example).
// If empty then the ID is generated by DefaultIDGenerator: randomString(64):
// IDGenerator func(ctx *iris.Context) string {},
})
app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint
app.StaticWeb("/js", "./static/js") // serve our custom javascript code
app.Get("/", func(ctx *iris.Context) {
ctx.Render("client.html", clientPage{"Client Page", ctx.Host()})
})
var myChatRoom = "room1"
ws.OnConnection(func(c websocket.Connection) {
// Context returns the (upgraded) *iris.Context of this connection
// avoid using it, you normally don't need it,
// websocket has everything you need to authenticate the user BUT if it's necessary
// then you use it to receive user information, for example: from headers.
// ctx := c.Context()
// join to a room (optional)
c.Join(myChatRoom)
c.On("chat", func(message string) {
if message == "leave" {
c.Leave(myChatRoom)
c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
return
}
// to all except this connection ->
// c.To(websocket.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message)
// to all connected clients: c.To(websocket.All)
// to the client itself ->
//c.Emit("chat", "Message from myself: "+message)
//send the message to the whole room,
//all connections are inside this room will receive this message
c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message)
})
// or create a new leave event
// c.On("leave", func() {
// c.Leave(myChatRoom)
// })
c.OnDisconnect(func() {
fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID())
})
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,38 @@
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
w = new Ws("ws://" + HOST + "/my_endpoint");
w.OnConnect(function () {
console.log("Websocket connection established");
});
w.OnDisconnect(function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
});
w.On("chat", function (message) {
appendMessage($("<div>" + message + "</div>"));
});
$("#sendBtn").click(function () {
w.Emit("chat", messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}

View File

@@ -0,0 +1,24 @@
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
<!-- This is auto-serving by the Iris, you don't need to have this file in your disk-->
<script src="/iris-ws.js" type="text/javascript"></script>
<!-- -->
<script src="js/chat.js" type="text/javascript"></script>
</body>
</html>

View File

@@ -0,0 +1,113 @@
package main
import (
"fmt"
"sync"
"time"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
"gopkg.in/kataras/iris.v6/adaptors/websocket"
)
type clientPage struct {
Title string
Host string
}
func main() {
app := iris.New()
app.Adapt(iris.DevLogger()) // enable all (error) logs
app.Adapt(httprouter.New()) // select the httprouter as the servemux
app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates
ws := websocket.New(websocket.Config{
// the path which the websocket client should listen/registed to,
Endpoint: "/my_endpoint",
// the client-side javascript static file path
// which will be served by Iris.
// default is /iris-ws.js
// if you change that you have to change the bottom of templates/client.html
// script tag:
ClientSourcePath: "/iris-ws.js",
//
// Set the timeouts, 0 means no timeout
// websocket has more configuration, go to ../../config.go for more:
// WriteTimeout: 0,
// ReadTimeout: 0,
// by-default all origins are accepted, you can change this behavior by setting:
// CheckOrigin: (r *http.Request ) bool {},
//
//
// IDGenerator used to create (and later on, set)
// an ID for each incoming websocket connections (clients).
// The request is an argument which you can use to generate the ID (from headers for example).
// If empty then the ID is generated by DefaultIDGenerator: randomString(64):
// IDGenerator func(ctx *iris.Context) string {},
})
app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint
app.StaticWeb("/js", "./static/js") // serve our custom javascript code
app.Get("/", func(ctx *iris.Context) {
ctx.Render("client.html", clientPage{"Client Page", ctx.ServerHost()})
})
Conn := make(map[websocket.Connection]bool)
var myChatRoom = "room1"
var mutex = new(sync.Mutex)
ws.OnConnection(func(c websocket.Connection) {
c.Join(myChatRoom)
mutex.Lock()
Conn[c] = true
mutex.Unlock()
c.On("chat", func(message string) {
if message == "leave" {
c.Leave(myChatRoom)
c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
return
}
})
c.OnDisconnect(func() {
mutex.Lock()
delete(Conn, c)
mutex.Unlock()
fmt.Printf("\nConnection with ID: %s has been disconnected!\n", c.ID())
})
})
var delay = 1 * time.Second
go func() {
i := 0
for {
mutex.Lock()
broadcast(Conn, fmt.Sprintf("aaaa %d\n", i))
mutex.Unlock()
time.Sleep(delay)
i++
}
}()
go func() {
i := 0
for {
mutex.Lock()
broadcast(Conn, fmt.Sprintf("aaaa2 %d\n", i))
mutex.Unlock()
time.Sleep(delay)
i++
}
}()
app.Listen(":8080")
}
func broadcast(Conn map[websocket.Connection]bool, message string) {
for k := range Conn {
k.To("room1").Emit("chat", message)
}
}

View File

@@ -0,0 +1,38 @@
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
w = new Ws("ws://" + HOST + "/my_endpoint");
w.OnConnect(function () {
console.log("Websocket connection established");
});
w.OnDisconnect(function () {
appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
});
w.On("chat", function (message) {
appendMessage($("<div>" + message + "</div>"));
});
$("#sendBtn").click(function () {
w.Emit("chat", messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}

View File

@@ -0,0 +1,24 @@
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
<!-- This is auto-serving by the Iris, you don't need to have this file in your disk-->
<script src="/iris-ws.js" type="text/javascript"></script>
<!-- -->
<script src="js/chat.js" type="text/javascript"></script>
</body>
</html>

View File

@@ -0,0 +1,179 @@
package main
// Run first `go run main.go server`
// and `go run main.go client` as many times as you want.
// Originally written by: github.com/antlaw to describe an old kataras/go-websocket's issue
// which you can find here: https://github.com/kataras/go-websocket/issues/24
import (
"fmt"
"os"
"strings"
"time"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/websocket"
xwebsocket "golang.org/x/net/websocket"
)
// WS is the current websocket connection
var WS *xwebsocket.Conn
func main() {
if len(os.Args) == 2 && strings.ToLower(os.Args[1]) == "server" {
ServerLoop()
} else if len(os.Args) == 2 && strings.ToLower(os.Args[1]) == "client" {
ClientLoop()
} else {
fmt.Println("wsserver [server|client]")
}
}
/////////////////////////////////////////////////////////////////////////
// client side
func sendUntilErr(sendInterval int) {
i := 1
for {
time.Sleep(time.Duration(sendInterval) * time.Second)
err := SendMessage("2", "all", "objectupdate", "2.UsrSchedule_v1_1")
if err != nil {
fmt.Println("failed to send join message", err.Error())
return
}
fmt.Println("objectupdate", i)
i++
}
}
func recvUntilErr() {
var msg = make([]byte, 2048)
var n int
var err error
i := 1
for {
if n, err = WS.Read(msg); err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("%v Received: %s.%v\n", time.Now(), string(msg[:n]), i)
i++
}
}
//ConnectWebSocket connect a websocket to host
func ConnectWebSocket() error {
var origin = "http://localhost/"
var url = "ws://localhost:9090/socket"
var err error
WS, err = xwebsocket.Dial(url, "", origin)
return err
}
// CloseWebSocket closes the current websocket connection
func CloseWebSocket() error {
if WS != nil {
return WS.Close()
}
return nil
}
// SendMessage broadcast a message to server
func SendMessage(serverID, to, method, message string) error {
buffer := []byte(message)
return SendtBytes(serverID, to, method, buffer)
}
// SendtBytes broadcast a message to server
func SendtBytes(serverID, to, method string, message []byte) error {
// look https://github.com/kataras/iris/tree/master/adaptors/websocket/message.go , client.go and client.js
// to understand the buffer line:
buffer := []byte(fmt.Sprintf("iris-websocket-message:%v;0;%v;%v;", method, serverID, to))
buffer = append(buffer, message...)
_, err := WS.Write(buffer)
if err != nil {
fmt.Println(err)
return err
}
return nil
}
// ClientLoop connects to websocket server, the keep send and recv dataS
func ClientLoop() {
for {
time.Sleep(time.Second)
err := ConnectWebSocket()
if err != nil {
fmt.Println("failed to connect websocket", err.Error())
continue
}
// time.Sleep(time.Second)
err = SendMessage("2", "all", "join", "dummy2")
go sendUntilErr(1)
recvUntilErr()
err = CloseWebSocket()
if err != nil {
fmt.Println("failed to close websocket", err.Error())
}
}
}
/////////////////////////////////////////////////////////////////////////
// server side
// OnConnect handles incoming websocket connection
func OnConnect(c websocket.Connection) {
fmt.Println("socket.OnConnect()")
c.On("join", func(message string) { OnJoin(message, c) })
c.On("objectupdate", func(message string) { OnObjectUpdated(message, c) })
// ok works too c.EmitMessage([]byte("dsadsa"))
c.OnDisconnect(func() { OnDisconnect(c) })
}
// ServerLoop listen and serve websocket requests
func ServerLoop() {
app := iris.New()
app.Adapt(iris.DevLogger()) // enable all (error) logs
app.Adapt(httprouter.New()) // select the httprouter as the servemux
ws := websocket.New(websocket.Config{Endpoint: "/socket"})
app.Adapt(ws)
ws.OnConnection(OnConnect)
app.Listen("0.0.0.0:9090")
}
// OnJoin handles Join broadcast group request
func OnJoin(message string, c websocket.Connection) {
t := time.Now()
c.Join("server2")
fmt.Println("OnJoin() time taken:", time.Since(t))
}
// OnObjectUpdated broadcasts to all client an incoming message
func OnObjectUpdated(message string, c websocket.Connection) {
t := time.Now()
s := strings.Split(message, ";")
if len(s) != 3 {
fmt.Println("OnObjectUpdated() invalid message format:" + message)
return
}
serverID, _, objectID := s[0], s[1], s[2]
err := c.To("server"+serverID).Emit("objectupdate", objectID)
if err != nil {
fmt.Println(err, "failed to broacast object")
return
}
fmt.Println(fmt.Sprintf("OnObjectUpdated() message:%v, time taken: %v", message, time.Since(t)))
}
// OnDisconnect clean up things when a client is disconnected
func OnDisconnect(c websocket.Connection) {
c.Leave("server2")
fmt.Println("OnDisconnect(): client disconnected!")
}

View File

@@ -0,0 +1,62 @@
package main
import (
"fmt"
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view"
"gopkg.in/kataras/iris.v6/adaptors/websocket"
)
/* Native messages no need to import the iris-ws.js to the ./templates.client.html
Use of: OnMessage and EmitMessage.
NOTICE: IF YOU HAVE RAN THE PREVIOUS EXAMPLES YOU HAVE TO CLEAR YOUR BROWSER's CACHE
BECAUSE chat.js is different than the CACHED. OTHERWISE YOU WILL GET Ws is undefined from the browser's console, becuase it will use the cached.
*/
type clientPage struct {
Title string
Host string
}
func main() {
app := iris.New()
app.Adapt(iris.DevLogger()) // enable all (error) logs
app.Adapt(httprouter.New()) // select the httprouter as the servemux
app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates
ws := websocket.New(websocket.Config{
// the path which the websocket client should listen/registed to,
Endpoint: "/my_endpoint",
// to enable binary messages (useful for protobuf):
// BinaryMessages: true,
})
app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint
app.StaticWeb("/js", "./static/js") // serve our custom javascript code
app.Get("/", func(ctx *iris.Context) {
ctx.Render("client.html", clientPage{"Client Page", ctx.Host()})
})
ws.OnConnection(func(c websocket.Connection) {
c.OnMessage(func(data []byte) {
message := string(data)
c.To(websocket.Broadcast).EmitMessage([]byte("Message from: " + c.ID() + "-> " + message)) // broadcast to all clients except this
c.EmitMessage([]byte("Me: " + message)) // writes to itself
})
c.OnDisconnect(func() {
fmt.Printf("\nConnection with ID: %s has been disconnected!", c.ID())
})
})
app.Listen(":8080")
}

View File

@@ -0,0 +1,38 @@
var messageTxt;
var messages;
$(function () {
messageTxt = $("#messageTxt");
messages = $("#messages");
w = new WebSocket("ws://" + HOST + "/my_endpoint");
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>" + message.data + "</div>"));
};
$("#sendBtn").click(function () {
w.send(messageTxt.val().toString());
messageTxt.val("");
});
})
function appendMessage(messageDiv) {
var theDiv = messages[0];
var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
messageDiv.appendTo(messages);
if (doScroll) {
theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
}
}

View File

@@ -0,0 +1,21 @@
<html>
<head>
<title>{{ .Title}}</title>
</head>
<body>
<div id="messages"
style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
</div>
<input type="text" id="messageTxt" />
<button type="button" id="sendBtn">Send</button>
<script type="text/javascript">
var HOST = {{.Host}}
</script>
<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
<script src="js/chat.js" type="text/javascript"></script>
</body>
</html>