Unix Socket Essentials

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 5

Unix Socket Essentials

In a shell, there are 3 socket fds used when using sockets. 2 by the server: 1 to listen and establish connections (sersock), and 1 to
perform communication (consock). Then 1 on the client (clisock) which is used for communication.
See necessary includes on the bottom

Summary:
This is a very common form of interprocess communication that can be easily
parallelized on multiple threads to communicate with multiple clients, or just as easily utilized to
force clients to wait until the server is ready for another client. Like pipes, data is transferred as
individual bytes, usually utilizing a buffer to send large chunks at a time.
It is important when developing your application that you consider the communication
flow of the client-server connection as you approach your problem. For example, when the
connection is established, should the server write data to the client and then wait for a response
or should the client write data first?
For one of my projects, I had the client write data to the server. The type of
communication or request the client wanted to make could be 1 of 6 different things. In order for
the server to identify which request it was going to be, I had the client prepend their message
with one of these characters: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, or ‘5’. Then, the server would read exactly one
byte and use a switch to determine what action should be taken. This is a very informal
“protocol”, feel free to experiment and establish your own paradigm.
It is very important that the client sends the character ‘0’ instead of the integer 0,
because using string functions on the buffer would interpret the integer 0 as the null character
‘\0’ and produce undesirable results.

Note that this document is purely a supplement to the C/Linux manual pages for socket(), bind(),
listen(), accept(), read(), and write().
Steps to establishing a connection on the server:

1. Setup the address struct (this is used to identify where the socket will be placed in your
filesystem)
a. .sun_family (​s​ocketaddress_​un​ix) = ​AF_UNIX
i. This is mandatory and the only option for .sun_family
b. .sun_path (​so ​ cketaddress_​un​ix) = the (absolute or relative) path on your
filesystem to the socket you are going to create, this will be the same for client
and server
2. socket()
a. Creates a socket (returns a fd so you can use ​read()​, ​write()​, or other
system calls that use fd’s)
b. NOTE: the socket we are making here will not be used to perform communication
- it will only be used to establish a connection with a client(s)
3. bind()
a. Attempts to create a socket file at the path specified, will error if the file already
exists! (calling unlink(​sock_path​) will remove the file if it exists)
b. You will use ​sock_path​ on the client to refer to the same socket the server has
created
4. listen()
a. This tells the socket to listen for incoming connections, the 10 specifies how
many clients can be waiting to connect at a time
​struct sockaddr_un addr;
int sersock;
int len = sizeof(addr);
memset(&addr, 0, len); // clear out the field

char sock_path[60];

strcpy(​sock_path​, getenv("HOME"));
strcat(​sock_path​, "/Desktop/.socket");

addr.sun_family = ​AF_UNIX​; // sun_family is always AF_UNIX


strncpy(addr.sun_path, ​sock_path​, sizeof(addr.sun_path)-1); // copy the
path
sersock = socket(​AF_UNIX​, SOCK_STREAM, 0);
if (sersock < 0) { // get a fd for the socket
perror("socket error");
return -1;
}
if (bind(sersock, (struct sockaddr*) &addr, sizeof(addr)) < 0) { // create socket file
perror("bind error");
return -1;
}
if (listen(sersock, 10) < 0) { // start enqueueing incoming connections
perror("listen error");
return -1;
}
return sersock;
5. accept()
a. This will block until a client calls ​connect()​ to this socket, returns the new fd to
use to communicate
b. Since both ​accept()​ [server] and ​connect()​ [client] block, this will make
processing one client at a time very easy since the connection won’t end (and
new clients won’t start) until you call ​close()
int consock = accept(sersock, NULL, NULL);
if (consock < 0) {
perror("Error accepting");
return -1;
}
read(consock, buffer, BUF_SIZE);
write(consock, buffer, strlen(buffer));

close(consock)

6. read()​ & ​write()


a. These are used on both the server and client to send and receive data, note that
read()​ will block until there has been the amount of bytes specified (1 byte, in
the example above) written into the socket
b. NOTE: these functions send/receive bytes/chars, not ints or structs, so functions
like ​sscanf()​, ​strtol()​, ​sprintf()​ will be very useful for sending info
7. When either side calls ​close(consock/clisock)​, the connection ends
a. I believe ​write()​ on a socket that either end has closed will block indefinitely,
so be sure that you have equal reads/writes on your client and server
Steps to connect to the server from the client:
1. Setup the address struct (this is used to identify where the socket will be placed in your
filesystem)
a. .sun_family (​s​ocketaddress_​un​ix) = ​AF_UNIX
i. This is mandatory and the only option for .sun_family
b. .sun_path (​so​ cketaddress_​un​ix) = the (absolute or relative) path on your
filesystem to the socket you are going to create, this will be the same for client
and server
2. socket()
a. Creates a socket fd to communicate with a server
3. connect()
a. Blocks until the server calls ​accept()
4. read()​ and ​write()

struct sockaddr_un addr;


int clisock;
char sock_path[60];

strcpy(​sock_path​, getenv("HOME"));
strcat(​sock_path​, "/Desktop/.socket");

memset(&addr, 0, sizeof(addr));
addr.sun_family = ​AF_UNIX​;
strncpy(addr.sun_path, ​sock_path​, sizeof(addr.sun_path)-1);

clisock = socket(​AF_UNIX​, SOCK_STREAM, 0);


if (connect(clisock, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
perror("Error connecting");
return 0;
}
// read and write here
write(clisock, buf, strlen(buf));
read(clisock, buf, BUF_SIZE);

close(clisock)
Includes I used
The bold ones are for sure needed for sockets, idk about the others
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysinfo.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

You might also like