Socket programming in Go — Syscalls

Valentin Omnès
4 min readFeb 19, 2020

This article is about socket programming in Go on a Unix system. The implementation is pretty similar to the one in C or C++.

Socket programming syscalls organisations

After few years of experience in developing web servers using high level language API such as net/http for Go, I wanted to improve my web development skills. So, I have decided to improve them by developing a web server using only syscalls in Go.

Introduction

I will explain what I have learned and the code that allows to create a basic TCP Server that can be the base for a web server.

You can find the source code at the end of the page or here.

Create a socket

func Socket(domain, type, protocol int) (fd int, err error)

Socket creates a socket and returns the file descriptor corresponding to the created socket.

Domaine :

  • AF_INET for the IPv4 address family
  • AF_INET6 for the IPv6 address family
  • AF_UNIX or AF_LOCAL for local communication

Type :

  • SOCK_STREAM is the stream connection socket for bidirectional, reliable, sequenced and connection-based messages like TCP.
  • SOCK_DGRAM is the datagram connection less socket for low latency, unreliable messages like UDP or UNIX.
  • SOCK_RAW is the socket for raw network protocol access can be used for new communication protocols.

Protocol :

  • IPPROTO_IP is for protocol level IP

More options and the documentation of socket() can be found here.

Bind

func Bind(fd int, sa Sockaddr) (err error)

Bind links a socket file descriptor to a socket address.

  • Sockaddr is an interface{} in the Go unix library so you can use any of the Sockaddr structure of the library such as SockaddrInet4 (AF_INET type) or SockaddrInet6 (AF_INET6 type).

More options and the documentation of bind() can be found here.

Listen

func Listen(sockfd int, backlog int) (err error)

Listen set a socket as a passive socket that is to say ready to accept incoming connection requests.

  • Sockfd is our socket server file descriptor. This fd can only be of
    type SOCK_STREAM or SOCK_SEQPACKET.
  • Backlog is the maximum size of the queue of the pending connection for sockfd. Pending connection is qualified as a completely established socket waiting to be accepted. The maximum value of Backlog is SOMAXCONN (4096) and the default value is 128.

More options and the documentation of listen() can be found here.

Select

func Select(int nfds, readFdSet *FdSet, writeFdSet *FdSet, exceptFdSet *FdSet, timeval *Timeval) error

Select disables the file descriptors in a given FdSet copy that are not yet available for reading or writing.

  • nfds is the the highest-numbered file descriptor in any of the three sets (from 0 to nfds). The maximum value is FD_SETSIZE.
  • readFdSet, writeFdSet and exceptFdSet are the file descriptors set to see if they are available for reading, writing or other.
  • timeval specifies the maximum time to wait. If you pass a null pointer for this argument, it means to block indefinitely until one of the file descriptors is ready. Set Timeval structure at zero if you want to find out which descriptors are ready without waiting if none are ready.

More options and the documentation of select() can be found here.

FdSet

type unix.FdSet struct {
Bits [32]int32 // FD_SETSIZE = 1024 = 32x32
}
func FDZero(p *unix.FdSet)
func FDSet(fd int, p *unix.FdSet)
func FDClr(fd int, p *unix.FdSet)
func FDIsSet(fd int, p *unix.FdSet) bool

FdSet is the structure that store a file descriptor (fd) set.

  • FDZero clears the set.
  • FDSet add a fd in the set.
  • FDClr delete a fd from the set.
  • FDIsSet checks and return true if the fd is part of the set.

The documentation of FdSet can be found here.

Accept

func Accept(fd int) (nfd int, sa Sockaddr, err error)

Accept extracts the first connection request on the queue of pending connections on a listening socket. It creates a new connected socket, and returns a new file descriptor referring to that socket and the address of this socket.

DO NOT FORGET to use Close() to close the file descriptor when you are finished with it.

More options and the documentation of accept() can be found here.

Receive message

func Recvfrom(fd int, buf []byte, flags int) (n int, from Sockaddr, err error)

Recvfrom open the socket fd, reads and store the data in buf.

More options and the documentation of recvfrom() can be found here.

Send message

func Sendmsg(sockfd int, buf, oob []byte, dest_addr Sockaddr, flags int) error

Sendmsg sends a message to another socket.

  • sockfd is the destinataire file descriptor
  • buf is the content of the message
  • oob is the Out Of Band data
  • dest_addr is the receiver socket address
  • flags is the bitwise OR of zero or more of the following flags : MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_MORE, MSG_NOSIGNAL, MSG_OOB

More options and the documentation of sendmsg() can be found here.

See more

You can find more examples and explanations on the following links:

--

--