Socket Code Examples
For the Raft project you will need to open TCP sockets. This requires some very specific code, especially on the server side. This page contains simple examples of code for both the client and server sides; the examples are based on IPv4. This code has not been tested (or even compiled!), so it may not be perfect. If you find bugs, please report them. This code shows places where error handling code is needed, but it doesn't include actual code to handle errors.
More detailed information about the system calls and functions is
available on the Web. For example, to get full details on the
getaddrinfo
function, Google "man getaddrinfo". You can also invoke
man getaddrinfo
program directly on a Linux machine, such as
the myth cluster. In some cases it
may help to specify the man
section number: 2 for system calls
("man 2 socket") and 3 for library functions ("man 3 getaddrinfo").
Client side
Here is an example of a method that connects to a TCP server that is listening on a given port on a given machine.
int openSocket(char *server, int port)
{
// First, must find an IP address for the given server name.
// getaddrinfo is now the official way to do this.
struct addrinfo hints;
struct addrinfo *matchingAddrs;
struct sockaddr dest;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
int status = getaddrinfo(host, NULL, &hints, &matchingAddrs);
if (status != 0) {
// Handle error
}
memcpy(dest, matchingAddrs->ai_addr, matchingAddrs->ai_addrlen);
freeaddrinfo(matchingAddrs);
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
// Handle error
}
if (connect(fd, &dest, sizeof(dest)) == -1) {
// Handle error
}
// This code disables the "Nagle Algorithm", which really shouldn't
// be enabled by default (it makes things slower).
int flag = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
// Handle error
}
return fd;
}
Server side
On the server side, there are two pieces of code involved. The first piece opens the socket and sets it up to accept new incoming connections on a given port:
int openListener(int port)
{
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
if (listenFd == -1) {
// Handle error
}
// This piece of code allows the listening socket to be reopened
// immediately if your server crashes (or is killed) and is then
// restarted. Without this code, the socket will not be reuable
// for 30 seconds after the last use ended.
int optionValue = 1;
if (setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &optionValue,
sizeof(optionValue)) != 0) {
// Handle error
}
// This code configures the socket so that accept will return
// immediately if there are no incoming connections to accept. You'll
// need this code if you are using sockets in an asynchronous way
// (e.g. with epoll or select). If the server is using the listening
// socket synchronously (a thread blocks waiting for connections),
// then you shouldn't use this code.
if (fcntl(listenFd, F_SETFL, O_NONBLOCK) != 0) {
// Handle error
}
// This code associates the socket with a particular port number;
// clients will specify this port number when they connect.
sockaddr_in addr;
addr.in4.sin_family = AF_INET;
addr.in4.sin_port = htons(port);
addr.in4.sin_addr.s_addr = INADDR_ANY;
if (bind(listenFd, (struct sockaddr *) addr, sizeof(addr)) == -1) {
// Handle error.
}
// This call tells the system that this socket will be used to
// accept new incoming connections.
if (listen(listenFd, 1000) == -1) {
// Handle error.
}
The second piece of code is invoked when the listening socket becomes
"readable": it accepts the connection, producing another socket that
can be used to communicate with a specific client. If your server is
synchronous (a thread will block waiting for incoming connections, then
this code can be invoked without waiting for the listening socket to
become readable. Note: you can use accept4
instead of accept
if
you'd like to specify options such as O_NONBLOCK.
int acceptConnection(int listenFd, struct sockaddr *addr)
{
int fd = accept(listenFd, &addr, sizeof(addr));
if (fd < 0) {
// Handle error
}
// Disable the Nagle algoritm (as for the client side).
int flag = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) <!= 0) {
// Handle error
}
return fd;
}