# How to use the jsonrpc codec with websockets in Go

WebSockets and JSON-RPC are powerful technologies that can significantly enhance real-time communication and data exchange in web applications. WebSockets provide full-duplex communication channels over a single TCP connection, while JSON-RPC is a lightweight remote procedure call protocol based on JSON. In this article, we'll explore how to leverage these technologies together to implement an efficient and scalable server in Go.

## Understanding JSON-RPC

JSON-RPC is a simple yet effective protocol for remote procedure calls over HTTP or other transport layers. It allows clients to invoke methods on a server and receive responses in a structured JSON format. The protocol is lightweight, language-agnostic, and easy to implement, making it an excellent choice for web applications.

We are going to use the version **JSON-RPC 2.0** which follows this format:

```bash

     {"jsonrpc":"2.0","method":"HelloService.Hello","params":{"msg":"John"},"id":0}
    
```

## Using WebSockets in Go

WebSockets enable bidirectional communication between clients and servers, allowing real-time data transfer. In Go, the standard library provides support for WebSockets through the [**golang.org/x/net/websocket**](http://golang.org/x/net/websocket) package. There are other more advanced packages for WebSockets, like the **gorilla/websocket** package, but I'll stick to the standard one for this example, as it is simpler.

## Implementing a JSON-RPC Server using WebSockets in Go

For this example, let's create a simple JSON-RPC server in Go that communicates with clients over WebSockets.

### Step 1: Define the service

Make sure you have Go installed and set up a Go workspace. Create a new directory for the project, and inside it, create the Go file named **internal/service/hello.go** containing the definition of the service that the server is going to expose through a WebSockets interface.

```go

package service

import (
	"log"
)

type HelloService struct{}

type HelloRequest struct {
	Name string
}

type HelloResponse struct {
	Greeting string `json:"string"`
}

func (s *HelloService) Hello(req *HelloRequest, res *HelloResponse) error {
	log.Println("Execute method: HelloService.Hello()")
	res.Greeting = "Hello: " + req.Name
	return nil
}
    
```

### Step 2: Implementing the JSON-RPC Server

Then we create the file **cmd/server/main.go** file to set up a WebSocket server and handle incoming JSON-RPC requests.

```go

package main

import (
	"bytes"
	"golang.org/x/net/websocket"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"

	"go-websocket-jsonrpc/internal/service"
)

func wsHandleRequest(ws *websocket.Conn) {
	for {
		var req []byte
		err := websocket.Message.Receive(ws, &req)
		if err != nil {
			log.Println("ReadMessage:", err)
			return
		}

		log.Println("ServeRequest...")
		var res bytes.Buffer
		err = rpc.ServeRequest(jsonrpc.NewServerCodec(struct {
			io.ReadCloser
			io.Writer
		}{
			ioutil.NopCloser(bytes.NewReader(req)),
			&res,
		}))
		if err != nil {
			log.Println("ServeRequest:", err)
			return
		}

		err = websocket.Message.Send(ws, res.Bytes())
		if err != nil {
			log.Println("WriteMessage:", err)
			return
		}
	}
}

func main() {
	log.Println("Starting http server")

	rpc.Register(&service.HelloService{})

	http.Handle("/ws", websocket.Handler(wsHandleRequest))
	http.ListenAndServe("localhost:8080", nil)
}
    
```

### Step 3: Implementing the JSON-RPC Client

Finally, we create the file cmd/client/main.go to implement a Go WebSocket client that reads strings from the command line, sends them to the server, and prints out the responses.

```go

package main

import (
	"bufio"
	"golang.org/x/net/websocket"
	"log"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"

	"go-websocket-jsonrpc/internal/service"
)

func sayHello(c *rpc.Client, name string) {
	req := service.HelloRequest{Name: name}
	var res service.HelloResponse

	err := c.Call("HelloService.Hello", req, &res)
	if err != nil {
		log.Fatal("error:", err)
	}
	log.Printf("Response: %s", res.Greeting)
}

func main() {
	ws, err := websocket.Dial("ws://localhost:8080/ws", "", "http://localhost/")
	if err != nil {
		log.Fatal(err)
	}
	defer ws.Close()

	c := jsonrpc.NewClient(ws)

	reader := bufio.NewReader(os.Stdin)
	for {
		text, _ := reader.ReadString('\n')
		sayHello(c, text)
	}
}
    
```

### Step 4: Test the example

Now that we have the server and client ready, we start the server in one terminal, and the client on another one. Every time we type a string in the client terminal and press Enter, the client will send that string to the server, and we will see right away the response from the server.

```bash

>./bin/client
./bin/client
john
2023/08/02 05:27:57 Response: Hello: john
lisa
2023/08/02 05:28:07 Response: Hello: lisa
tom
2023/08/02 05:28:08 Response: Hello: tom
```

## Conclusion

WebSockets and JSON-RPC can work harmoniously together, providing an efficient and real-time communication mechanism for web applications. In this article, we explored how to implement a JSON-RPC server using WebSockets in Go. You can build upon this example to create more sophisticated applications with real-time updates and bidirectional data flow. Whether it's real-time chats, live notifications, or dynamic dashboards, the combination of WebSockets and JSON-RPC in Go is a powerful solution for modern web development.
