- Published on
Socket programming (using Go)
What is a socket?
A socket is that thing with 3 holes on your wall. :)
On a serious note, a socket is a standard way to perform communication through a computer operating system. Sockets are normally used to receive or send data over a specified network. The data is normally transmitted at the session layer of the OSI model.
There are two common types of sockets:
- Stream sockets
- Datagram sockets
Stream sockets
Stream packets provide a two way communication similar to when you are talking to someone on phone i.e, it is a full-duplex
communication. Data flows to and fro between the (atleast) two parties. Stream sockets use the Transmission Control Protocol (TCP) to relay data in chunks called packets in sequence and without errors. Computer servers and their clients use this kind of sockets to communicate.
Datagram sockets
Unlike in stream sockets, communication in datagram sockets is one way. For example in walkie-talkies. Only one party can use the communication channel to relay whatever message they have. Therefore, the communication is termed as half-duplex
Datagram sockets use the User Datagram Protocol (UDP) which is lightweight amd highly customizable.
In this article, we will focus on a subset of stream sockets called web sockets.
Working with web sockets in Go
While dealing with sockets, there are two main operations that you will perform, i.e reading
from a source (e.g a mail server) and writing
to a destination (e.g a client).
Go has a sub-directory package called golang.org/x/net/websocket
. It's documentation states:
This package currently lacks some features found in an alternative and actively maintained Websockets package: https://pkg.go.dev/github.com/gorilla/websocket.
For the above matter, we shall use the suggested package.
Writing a web socket server and client
- Create a directory for your code, initialize a Go project, create files and initialize VCS using Git
$ cd ~ && mkdir websocket\
&& cd websocket\
&& go mod init github.com/your-name/websocket\
&& touch server.go client.go Makefile\
&& git init
- In your server.go file
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// HTTP handler
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Handling /")
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
return
}
for n := 0; n < 10; n++ {
msg := "Hello" + string(n + 48)
fmt.Println("Sending to client: " + msg)
err = conn.WriteMessage(websocket.TextMessage,
[]byte(msg))
_, reply, err := conn.ReadMessage()
if err != nil {
fmt.Println("Can't receive")
break
}
fmt.Println("Received back from client: " +
string(reply[:]))
}
conn.Close()
}
func main() {
http.HandleFunc("/", Handler)
err := http.ListenAndServe("localhost:5000", nil)
checkError(err)
}
func checkError(err error) {
if err != nil {
log.Fatalln("Fatal error ", err.Error())
}
}
- Create a client in your
client.go
file
package main
import (
"fmt"
"io"
"log"
"net/http"
"os"
"github.com/gorilla/websocket"
)
func main() {
if len(os.Args) != 2 {
log.Fatalln("Usage: ", os.Args[0], "ws://host:port")
}
service := os.Args[1]
header := make(http.Header)
header.Add("Origin", "http://localhost:5000")
conn, _, err := websocket.DefaultDialer.Dial(service, header)
checkError(err)
for {
_, reply, err := conn.ReadMessage()
if err != nil {
if err == io.EOF {
// graceful shutdown by server
fmt.Println(`EOF from server`)
break
}
if websocket.IsCloseError(err,
websocket.CloseAbnormalClosure) {
fmt.Println(`Close from server`)
break
}
fmt.Println("Couldn't receive msg " +
err.Error())
break
}
fmt.Println("Received from server: " +
string(reply[:]))
// return the msg
err = conn.WriteMessage(websocket.TextMessage, reply)
if err != nil {
fmt.Println("Couldn't return msg")
break
}
}
}
func checkError(err error) {
if err != nil {
log.Fatalln("Fatal error ", err.Error())
}
}
- For easier running of shell commands, create a
Makefile
$ vi Makefile
And then, copy the following to the Makefile
server:
go run server.go
client:
go run client.go
- Testing
In your terminal, make client ws://localhost:5000
Received from server: Hello 0
Received from server: Hello 9
Close from server
Back on the server, we see
Handling /
Sending to client: Hello 0
Received back from client: Hello 0
Sending to client: Hello 1
Sending to client: Hello 9
Received back from client: Hello 9
Resources
- Network Programming with Go Language - Jan Newmarch Ronald Petty
- Network Automation with Go - Nicolas Leiva, Michael Kashin
Let me know what your thoughts are about this article hi[at]luigimorel.com. Cheers!