Table of Contents
- Introduction
- Prerequisites
- Setting Up the Chat Server
- Creating the Server
- Handling Connections
- Broadcasting Messages
- Testing the Chat Server
- Conclusion
Introduction
In this tutorial, we will learn how to create a simple chat server using Go (or Golang). By the end of this tutorial, you will be able to build a basic chat server that allows multiple clients to connect and exchange messages in real-time.
Prerequisites
Before getting started, make sure you have the following prerequisites:
- Basic understanding of Go programming language.
- Go installed on your machine.
- Familiarity with TCP/IP networking concepts.
Setting Up the Chat Server
To begin, let’s create a new directory for our chat server project. Open your terminal or command prompt and run the following command:
mkdir chat-server
Navigate to the newly created directory:
cd chat-server
Creating the Server
Now let’s create the main file for our chat server. In the chat-server
directory, create a new file called main.go
and open it in a text editor.
Add the following code to import the necessary packages and define the main function:
package main
import (
"fmt"
"log"
"net"
)
func main() {
// Server code goes here
}
Inside the main function, we will create a TCP server that listens for incoming client connections. Add the following code to create a server listening on a specific port (e.g., 9000):
func main() {
// Create server
ln, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
fmt.Println("Chat server started on port 9000")
// Server code continues here
}
The net.Listen
function creates a new TCP listener on the specified port. If there’s an error, we log it and exit the program. The defer ln.Close()
statement ensures that the listener is properly closed when the server exits.
Handling Connections
Next, we need to handle incoming client connections. Modify the main
function as follows:
func main() {
// Create server
ln, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
fmt.Println("Chat server started on port 9000")
// Handle connections
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
// Handle individual client connection
}
Within the for
loop, we call ln.Accept()
to accept incoming client connections. If there’s an error accepting a connection, we log it and continue to the next iteration of the loop.
For each successful connection, we spawn a new goroutine (using go
) and pass the connection to the handleConnection
function. Goroutines allow us to handle multiple client connections concurrently.
Broadcasting Messages
Now let’s implement the functionality to broadcast messages from one client to all connected clients. Update the handleConnection
function as follows:
func handleConnection(conn net.Conn) {
defer conn.Close()
// Add client to connected clients list
// ...
// Read and broadcast messages
for {
// Read message from client
// ...
// Broadcast message to connected clients
// ...
}
}
In the handleConnection
function, we defer closing the connection to ensure it is closed when the function returns.
Inside the loop, we will read messages from the client and broadcast them to all connected clients. Add the following code to read messages:
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
message := scanner.Text()
if message == "/quit" {
break
}
// Broadcast message to connected clients
// ...
}
if err := scanner.Err(); err != nil {
log.Println(err)
}
We use a bufio.Scanner
to read messages line by line from the client. If the client sends “/quit”, we exit the loop and close the connection.
To broadcast messages, we need to maintain a list of all connected clients. Add the following code before the message reading loop:
clients := make(map[net.Conn]bool)
for client := range clients {
_, err := client.Write([]byte("New client joined"))
if err != nil {
log.Println(err)
client.Close()
delete(clients, client)
}
}
clients[conn] = true
The clients
variable is a map with net.Conn
as the key and a boolean value. It keeps track of whether a client is still connected or not.
In the loop, we send a message to all currently connected clients to notify them about the new client.
To broadcast messages, add the following code after the message reading loop:
for client := range clients {
_, err = client.Write([]byte(message))
if err != nil {
log.Println(err)
client.Close()
delete(clients, client)
}
}
This loop sends the received message to all connected clients, excluding the client who sent the message.
Testing the Chat Server
To test the chat server, let’s create a simple client program that connects to the server and allows us to send messages.
Create a new file called client.go
in the chat-server
directory and add the following code:
package main
import (
"bufio"
"fmt"
"log"
"net"
"os"
)
func main() {
conn, err := net.Dial("tcp", "localhost:9000")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
message := scanner.Text()
_, err := fmt.Fprintf(conn, "%s\n", message)
if err != nil {
log.Println(err)
break
}
if message == "/quit" {
break
}
}
if err := scanner.Err(); err != nil {
log.Println(err)
}
}
This client program connects to the chat server running on localhost:9000
. It provides a console interface to send messages to the server. The loop reads input from the user, sends it to the server, and exits when “/quit” is entered.
Open two terminal windows and run the chat server in one window:
go run main.go
In the other window, run the client program:
go run client.go
Type messages in the client window and press Enter to send them to the server. The server should broadcast the messages to all connected clients, including the client that sent the message.
Conclusion
Congratulations! You have successfully created a simple chat server in Go. This server allows multiple clients to connect and exchange messages in real-time. You have learned how to handle connections, read and broadcast messages, and test the chat server using a simple client program.
Feel free to explore further and extend the functionality of the chat server. You can add username support, implement a chat room system, or even secure the communication using encryption.
Remember to always handle errors properly, add necessary error checking, and consider the performance and scalability aspects of your application.