SoFunction
Updated on 2025-03-05

Use Go goroutine to implement concurrent Clock services

Network programming is an area where concurrency shows its skills. Since servers are the most typical programs that require many connections to be processed simultaneously, these connections generally come from independent clients.

In this article, we will use the go language net package, which provides basic components for writing a network client or server program, regardless of whether the communication between the two uses TCP, UDP or Unix domain sockets.

Our first example is a clock server that executes sequentially, which writes the current time to the client every second:

// Clock1 is a TCP server that periodically writes the time.
package main
import (
    "io"
    "log"
    "net"
    "time"
)
func main() {
    listener, err := ("tcp", "localhost:8000")
    if err != nil {
        (err)
    }
    for {
        conn, err := ()
        if err != nil {
            (err) // ., connection aborted
            continue
        }
        handleConn(conn) // handle one connection at a time
    }
}
func handleConn(c ) {
    defer ()
    for {
        _, err := (c, ().Format("15:04:05\n"))
        if err != nil {
            return // ., client disconnected
        }
        (1 * )
    }
}

The Listen function creates an object that will listen for connections coming on a network port. In this example, we use TCP's localhost:8000 port. The Accept method of the listener object will block directly until a new connection is created, and then an object will be returned to represent the connection.

The handleConn function handles a complete client connection. In a for-loss loop, use() to get the current moment and write to the client. Since the interface is implemented, we can write content directly to it. This dead loop will be executed until the write fails. The most likely reason is that the client actively disconnects. In this case, the handleConn function will use the defer call to close the connection on the server side, and then return to the main function and continue to wait for the next connection request.

Methods provide a way to format date and time information. Its parameter is a formatting template that identifies how to format the time, and this formatting template is limited to Mon Jan 2 03:04:05PM 2006 UTC-0700. There are 8 parts (what of the week, month, day of the month...). The previous template can be combined in any form; the part that appears in the template will be used as a reference to output the time format.

In the above example we only used hours, minutes and seconds. The time package defines many standard time formats, such as time.RFC1123. The same strategy will also be used when performing reverse formatting operations. (Translator's note: This is a rather weird place in Go language compared to other languages. You need to remember that the formatted string is 3:4:5:00 pm on January 2, UTC-0700, unlike other languages, which is not the same as Y-m-d H:i:s. Of course, you can use 1234567 to remember it, which is not troublesome.)

In order to connect to the server in the example, we need a client program, such as the netcat tool (nc command), which can be used to perform network connection operations.

$ go build /ch8/clock1
$ ./clock1 &
$ nc localhost 8000
13:58:54
13:58:55
13:58:56
13:58:57
^C

The client displays the time sent by the server. We use Control+C to interrupt the client's execution. On Unix systems, you will see a response like ^C. If your system does not have the nc tool installed, you can use telnet to achieve the same effect, or you can use our simple telnet program written in Go, and you can simply create a TCP connection:

// Netcat1 is a read-only TCP client.
package main
import (
    "io"
    "log"
    "net"
    "os"
)
func main() {
    conn, err := ("tcp", "localhost:8000")
    if err != nil {
        (err)
    }
    defer ()
    mustCopy(, conn)
}
func mustCopy(dst , src ) {
    if _, err := (dst, src); err != nil {
        (err)
    }
}

This program will read data from the connection and write the read content to the standard output until an end of file condition or an error occurs. We will use the mustCopy function in several examples in this section. Let's run two clients at the same time to perform a test. Here you can open two terminal windows. The left one below is the output of one of them, and the right one below is the output of the other:

$ go build /ch8/netcat1
$ ./netcat1
13:58:54                               $ ./netcat1
13:58:55
13:58:56
^C
                                       13:58:57
                                       13:58:58
                                       13:58:59
                                       ^C
$ killall clock1

The killall command is a Unix command line tool that can kill all processes matching the name with a given process name.

The second client must wait for the first client to complete the work so that the server can continue to execute backwards; because our server program here can only handle one client connection at the same time. We have made some minor changes to the server program to support concurrency: add the go keyword in the handleConn function call, so that each handleConn call enters an independent goroutine.

for {
    conn, err := ()
    if err != nil {
        (err) // ., connection aborted
        continue
    }
    go handleConn(conn) // handle connections concurrently
}

Now multiple clients can receive time at the same time:

$ go build /ch8/clock2
$ ./clock2 &
$ go build /ch8/netcat1
$ ./netcat1
14:02:54                               $ ./netcat1
14:02:55                               14:02:55
14:02:56                               14:02:56
14:02:57                               ^C
14:02:58
14:02:59                               $ ./netcat1
14:03:00                               14:03:00
14:03:01                               14:03:01
^C                                     14:03:02
                                       ^C
$ killall clock2

So far, it's done!

This is the end of this article about using Go goroutine to implement concurrent Clock services. For more related Go goroutine content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!