Site hosted by Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:


About the current section

In the current section the X Server prepares its listening sockets, the socket that wait for new client connections. We described sockets in sections 1.4.1, 1.4.2 and 1.4.3 as the interprocess communication mechanism for the connection of the X client-server processes locally or via the tcp/ip network protocol.

The next figure illustrates the X client and X server sockets as the interface used for the client-server connection:

The following image shows a typical Unix X server that uses two socket families for the client connections. The first is a tcp/ip socket, for instance the combination of the IP address and the port number 6000. The second is a Unix Domain socket that uses for instance the file /tmp/.X11-unix/X0.

The role of CreateWellKnownSockets() is to create those sockets and if we focus on the tcp/ip socket in the previous example we see by examining the CreateWellKnownSockets() code that it prepares the socket and makes it listening for client connections by using the socket(), bind() and listen() calls of the socket API. As we read in section 1.4.1 the first call creates a socket, the second binds an address (for the previous example IP, port number 6000) and the third waits for nre client connections. The following figure (from section 1.4.1) highlights the previous calls in a typical client-server connection:

For instance the TCP/IP socket {IP, port 6000} listens for new X Client connections. All new tcp/ip connections for this X Server use the previous socket. The distinction of the client connections is based on the other half of the connection, the socket of the X client. For instance two active connections at the same time would be:

Connection 1: {, 6000,, 21736}
Connection 2: {, 6000,, 12245}

Where and 21736 is the IP, port pair of the first X client and, 12245 the IP, port pair of the second client.

The following four images show the process, the X Server follows, to accept a client connection with the tcp/ip sockets:

The listening socket, the one created by CreateWellKnownSockets(), possibly among other sockets as well, is given in the next figure as 'socket for connection requests'. This socket accepts the client connection and another one is instantly created to handle this connection. This socket is given in the figure as 'socket for individual connection' and as we notice in the figure's example there is already on socket that handles a client connection:

In the next figure a new X client (IP, port number 21736) asks for a connection with the X Server.

The socket responsible for connection requests (IP, port number 6000 for all X Server connection of the current X Server) passes this specific client to the new server socket, prepared right away to handle the client:

The connection is established:

Certainly this scheme is not the only choice for a server, however is the case in the X Server. For more info on this options (threads, concurrent, iterative, etc server see the 'Internetworking with TCP/IP Client-Server Programming and Applications, Linux POSIX Sockets Version', book by D. Comer and D. Stevens).

CreateWellKnownSockets() calls _XSERVTransMakeAllCOTSServerListeners() as:

    if ((_XSERVTransMakeAllCOTSServerListeners (port, &partial,
	&ListenTransCount, &ListenTransConns) >= 0) &&
	(ListenTransCount >= 1))


_XSERVTransMakeAllCOTSServerListeners() is implemented as TRANS(MakeAllCOTSServerListeners). This routine iterates for each transport interface supported by the server. The number of transport interfaces is indicated by NUMTRANS, which is derived in the same file (Xtrans.c) from the following #define:

#define NUMTRANS	(sizeof(Xtransports)/sizeof(Xtransport_table))

Xtransports[] is given also in the same file:

Xtransport_table Xtransports[] = {
#if defined(STREAMSCONN)
#endif /* STREAMSCONN */
#if defined(TCPCONN)
#if defined(IPv6) && defined(AF_INET6)
#endif /* IPv6 */
#endif /* TCPCONN */
#if defined(DNETCONN)
#endif /* DNETCONN */
#if defined(UNIXCONN)
#if !defined(LOCALCONN)
    { &TRANS(SocketLocalFuncs),	TRANS_SOCKET_LOCAL_INDEX },
#endif /* !LOCALCONN */
#endif /* UNIXCONN */
#if defined(OS2PIPECONN)
#endif /* OS2PIPECONN */
#if defined(LOCALCONN)
#ifndef sun
#endif /* sun */
#ifdef SVR4
#ifndef sun
#if !defined(__SCO__) && !defined(__UNIXWARE__)
#endif /* sun */
#endif /* LOCALCONN */

Linux, for instance, defines the DUNIXCONN and DTCPCONN connection flags. When the X Server is compiled with those flags the UNIXCONN and TCPCONN are defined respectively (see this link). As seen in configuration Imake file the UNIXCONN and TCPCONN flags are used. Those flags are used at build time to determine the X server's transport interfaces. See the INSTALL text for more details.

# define ConnectionFlags	-DUNIXCONN -DTCPCONN

As seen in the Xtransports[] table in that case the number of transports, NUMTRANS, becomes five (at most), described with the following sets of functions:

TRANS(SocketTCPFuncs) for instance is found in Xtranssock.c:

Xtransport	TRANS(SocketTCPFuncs) = {
	/* Socket Interface */
#endif /* TRANS_CLIENT */
#endif /* TRANS_SERVER */
#endif /* TRANS_CLIENT */
#endif /* TRANS_SERVER */
	NULL,		       			/* ResetListener */
#endif /* TRANS_SERVER */
#endif /* TRANS_CLIENT */

Xtransport is a big struct defined in Xtransint.h

Following the source code at TRANS(MakeAllCOTSServerListeners) we have for each transport interface a
TRANS(OpenCOTSServer) call as:

	if ((ciptr = TRANS(OpenCOTSServer(buffer))) == NULL)

where at buffer was previously copied the TransName and the port:

	sprintf(buffer,"%s/:%s", trans->TransName, port ? port : "");

Notice that the host part is missing from the protocol, host, port triplet that make the address. At TRANS(ParseAddress) latter we see that in that case TRANS(GetHostname), which is a form of a gethostname() sockets call resolves the host.

    if (_host_len == 0)
	TRANS(GetHostname) (hostnamebuf, sizeof (hostnamebuf));
	_host = hostnamebuf;

TRANS(OpenCOTSServer) is implemented in the same file as:

TRANS(OpenCOTSServer) (char *address)

    PRMSG (2,"OpenCOTSServer(%s)\n", address, 0, 0);
    return TRANS(Open) (XTRANS_OPEN_COTS_SERVER, address);

This calls the TRANS(Open), with arguments:

and returns a filled XtransConnInfo (connection info) struct.

More specifically in TRANS(Open) the address is parsed, that is protocol, host, and port parts are obtained from the address by calling TRANS(ParseAddress), as:

TRANS(ParseAddress) (char *address, char **protocol, char **host, char **port)

From the protocol the transport type is derived by calling:

   if ((thistrans = TRANS(SelectTransport) (protocol)) == NULL)

The transport name is compared with the protocol and if a match is found the specific transport is selected. For instance if protocol is "tcp" the transport that are selected becomes also tcp, the one that includes TRANS(SocketTCPFuncs). Since the first argument of TRANS(Open), type, is XTRANS_OPEN_COTS_SERVER, the following instruction is executed next:

	ciptr = thistrans->OpenCOTSServer(thistrans, protocol, host, port);
#endif /* TRANS_SERVER */

In the case of TRANS(SocketTCPFuncs) OpenCOTSServer() becomes TRANS(SocketOpenCOTSServer). This function mainly calls:

The latter is resolved to a socket() call.

Following the source code at TRANS(MakeAllCOTSServerListeners) we have a TRANS(CreateListener) call.
TRANS(CreateListener) is implemented in Xtrans.c as:

TRANS(CreateListener) (XtransConnInfo ciptr, char *port, unsigned int flags)

    return ciptr->transptr->CreateListener (ciptr, port, flags);

Taken as example the TRANS(SocketTCPFuncs) the TRANS(CreateListener) becomes
TRANS(SocketINETCreateListener), which:

The latter is resolved to a bind() call of the socket API and then the fd of the current socket is passed to a listen() socket call. Therefore the socket opened previously with socket() binds its address (IP, port number in case of tcp/ip) with bind() and then waits for new client connections with listen().

What is interesting here is the way it handles port. Port is a string that contains the ascii value of the display, e.g. 0. From CreateWellKnownSockets() there was the following assignment:

    sprintf (port, "%d", atoi (display));

display was set to "0" at main.c

The port in TRANS(SocketINETCreateListener) is converted to numeric form and is added to X_TCP_PORT, which is defined in Xtransint.h as:

#define X_TCP_PORT	6000

Therefore the listening port for the first display (the first X Server process in a system) becomes 6000, for the second display (1) becomes 6001, for the third (2) becomes 6002, etc. As indicated before the default value for the display is "0", however when the X Server starts as seen in the X Server man page the display is changed by the ":displaynumber" argument. For a detailed description see subsection "The X Server's socket address - TCP/IP transport" in section 1.4.2

TRANS(MakeAllCOTSServerListeners) ends here and we are back to CreateWellKnownSockets().