I believe everyone is familiar with chat services, because they are often used in our lives.
We use Go concurrency to implement a chat server, which allows some users to broadcast text messages to all other users through the server.
There are four goroutines in this program.
Main and broadcaster are each a goroutine instance, and each client connection will have a goroutine of handleConn and clientWriter.
broadcaster is a good example of the usage of select because it needs to handle three different types of messages.
The work of main goroutine we will demonstrate below is the connection between listen and accept (a concept in network programming) from the client. For each connection, the program will create a new handleConn goroutine.
func main() { listener, err := ("tcp", "localhost:8000") if err != nil { (err) } go broadcaster() for { conn, err := () if err != nil { (err) continue } go handleConn(conn) } }
Then there is the goroutine of the broadcaster.
Its internal variable clients will record the current collection of clients that have established the connection, and the content recorded is that each client's message sends the channel's "qualification" information.
type client chan<- string // an outgoing message channel var ( entering = make(chan client) leaving = make(chan client) messages = make(chan string) // all incoming client messages ) func broadcaster() { clients := make(map[client]bool) // all connected clients for { select { case msg := <-messages: // Broadcast incoming message to all // clients' outgoing message channels. for cli := range clients { cli <- msg } case cli := <-entering: clients[cli] = true case cli := <-leaving: delete(clients, cli) close(cli) } } }
The broadcaster listens to the entry and leaving channels from the global network to know the client's arrival and departure events.
When it receives one of these events, the clients collection is updated, and when the event is a departure behavior, it closes the client's message sending channel. The broadcaster will also listen to global message channels, and all clients will send messages to this channel. When a broadcaster receives a message, it will broadcast it to all clients connected to the server.
Now let's look at the goroutine of each client.
The handleConn function will create a message for its client to send the channel and notify the client of the arrival of the client through the entry channel. It will then read each line of text sent by the client, and send these texts through the global message channel, and use the sender's prefix to indicate the message identity. When the client sends it, handleConn will notify the client of leaving and close the connection by leaving the channel.
func handleConn(conn ) { ch := make(chan string) // outgoing client messages go clientWriter(conn, ch) who := ().String() ch <- "You are " + who messages <- who + " has arrived" entering <- ch input := (conn) for () { messages <- who + ": " + () } // NOTE: ignoring potential errors from () leaving <- ch messages <- who + " has left" () } func clientWriter(conn , ch <-chan string) { for msg := range ch { (conn, msg) // NOTE: ignoring network errors } }
In addition, handleConn creates a clientWriter goroutine for each client, which receives broadcast messages in the channel that sends messages to the client and writes them to the client's network connection. The client's read loop will be terminated after the broadcaster receives the leaving notification and closes the channel.
The following demonstrates the situation when the server has two active client connections and runs in two windows, use netcat to chat:
$ go build /ch8/chat $ go build /ch8/netcat3 $ ./chat & $ ./netcat3 You are 127.0.0.1:64208 $ ./netcat3 127.0.0.1:64211 has arrived You are 127.0.0.1:64211 Hi! 127.0.0.1:64208: Hi! 127.0.0.1:64208: Hi! Hi yourself. 127.0.0.1:64211: Hi yourself. 127.0.0.1:64211: Hi yourself. ^C 127.0.0.1:64208 has left $ ./netcat3 You are 127.0.0.1:64216 127.0.0.1:64216 has arrived Welcome. 127.0.0.1:64211: Welcome. 127.0.0.1:64211: Welcome. ^C 127.0.0.1:64211 has left”
When keeping a chat session with n clients, this program will have 2n+2 concurrent goroutines, but this program does not require explicit locks. clients This map is restricted to a separate goroutine, broadcaster, so it cannot be accessed concurrently.
The variables shared by multiple goroutines are only instances of these channels and the two things are concurrently safe.
This is the end of this article about implementing a simple chat service based on Go goroutine. For more related Go goroutine chat service content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!