项目作者: philippseith

项目描述 :
SignalR server and client in go
高级语言: Go
项目地址: git://github.com/philippseith/signalr.git
创建时间: 2020-01-14T19:27:53Z
项目社区:https://github.com/philippseith/signalr

开源协议:MIT License

下载


SignalR

Actions Status
codecov
PkgGoDev

SignalR is an open-source library that simplifies adding real-time web functionality to apps.
Real-time web functionality enables server-side code to push content to clients instantly.

Historically it was tied to ASP.NET Core but the
protocol is open and implementable in any language.

This repository contains an implementation of a SignalR server and a SignalR client in go.
The implementation is based on the work of David Fowler at https://github.com/davidfowl/signalr-ports.
Client and server support transport over WebSockets, Server Sent Events and raw TCP.
Protocol encoding in JSON and MessagePack is fully supported.

Install

With a correctly configured Go toolchain:

  1. go get -u github.com/philippseith/signalr

Getting Started

SignalR uses a signalr.HubInterface instance to anchor the connection on the server and a javascript HubConnection object to anchor the connection on the client.

Server side

Implement the HubInterface

The easiest way to implement the signalr.HubInterface in your project is to declare your own type and embed signalr.Hub which implements that interface and will take care of all the signalr plumbing. You can call your custom type anything you want so long as it implements the signalr.HubInterface interface.

  1. package main
  2. import "github.com/philippseith/signalr"
  3. type AppHub struct {
  4. signalr.Hub
  5. }

Add functions with your custom hub type as a receiver.

  1. func (h *AppHub) SendChatMessage(message string) {
  2. h.Clients().All().Send("chatMessageReceived", message)
  3. }

These functions must be public so that they can be seen by the signalr server package but can be invoked client-side as lowercase message names. We’ll explain setting up the client side in a moment, but as a preview, here’s an example of calling our AppHub.SendChatMessage(...) method from the client:

  1. // javascript snippet invoking that AppHub.Send method from the client
  2. connection.invoke('sendChatMessage', val);

The signalr.HubInterface contains a pair of methods you can implement to handle connection and disconnection events. signalr.Hub contains empty implementations of them to satisfy the interface, but you can “override” those defaults by implementing your own functions with your custom hub type as a receiver:

  1. func (c *chat) OnConnected(connectionID string) {
  2. fmt.Printf("%s connected\n", connectionID)
  3. }
  4. func (c *chat) OnDisconnected(connectionID string) {
  5. fmt.Printf("%s disconnected\n", connectionID)
  6. }

Serve with http.ServeMux

  1. import (
  2. "net/http"
  3. "github.com/philippseith/signalr"
  4. )
  5. func runHTTPServer() {
  6. address := 'localhost:8080'
  7. // create an instance of your hub
  8. hub := AppHub{}
  9. // build a signalr.Server using your hub
  10. // and any server options you may need
  11. server, _ := signalr.NewServer(context.TODO(),
  12. signalr.SimpleHubFactory(hub)
  13. signalr.KeepAliveInterval(2*time.Second),
  14. signalr.Logger(kitlog.NewLogfmtLogger(os.Stderr), true))
  15. )
  16. // create a new http.ServerMux to handle your app's http requests
  17. router := http.NewServeMux()
  18. // ask the signalr server to map it's server
  19. // api routes to your custom baseurl
  20. server.MapHTTP(signalr.WithHTTPServeMux(router), "/chat")
  21. // in addition to mapping the signalr routes
  22. // your mux will need to serve the static files
  23. // which make up your client-side app, including
  24. // the signalr javascript files. here is an example
  25. // of doing that using a local `public` package
  26. // which was created with the go:embed directive
  27. //
  28. // fmt.Printf("Serving static content from the embedded filesystem\n")
  29. // router.Handle("/", http.FileServer(http.FS(public.FS)))
  30. // bind your mux to a given address and start handling requests
  31. fmt.Printf("Listening for websocket connections on http://%s\n", address)
  32. if err := http.ListenAndServe(address, router); err != nil {
  33. log.Fatal("ListenAndServe:", err)
  34. }
  35. }

Client side: JavaScript/TypeScript

Grab copies of the signalr scripts

Microsoft has published the client-side libraries as a node package with embedded typescript annotations: @microsoft/signalr.

You can install @microsoft/signalr through any node package manager:

package manager command
npm npm install @microsoft/signalr@latest
yarn yarn add @microsoft/signalr@latest
LibMan libman install @microsoft/signalr@latest -p unpkg -d wwwroot/js/signalr --files dist/browser/signalr.js --files dist/browser/signalr.min.js --files dist/browser/signalr.map.js
none you can download the version we are using in our chatsample from here (the minified version is here)

Use a HubConnection to connect to your server Hub

How you format your client UI is going to depend on your application use case but here is a simple example. It illustrates the basic steps of connecting to your server hub:

  1. import the signalr.js library (or signalr.min.js);
  2. create a connection object using the HubConnectionBuilder;
  3. bind events

    • UI event handlers can use connection.invoke(targetMethod, payload) to send invoke functions on the server hub;
    • connection event handlers can react to the messages sent from the server hub;
  4. start your connection

  1. <html>
  2. <body>
  3. <!-- you may want the content you send to be dynamic -->
  4. <input type="text" id="message" />
  5. <!-- you may need a trigger to initiate the send -->
  6. <input type="button" value="Send" id="send" />
  7. <!-- you may want some container to display received messages -->
  8. <ul id="messages">
  9. </ul>
  10. <!-- 1. you need to import the signalr script which provides
  11. the HubConnectionBuilder and handles the connection
  12. plumbing.
  13. -->
  14. <script src="js/signalr.js"></script>
  15. <script>
  16. (async function () {
  17. // 2. use the signalr.HubConnectionBuilder to build a hub connection
  18. // and point it at the baseurl which you configured in your mux
  19. const connection = new signalR.HubConnectionBuilder()
  20. .withUrl('/chat')
  21. .build();
  22. // 3. bind events:
  23. // - UI events can invoke (i.e. dispatch to) functions on the server hub
  24. document.getElementById('send').addEventListener('click', sendClicked);
  25. // - connection events can handle messages received from the server hub
  26. connection.on('chatMessageReceived', onChatMessageReceived);
  27. // 4. call start to initiate the connection and start streaming events
  28. // between your server hub and your client connection
  29. connection.start();
  30. // that's it! your server and client should be able to communicate
  31. // through the signalr.Hub <--> connection pipeline managed by the
  32. // signalr package and client-side library.
  33. // --------------------------------------------------------------------
  34. // example UI event handler
  35. function sendClicked() {
  36. // prepare your target payload
  37. const msg = document.getElementById('message').value;
  38. if (msg) {
  39. // call invoke on your connection object to dispatch
  40. // messages to the server hub with two arguments:
  41. // - target: name of the hub func to invoke
  42. // - payload: the message body
  43. //
  44. const target = 'sendChatMessage';
  45. connection.invoke(target, msg);
  46. }
  47. }
  48. // example server event handler
  49. function onChatMessageReceived(payload) {
  50. // the payload is whatever was passed to the inner
  51. // clients' `Send(...)` method in your server-side
  52. // hub function.
  53. const li = document.createElement('li');
  54. li.innerText = payload;
  55. document.getElementById('messages').appendChild(li);
  56. }
  57. })();
  58. </script>
  59. </body>
  60. </html>

Client side: go

To handle callbacks from the server, create a receiver class which gets the server callbacks mapped
to its methods:

  1. type receiver struct {
  2. signalr.Hub
  3. }
  4. func (c *receiver) Receive(msg string) {
  5. fmt.Println(msg)
  6. }

Receive gets called when the server does something like this:

  1. hub.Clients().Caller().Send("receive", message)

The client itself might be used like that:

  1. // Create a Connection (with timeout for the negotiation process)
  2. creationCtx, _ := context.WithTimeout(ctx, 2 * time.Second)
  3. conn, err := signalr.NewHTTPConnection(creationCtx, address)
  4. if err != nil {
  5. return err
  6. }
  7. // Create the client and set a receiver for callbacks from the server
  8. client, err := signalr.NewClient(ctx,
  9. signalr.WithConnection(conn),
  10. signalr.WithReceiver(receiver))
  11. if err != nil {
  12. return err
  13. }
  14. // Start the client loop
  15. c.Start()
  16. // Do some client work
  17. ch := <-c.Invoke("update", data)
  18. // ch gets the result of the update operation

Debugging

Server, Client and the protocol implementations are able to log most of their operations. The logging option is disabled
by default in all tests. To configure logging, edit the testLogConf.json file:

  1. {
  2. "Enabled": false,
  3. "Debug": false
  4. }
  • If Enabled is set to true, the logging will be enabled. The tests will log to os.Stderr.
  • If Debug ist set to true, the logging will be more detailed.