Introduction to the

Gotalk focuses on communication between processes and aims to simplify communication protocols and processes. At the same time it:

  • Provide concise and clear apis;
  • Support TCP, WebSocket and other protocols;
  • Using a very simple and efficient transmission protocol format, easy to capture debugging;
  • JavaScript files are built ingotalk.jsTo facilitate the development of client programs based on Web pages;
  • It contains rich examples for learning reference.

So, let’s play

Quick to use

The code in this article uses Go Modules.

Create directory and initialize:

$ mkdir gotalk && cd gotalk
$ go mod init github.com/darjun/go-daily-lib/gotalk
Copy the code

Install goTalk library:

$ go get -u github.com/rsms/gotalk
Copy the code

Let’s write a simple echo program where the server returns the received client message without doing anything. The first is the server side:

// get-started/server/server.go
package main

import (
  "log"

  "github.com/rsms/gotalk"
)

func main(a) {
  gotalk.Handle("echo".func(in string) (string, error) {
    return in, nil
  })
  if err := gotalk.Serve("tcp".": 8080".nil); err ! =nil {
    log.Fatal(err)
  }
}
Copy the code

Register message processing with gotalk.handle (), which takes two arguments. The first parameter is the message name, which is a string and is guaranteed to be unique and recognizable. The second argument is the handler function, which is called to process the message with the corresponding name. The handler takes one argument and returns two values. The first return value is passed after normal processing, and the second return value indicates the type of error.

The processor function here is simpler, taking a string argument and returning it as is.

Then, call gotalk.serve () to start the server and listen for the port. It takes three parameters, protocol type, listener address, and processor object. Here we’re using TCP, listening on local port 8080, using the default processor object, passing in nil.

Requests are processed in a loop internally.

Then there is the client:

func main(a) {
  s, err := gotalk.Connect("tcp".": 8080")
  iferr ! =nil {
    log.Fatal(err)
  }

  for i := 0; i < 5; i++ {
    var echo string
    if err := s.Request("echo"."hello", &echo); err ! =nil {
      log.Fatal(err)
    }

    fmt.Println(echo)
  }

  s.Close()
}
Copy the code

The client first calls gotalk.connect () to Connect to the server, which takes two parameters: protocol and address (IP + port). We use the same protocol and address as the server. A successful connection returns a connection object. Call the Request() method of the connection object to send a message to the server. The Request() method takes three arguments. The first parameter is the message name, which corresponds to the message name registered by the server. Requesting a nonexistent message name returns an error. The second parameter is the one passed to the server, with one and only one parameter, corresponding to the input parameter of the processor function. The third argument is a pointer to the return value, which is used to accept the result returned by the server.

If the request fails, an error err is returned. Don’t forget to close the connection object when you’re done.

Run the server first:

$ go run server.go
Copy the code

On opening a command line, run the client:

$ go run client.go
hello
hello
hello
hello
hello
Copy the code

In fact, if you know the standard library NET/HTTP, you should see that server-side code using GoTalk is very similar to writing a Web server using NET/HTTP. Very simple and clear:

// get-started/http/main.go
package main

import (
  "fmt"
  "log"
  "net/http"
)

func index(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "hello world")}func main(a) {
  http.HandleFunc("/", index)

  if err := http.ListenAndServe(": 8888".nil); err ! =nil {
    log.Fatal(err)
  }
}
Copy the code

Run:

$ go run main.go

Copy the code

Using curl to verify:

$ curl localhost:8888
hello world
Copy the code

WebSocket

In addition to TCP, GoTalk supports communication based on the WebSocket protocol. Let’s rewrite the above server using WebSocket and write a simple Web page to communicate with it.

Server:

func main(a) {
  gotalk.Handle("echo".func(in string) (string, error) {
    return in, nil
  })

  http.Handle("/gotalk/", gotalk.WebSocketHandler())
  http.Handle("/", http.FileServer(http.Dir(".")))
  if err := http.ListenAndServe(": 8080".nil); err ! =nil {
    log.Fatal(err)
  }
}
Copy the code

The goTalk message processing functions are registered as before. The difference here is that requests for HTTP paths /gotalk/ are handled by gotalk.webSockethandler (), which handles WebSocket requests. At the same time, start a file server in the current working directory and mount it to the HTTP path /. The file server requests the index.html page for the client. Finally, call http.listenandServe () to start the Web server and listen on port 8080.

Then comes the client side. Gotalk encapsulates WebSocket communication details in a JavaScript file gotalk.js to facilitate the writing of Web programs. You can use it directly from the js directory in the repository. Next we write the page index.html and introduce gotalk.js:

<! DOCTYPEHTML>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="gotalk/gotalk.js"></script>
  </head>
  <body>
    <input id="txt">
    <button id="snd">send</button><br>
    <script>
    let c = gotalk.connection()
      .on('open'.() = > log(`connection opened`))
      .on('close'.reason= > log(`connection closed (reason: ${reason}) `))
    let btn = document.querySelector("#snd")
    let txt = document.querySelector("#txt")
    btn.onclick = async() = > {let content = txt.value
      if (content.length === 0) {
        alert("no message")
        return
      }
      let res = await c.requestp('echo', content)
      log(`reply: The ${JSON.stringify(res, null.2)}`)
      return false
    }
    function log(message) {
      document.body.appendChild(document.createTextNode(message))
      document.body.appendChild(document.createElement("br"))}</script>
  </body>
</html>
Copy the code

The first call to the server is gotalk.Connection (), which returns a connection object. Call the on() method of this object to register the connection establishment and disconnection callbacks, respectively. You then add a callback to the button that sends the contents of the input box to the server with each click. Call the connection object’s requestp() method to send the request. The first parameter is the message name, which corresponds to the name registered on the server using gotalk.handle (). The second, the processing parameter, is sent to the server. Promise is used here to handle asynchronous requests and responses, and async-await synchronization is written for ease of writing and understanding. The content of the response is displayed directly on the page:

Note that the gotalk.js file needs to be placed in the gotalk directory of the server’s run directory.

Protocol format

Gotalk is an ASCII based protocol format designed to be human-readable and flexible. Each transmitted message is divided into several parts: type identification, request ID, operation, and message content.

  • Type identifier: A single byte is used to indicate the type of message, whether it is a request or response message, whether it is a streaming or non-streaming message, and error, heartbeat, and notification also have their own specific type identifier.
  • Request ID: 4 bytes to facilitate matching of responses. Due to thegotalkYou can send any request at the same time and receive the response to the previous request. So you need to have an ID that identifies the received response to which request was previously sent.
  • Action: the message name we defined above, such as “echo”.
  • Message content: Use length + actual content format.

Look at an example of an official request:

+------------------ SingleRequest
|   +---------------- requestID   "0001"
|   |      +--------- operation   "echo" (text3Size 4, text3Value "echo")
|   |      |       +- payloadSize 25
|   |      |       |
r0001004echo00000019{"message":"Hello World"}
Copy the code
  • r: indicates a single request.
  • 0001: The request ID is 1, which is a hexadecimal code.
  • 004echo: This part indicates that the operation is “echo”. The length must be specified before the actual string content, otherwise the receiver does not know where the content ends.004Indicates that the length of “echo” is 4, also in hexadecimal code.
  • 00000019{"message":"Hello World"}: This is the content of the message. Also specify length, hexadecimal00000019The length is 25.

You can check the official documentation for detailed formats.

Using this readable format makes troubleshooting a lot easier. But in practice, security and privacy issues may need to be considered.

The chat room

Examples includes a websocket-based chat room sample application. Features are as follows:

  • Rooms can be created, with 3 rooms by defaultanimals/jokes/golang;
  • Chat in the room (basic function);
  • A simple Web page.

Run:

$ go run server.go
Copy the code

Open your browser and enter “localhost:1235”. The following information is displayed:

Now you can create the room and chat in the room.

The whole implementation has several key points:

First, the WebSocket handler created by gotalk.webSockethandler () can set connection callbacks:

gh := gotalk.WebSocketHandler()
gh.OnConnect = onConnect
Copy the code

Set random user name in callback and store current connected gotalk.Sock for message broadcast:

func onConnect(s *gotalk.WebSocket) {
  socksmu.Lock()
  defer socksmu.Unlock()
  socks[s] = 1

  username := randomName()
  s.UserData = username
}
Copy the code

Second, GoTalk sets the handler function to take two arguments, the first representing the current connection and the second the actual message received.

Third, the enableGracefulShutdown() function implements a graceful shutdown of the Web server, which is well worth learning. Upon receiving SIGINT signal, close all connections before exiting the program. Note that listening for signals is not the same goroutine as running the HTTP server, and see how they work together:

func enableGracefulShutdown(server *http.Server, timeout time.Duration) chan struct{} {
  server.RegisterOnShutdown(func(a) {
    // close all connected sockets
    fmt.Printf("graceful shutdown: closing sockets\n")
    socksmu.RLock()
    defer socksmu.RUnlock()
    for s := range socks {
      s.CloseHandler = nil // avoid deadlock on socksmu (also not needed)
      s.Close()
    }
  })
  done := make(chan struct{})
  quit := make(chan os.Signal, 1)
  signal.Notify(quit, syscall.SIGINT)
  go func(a) {
    <-quit // wait for signal

    fmt.Printf("graceful shutdown initiated\n")
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()

    server.SetKeepAlivesEnabled(false)
    iferr := server.Shutdown(ctx); err ! =nil {
      fmt.Printf("server.Shutdown error: %s\n", err)
    }

    fmt.Printf("graceful shutdown complete\n")
    close(done)
  }()
  return done
}
Copy the code

Server.listenandserve () returns an HTTP.ErrServerClosed error and exits the loop:

done := enableGracefulShutdown(server, 5*time.Second)

// Start server
fmt.Printf("Listening on http://%s/\n", server.Addr)
iferr := server.ListenAndServe(); err ! =nil&& err ! = http.ErrServerClosed {panic(err)
}

<- done
Copy the code

The chat room function is relatively simple, the code is relatively short, suggest in-depth understanding. It’s also easy to extend on top of that.

conclusion

Gotalk implements a simple, easy-to-use communication library. JavaScript file gotalk.js is provided to facilitate the development of Web programs. The protocol format is clear and easy to debug. Rich examples built in. The entire library code is not long, I suggest in-depth understanding.

If you find a fun and useful Go library, please Go to GitHub and submit issue😄

reference

  1. Gotalk GitHub:github.com/rsms/gotalk
  2. GitHub: github.com/darjun/go-d…

I

My blog is darjun.github. IO

Welcome to follow my wechat public account [GoUpUp], learn together, progress together ~