Hands-on Projects for the Linux Graphics Subsystem
|
About the current sectionIn 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 192.168.1.100 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 192.168.1.100, 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 192.168.1.100, 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: {192.168.1.100, 6000, 192.168.1.101, 21736} Where 192.168.1.101 and 21736 is the IP, port pair of the first X client and 192.168.1.102, 12245 the IP, port pair of the second client.
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:
static Xtransport_table Xtransports[] = { #if defined(STREAMSCONN) { &TRANS(TLITCPFuncs), TRANS_TLI_TCP_INDEX }, { &TRANS(TLIINETFuncs), TRANS_TLI_INET_INDEX }, { &TRANS(TLITLIFuncs), TRANS_TLI_TLI_INDEX }, #endif /* STREAMSCONN */ #if defined(TCPCONN) { &TRANS(SocketTCPFuncs), TRANS_SOCKET_TCP_INDEX }, #if defined(IPv6) && defined(AF_INET6) { &TRANS(SocketINET6Funcs), TRANS_SOCKET_INET6_INDEX }, #endif /* IPv6 */ { &TRANS(SocketINETFuncs), TRANS_SOCKET_INET_INDEX }, #endif /* TCPCONN */ #if defined(DNETCONN) { &TRANS(DNETFuncs), TRANS_DNET_INDEX }, #endif /* DNETCONN */ #if defined(UNIXCONN) #if !defined(LOCALCONN) { &TRANS(SocketLocalFuncs), TRANS_SOCKET_LOCAL_INDEX }, #endif /* !LOCALCONN */ { &TRANS(SocketUNIXFuncs), TRANS_SOCKET_UNIX_INDEX }, #endif /* UNIXCONN */ #if defined(OS2PIPECONN) { &TRANS(OS2LocalFuncs), TRANS_LOCAL_LOCAL_INDEX }, #endif /* OS2PIPECONN */ #if defined(LOCALCONN) { &TRANS(LocalFuncs), TRANS_LOCAL_LOCAL_INDEX }, #ifndef sun { &TRANS(PTSFuncs), TRANS_LOCAL_PTS_INDEX }, #endif /* sun */ #ifdef SVR4 { &TRANS(NAMEDFuncs), TRANS_LOCAL_NAMED_INDEX }, #endif #ifndef sun #if !defined(__SCO__) && !defined(__UNIXWARE__) { &TRANS(ISCFuncs), TRANS_LOCAL_ISC_INDEX }, #endif { &TRANS(SCOFuncs), TRANS_LOCAL_SCO_INDEX }, #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 linux.cf 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 */ "tcp", TRANS_ALIAS, #ifdef TRANS_CLIENT TRANS(SocketOpenCOTSClient), #endif /* TRANS_CLIENT */ #ifdef TRANS_SERVER tcp_nolisten, TRANS(SocketOpenCOTSServer), #endif /* TRANS_SERVER */ #ifdef TRANS_CLIENT TRANS(SocketOpenCLTSClient), #endif /* TRANS_CLIENT */ #ifdef TRANS_SERVER TRANS(SocketOpenCLTSServer), #endif /* TRANS_SERVER */ #ifdef TRANS_REOPEN TRANS(SocketReopenCOTSServer), TRANS(SocketReopenCLTSServer), #endif TRANS(SocketSetOption), #ifdef TRANS_SERVER TRANS(SocketINETCreateListener), NULL, /* ResetListener */ TRANS(SocketINETAccept), #endif /* TRANS_SERVER */ #ifdef TRANS_CLIENT TRANS(SocketINETConnect), #endif /* TRANS_CLIENT */ TRANS(SocketBytesReadable), TRANS(SocketRead), TRANS(SocketWrite), TRANS(SocketReadv), TRANS(SocketWritev), TRANS(SocketDisconnect), TRANS(SocketINETClose), TRANS(SocketINETClose), }; |
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:
XtransConnInfo 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:
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:
#ifdef TRANS_SERVER 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:
int 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:
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().