Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

Sockets

Sockets is the most common interprocess communication (IPC) method between the X Client and the X server processes. It provides the Application Programming Interface (API) for communication in the TCP/IP domain and also locally only in the UNIX domain. There are several other APIs described in the X Transport Interface, for instance TLI. Other options for IPC between for the X client-server, require X Window system extensions, for instance the MIT Shared Memory Extension or MIT-SHM (see this wiki).

This section aims to underline the most significant parts of the socket communication. For more details on the subject there are several web tutorials and books available.

TCP/IP Sockets

In the Internet domain the socket of each endpoint has a two-part name: a host (an IP address) and a port number. For instance IP 192.168.1.100, port 6000. The connection between a server (IP 192.168.1.100, port 6000) and a client (IP 192.168.1.101, port 41212) would be identified by the socket pair: 192.168.1.100:6000, 192.168.1.101:41212.

Sockets are created with the socket call:

int socket(int domain, int type, int protocol);
domain is AF_INET for the Internet domain protocols

type is usual one of the following:

SOCK_STREAM for the TCP protocol
SOCK_DGRAM for the UDP protocol
SOCK_RAW for the IP protocol
and protocol is set to 0.

A typical socket() call would be:

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
   ...
Read also the socket man page.

Another call setsockopt() is used at this stage to set some socket options. See its man page.

It is the bind socket call that associates a local source port and a network interface address:

bind(int socket, struct sockaddr_in *address, int address_length);
where the first argument is the socket handle, returned from the socket function call. The second argument is a sockaddr_in structure:
struct sockaddr_in {
   u_char   sin_len;              /* length of structure (16) */
   u_char   sin_family;           /* AF_INET */
   u_short  sin_port;             /* 16 bit TCP or UDP port number */
   struct   in_addr sin_addr;     /* 32-bit IPv4 address */
   char  sin_zero[8];             /* unused */
};
A typical bind() call would be:
struct sockaddr_in server;

bzero((char *) &server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(SERVER_PORT);
if (bind(sock, (struct sockaddr *) &server, sizeof(server)))
   ...
Read also the bind man page.

With the listen() call of the sockets API the socket passed as the first argument starts accepting connections and limits the number of outstanding connections in the socket's listen queue to the value specified by the second argument. Read aslo the listen man page.

int listen(int sd, int backlog);
With the accept() call the socket passed as the first argument extracts the first connection request on queue and blocks if the queue is empty. The second argument points to the address of the peer socket and the third argument on return contains the size of the peer address. For more info read the man page.
int accept(int sd, struct sockaddr *addr, int *addrlen);
A socket connects to another socket, opening a communication channel, with connect(). As we read in the man page:
The connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by addr. The addrlen argument specifies the size of addr. The format of the address in addr is determined by the address space of the socket sockfd;
int connect(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);
The two sockets of each side of the connection communicates with the other with the read() and write() calls:
ssize_t read(int sd, void* buf, size_t nbytes);
The read() call reads at most nbytes bytes into buf from sd.
ssize_t write(int sd, const void *buf, size_t nbytes);
write() attempts to write nbytes from buf to sd.

There are also similar calls to read() and write(): recv() and send() respectively:

int recv(int s, void *buf, size_t len, int flags);
and
int send(int s, const void *msg, size_t len, int flags);
The only difference between the 'read, write' and the 'recv, send' pairs are the flags that are used as the fourth argument. For more info on the flags read the recv and the send man pages.

Another alternative option would be also the readv(), writev() pair. The readv() function reads data from a file or a socket with descriptor fs and stores it in a set of buffers and the writev() function writes data to a file or socket with descriptor fs from a set of buffers. Notice that the previous routines are implemented on stream sockets. For datagram sockets see socket().

With the close() call the socket with the descriptor sd closes.

int close(int sd);

UNIX Domain Sockets

UNIX domain sockets are used to communicate with processes running on the same machine. Although Internet domain sockets can be used locally Unix domain sockets are more efficient, since they do not have the protocol overhead (checksums, byte orders, etc).

In the Unix domain, sockets are named using files (e.g., /tmp/mysocket). This is the sun_path member of the sockaddr_un structure:

   struct sockaddr_un {
        sa_family_t sun_family;      /* AF_UNIX */
        char        sun_path[108];   /* pathname */
   };
When we bind an address to a UNIX domain socket, the system creates a file of type S_IFSOCK with the same name with sun_path[]. If the file already exists the bind call will fail. When the socket closes, this file is not automatically removed, and unlink should be used before the application exits.