DEV Community

loading...

Getting Started with Sockets Concurrently in GoLang

alicewilliamstech profile image Alice Williams ・4 min read

Getting Started with Sockets Concurrently in GoLang

What are sockets?

Sockets, or 'Web Sockets', are a TCP-based internet protocol for real-time and persistent connections between a server and one or more client(s). This protocol is faster than standard HTTP connections, allows for a two-way back-and-forth transfer of data between server and clients. Initialized with a HTTP upgrade request as a handshake operation, after which all parties, server and clients, may send data at any time, from either direction, at any time. Generally used for applications such as social feeds, chat clients, multiplayer games, multimedia streaming, and collaborative development sessions.

Why GoLang?

Concurrency, the biggest advantage to our use-case, socket programming, given it's asynchronous design. Along with the fact that GoLang is an extremely modern high-level language making development of established applications quick and efficient. Plus I just love the simplistic elegance of the language! :) Don't know GoLang? Well, this is a great low-level project to see some basic concepts at play, especially goroutines, GoLang's concurrency feature.

Project Start

This tutorial is based off of my open-source repository.

Create a new project folder and initialize a new git repository to track changes, even if only stored locally by opening a shell or command-line window within your new project folder and running, git init. Make two sub-folders in your project folder, for the server and client. Initialize a GoLang module in each folder by running, go mod init, within each folder, you may need to supply the path if you're outside of your GOPATH. Create two new files, each called, main.go, as the application entry-points, inside of each sub-folder.

Socket Server

The 'Hello World' step:

package main

import (
    "fmt"
)

const (
    connHost = "localhost"
    connPort = "8080"
    connType = "tcp"
)

func main() {
    fmt.Println("Starting " + connType + " server on " + connHost + ":" + connPort)

}

Listening and the main loop:

package main

import (
    "fmt"
    "os"
    "net"
)

const (
    connHost = "localhost"
    connPort = "8080"
    connType = "tcp"
)

func main() {
    fmt.Println("Starting " + connType + " server on " + connHost + ":" + connPort)
    l, err := net.Listen(connType, connHost+":"+connPort)
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    defer l.Close()

    for {

    }
}

Accept client connections:

package main

import (
    "fmt"
    "os"
    "net"
)

const (
    connHost = "localhost"
    connPort = "8080"
    connType = "tcp"
)

func main() {
    fmt.Println("Starting " + connType + " server on " + connHost + ":" + connPort)
    l, err := net.Listen(connType, connHost+":"+connPort)
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    defer l.Close()

    for {
        c, err := l.Accept()
        if err != nil {
            fmt.Println("Error connecting:", err.Error())
            return
        }
        fmt.Println("Client connected.")

        fmt.Println("Client " + c.RemoteAddr().String() + " connected.")
    }
}

Handling clients concurrently:

package main

import (
    "bufio"
    "fmt"
    "log"
    "net"
    "os"
)

const (
    connHost = "localhost"
    connPort = "8080"
    connType = "tcp"
)

func main() {
    fmt.Println("Starting " + connType + " server on " + connHost + ":" + connPort)
    l, err := net.Listen(connType, connHost+":"+connPort)
    if err != nil {
        fmt.Println("Error listening:", err.Error())
        os.Exit(1)
    }
    defer l.Close()

    for {
        c, err := l.Accept()
        if err != nil {
            fmt.Println("Error connecting:", err.Error())
            return
        }
        fmt.Println("Client connected.")

        fmt.Println("Client " + c.RemoteAddr().String() + " connected.")

        go handleConnection(c)
    }
}

func handleConnection(conn net.Conn) {
    buffer, err := bufio.NewReader(conn).ReadBytes('\n')

    if err != nil {
        fmt.Println("Client left.")
        conn.Close()
        return
    }

    log.Println("Client message:", string(buffer[:len(buffer)-1]))

    conn.Write(buffer)

    handleConnection(conn)
}

Testing without a client:

If you're developing on Linux you can test your new socket server application without writing the client application first. Start your socket server with, go run main.go, from the server sub-folder within your shell. Now run, nc localhost 8080, replacing the host and port number if needed. There are applications to run the nc (netcat) command on Windows, however, I don't know the best of options here.

Socket Client

The 'Hello World' step:

package main

import (
    "fmt"
)

const (
    connHost = "localhost"
    connPort = "8080"
    connType = "tcp"
)

func main() {
    fmt.Println("Connecting to " + connType + " server " + connHost + ":" + connPort)
}

Connecting to the server:

package main

import (
    "fmt"
)

const (
    connHost = "localhost"
    connPort = "8080"
    connType = "tcp"
)

func main() {
    fmt.Println("Connecting to " + connType + " server " + connHost + ":" + connPort)
}
    conn, err := net.Dial(connType, connHost+":"+connPort)
    if err != nil {
        fmt.Println("Error connecting:", err.Error())
        os.Exit(1)
    }

Handling input and relaying response:

package main

import (
    "fmt"
)

const (
    connHost = "localhost"
    connPort = "8080"
    connType = "tcp"
)

func main() {
    fmt.Println("Connecting to " + connType + " server " + connHost + ":" + connPort)Getting Started with Sockets in GoLang
}
    conn, err := net.Dial(connType, connHost+":"+connPort)
    if err != nil {
        fmt.Println("Error connecting:", err.Error())
        os.Exit(1)
    }
    reader := bufio.NewReader(os.Stdin)

    for {
        fmt.Print("Text to send: ")

        input, _ := reader.ReadString('\n')

        message, _  := bufio.NewReader(conn).ReadString('\n')

        log.Print("Server relay:", message)
    }

Testing the client:

Run your socket server with, go run main.go, from the server project sub-folder on your shell or command-line. Now open another shell or command-line session within the client folder and run, go run main.go here as well to start the client application. Any text entered into the client will be sent to the server, displayed with a timestamp, and relayed back, being displayed again.

This tests the basic functionality of the socket server-client interface and can be extended upon by expanding the server.go handleConnection function. Each client is handled concurrently by the server and as many clients may connect as your system can handle running, you may also connect through other local systems or over the internet if you expose the port to traffic.

Wrapping up

Well now you have stand-alone socket server and client interfaces, in pure standard-library GoLang! So what's next you ask? The sky's the limit, write an IRC-style chat client or a streaming application, just take it one step at a time, and that's one reason we started with a new git repository to manage all those changes you'll start adding.

My open-source Github repository the code for this tutorial is stored in has further line-by-line documentation.


All The Best and Happy Coding!
By, Alice Williams

Discussion (3)

pic
Editor guide
Collapse
jaakidup profile image
Jaaki

Hey Alice, I love what your doing here.

There is an issue with your code though, so I would like it if you had another look at it. [hint: it's in the last code block]

Hit me up when you find it :D

Collapse
yeboahnanaosei profile image
Nana Yeboah

This is just unfortunate. Why don't you just point out what the issue is to the benefit of everybody who comes across this post.

Collapse
jaakidup profile image
Jaaki • Edited

She posted here because she just started learning go.
I'm giving her space to learn what she did wrong and fix the errors.
I just pointed out the easy to spot one, not the others