A 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.
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.
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:
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. We shall see more of these in future Go network programming tutorials.
Some network application developers say that the lower application layers are all about socket programming. This may not be true for all cases, but many modern web applications do indeed use sockets to their advantage. Have you ever wondered how browsers communicate with web servers when you are surfing the internet? Or How MSN connects you and your friends together in a chatroom, relaying each message in real-time? Many services like these use sockets to transfer data. As you can see, sockets occupy an important position in network programming today, and we're going to learn about using sockets in Go in this section.
Sockets originate from Unix, and given the basic "everything is a file" philosophy of Unix, everything can be operated on with "open -> write/read -> close". Sockets are one implementation of this philosophy. Sockets have a function call for opening a socket just like you would open a file. This returns an int descriptor of the socket which can then be used for operations like creating connections, transferring data, etc.
Two types of sockets that are commonly used are stream sockets (SOCK_STREAM) and datagram sockets (SOCK_DGRAM). Stream sockets are connection-oriented like TCP, while datagram sockets do not establish connections, like UDP.
Before we understand how sockets communicate with one another, we need to figure out how to make sure that every socket is unique, otherwise establishing a reliable communication channel is already out of the question. We can give every process a unique PID which serves our purpose locally, however that's not able to work over a network. Fortunately, TCP/IP helps us solve this problem. The IP addresses of the network layer are unique in a network of hosts, and "protocol + port" is also unique among host applications. So, we can use these principles to make sockets which are unique.
Applications that are based on TCP/IP all use socket APIs in their code in one way or another. Given that networked applications are becoming more and more prevalent in the modern day, it's no wonder some developers are saying that "everything is about sockets".
The only difference between a UDP socket and a TCP socket is the processing method for dealing with multiple requests on server side. This arises from the fact that UDP does not have a function like Accept. All of the other functions have UDP counterparts; just replace TCP with UDP in the functions mentioned above.
Through describing and coding some simple programs using TCP and UDP sockets, we can see that Go provides excellent support for socket programming, and that they are fun and easy to use. Go also provides many functions for building high performance socket applications.
Concurrency, the biggest advantage to our use-case, socket programming, given it's asynchronous design. Along with the fact that GoLang is an extremely modern high-level language making development of established applications quick and efficient. Plus I just love the simplistic elegance of the language! :) Don't know GoLang? Well, this is a great low-level project to see some basic concepts at play, especially goroutines, GoLang's concurrency feature.
If you're developing on Linux you can test your new socket server application without writing the client application first. Start your socket server with, go run main.go, from the server sub-folder within your shell. Now run, nc localhost 8080, replacing the host and port number if needed. There are applications to run the nc (netcat) command on Windows, however, I don't know the best of options here.
Run your socket server with, go run main.go, from the server project sub-folder on your shell or command-line. Now open another shell or command-line session within the client folder and run, go run main.go here as well to start the client application. Any text entered into the client will be sent to the server, displayed with a timestamp, and relayed back, being displayed again.
This tests the basic functionality of the socket server-client interface and can be extended upon by expanding the server.go handleConnection function. Each client is handled concurrently by the server and as many clients may connect as your system can handle running, you may also connect through other local systems or over the internet if you expose the port to traffic.
Well now you have stand-alone socket server and client interfaces, in pure standard-library GoLang! So what's next you ask? The sky's the limit, write an IRC-style chat client or a streaming application, just take it one step at a time, and that's one reason we started with a new git repository to manage all those changes you'll start adding.
When it comes to inter-process communication (IPC) between processes on the sameLinux host, there are multiple options: FIFOs, pipes, shared memory, sockets andso on. One of the most interesting options is Unix Domain Sockets that combinethe convenient API of sockets with the higher performance of the othersingle-host methods.
IPC with UDS looks very similar to IPC with regular TCP sockets using theloop-back interface (localhost or 127.0.0.1), but there is a keydifference: performance. While the TCP loop-back interface can skip some of thecomplexities of the full TCP/IP network stack, it retains many others (ACKs, TCPflow control, and so on). These complexities are designed for reliablecross-machine communication, but on a single host they're an unnecessary burden.This post will explore some of the performance advantages of UDS.
There are some additional differences. For example, since UDS use paths in thefilesystem as their addresses, we can use directory and file permissions tocontrol access to sockets, simplifying authentication. I won't list all thedifferences here; for more information feel free to check out the Wikipedia linkand additional resources like Beej's UNIX IPC guide.
The big disadvantage of UDS compared to TCP sockets is the single-hostrestriction, of course. For code written to use TCP sockets we only have tochange the address from local to remote and everything keeps working. That said,the performance advantages of UDS are significant enough, and the API is similarenough to TCP sockets that it's quite possible to write code that supports both(UDS on a single host, TCP for remote IPC) with very little difficulty.
When servers shut down, the file representing the socket can remain in thefile system unless the server did orderly cleanup after itself. If we re-runanother server with the same socket path, we may get the error:
We can see that writing UDS servers and clients is very similar to writingregular socket servers and clients. The only difference is having to pass"unix" as the network parameter of net.Listen and net.Dial; therest of the code remains the same. Obviously, this makes it very easy towrite generic server and client code that's independent of the actual kind ofsocket it's using.
Note: benchmarking is hard, so please take these results with a grain of salt.There's some more information on benchmarking different socket typeson the Redis benchmarks page and inthis paper, aswell as many other resources online. I also found this set of benchmarks (written in C) instructive.
The throughput/bandwidth benchmarkis conceptually simpler than the latency benchmark. The server listens on asocket and grabs all the data it can get (and discards it). The client sendslarge packets (hundreds of KB or more) and measures how long each packet takesto send; the send is done synchronously and the client expects the whole messageto be sent in a single call, so it's a good approximation of bandwidth if thepacket size is large enough. 2b1af7f3a8