Beginner's Guide to Socket Programming in Go
In Go, the net package provides the necessary APIs to implement socket communication between two of the topmost TCP/IP layers: application and transport.
Join the DZone community and get the full member experience.
Join For FreeA socket is a connection endpoint for communication over a network. It is designated by a name and an address and shows the way to establish communication links with the help of socket APIs over remote and local processes. A program stored or installed on the hard disk is in a dormant state. This program in execution is called a process. Most programs in execution are nothing but a collection of a number of processes, communicating and sharing information amongst each other. This is typical of any multiprocessing or multitasking environment. Although socket is not the only one, it is a way to establish communication between the processes. The processes which use sockets for communication can be from the same computing system, locally, or from a system located over a remote network. This means that sockets can be used as a communication means for both standalone as well as network applications. Most programming languages provide the necessary library support for socket programming. The API supported by the library is the network standard for TCP/IP. Therefore, the underlying concept of socket programming in any language is similar.
What is the Client-Server Model?
In a typical client/server application model, the server waits for the client to start the conversation. However, do not assume that client/server always means remote communication over a network. It can very well be two processes residing in the same system locally. In such a case, a single machine acts as a network providing the communication between a client and server program that goes through layers of a TCP/IP protocol stack. Read: Java vs. Go Microservices - Load Testing (Rematch)
The net
Package in Go
In Go, the net
package provides the necessary APIs to implement socket communication between two of the topmost TCP/IP layers: application and transport. The net
package provides an interface for network I/O, TCP/IP, UDP, domain name resolution, and Unix domain sockets. This package also enables low-level access to network primitives. Basic interfaces for communication are provided by functions like Dial
, Listen
, and Accept
and the associated Conn
and Listener
interfaces.
Client Socket Basics in Go
As mentioned, a socket is responsible for establishing a connection between the endpoints of two hosts. The basic operation for the client can be discreetly laid out as follows:
- Establish connection to remote host
- Check if any connection error has occurred or not
- Send and receive bytes of information.
- Finally, close the connection
Here is an example of how to use a socket in Go. The client simply establishes a connection with the server, sends some bytes of information, and receives some data from the server:
// socket-client project main.go
package main
import (
"fmt"
"net"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
func main() {
//establish connection
connection, err := net.Dial(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
panic(err)
}
///send some data
_, err = connection.Write([]byte("Hello Server! Greetings."))
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
fmt.Println("Error reading:", err.Error())
}
fmt.Println("Received: ", string(buffer[:mLen]))
defer connection.Close()
}
Note that we have used the Dial
function of the net
package to connect to the address on the named network. In TCP and UDP, the address is in the form of “host:port”
. We can provide the named host or the IP address for the host part. The named host will automatically resolve IP addresses. The port must be a literal port number or service name. For example, the standard port for HTTP requests is 80
. Here in our code, we have used port number: 9988
, assuming it to be unused. The function either establishes a connection or returns an error if unsuccessful. Once the connection is established, we send data bytes using the Write
function of the established connection object. Similarly, we may read data bytes using the Read
function associated with the same connection. Read: Understanding Concurrency Patterns in Go
Server Socket Basics in Go
The socket server waits for incoming calls and responds accordingly. A server is bound by a port and listens to the incoming TCP connections. As the client socket attempts to connect to the port, the server wakes up to negotiate the connection by opening sockets between two hosts. It is this socket between the two forms that acts as the interface for communication and information exchange. In short, what the server socket does is:
- Create a socket on a specific port
- Listen to any attempt of connection to that port
- If the connection is successful, communication can begin between client and server
- The communication, however, should be according to the agreed protocol
- Continue listening to the port
- Once the connection is closed, the server stops listening and exits
Here is a server example with reference to the client program code above, written in Golang:
// socket-server project main.go
package main
import (
"fmt"
"net"
"os"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
func main() {
fmt.Println("Server Running...")
server, err := net.Listen(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
fmt.Println("Error listening:", err.Error())
os.Exit(1)
}
defer server.Close()
fmt.Println("Listening on " + SERVER_HOST + ":" + SERVER_PORT)
fmt.Println("Waiting for client...")
for {
connection, err := server.Accept()
if err != nil {
fmt.Println("Error accepting: ", err.Error())
os.Exit(1)
}
fmt.Println("client connected")
go processClient(connection)
}
}
func processClient(connection net.Conn) {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
fmt.Println("Error reading:", err.Error())
}
fmt.Println("Received: ", string(buffer[:mLen]))
_, err = connection.Write([]byte("Thanks! Got your message:" + string(buffer[:mLen])))
connection.Close()
}
Here, the server is set using the Listen
function, which takes the host port and the type of network as its parameters. The typical network types are: tcp
, tcp4
, tcp6
, unix
, or unix packet
. If the host part of the parameter is left unspecified, then the function listens on all available unicast and anycast IP addresses of the local system. Once the server is created, it waits and listens to the port for any incoming communication from any client. As we have seen in the client code above, any messages are sent to - or received from - the client using Write
and Read
functions, respectively. Read: Tools to Improve Your Golang Code Quality
Running the Go Server and Client Code Examples
To run the above code, simply run them separately (from two different terminals) using the following commands:
$ go build
$ go run main.go
Final Thoughts on Socket and Network Programming in Go
As we can see, creating client/server communication using sockets in Go is actually quite simple. The standard APIs are grouped in the net
package. This package also includes APIs for low-level network access. Note that, here, we have used connection-oriented transmission represented by TCP packets. There is another type of client/server interaction that is connectionless, using UDP packets.
Opinions expressed by DZone contributors are their own.
Comments