Sockets: The Connectionless Paradigm

Sockets The Connectionless Paradigm

The sequence of events for connectionless clientserver communication has some common elements with connection-oriented communication. Both the client and server still generate sockets using the socket call. The server and, most often, the client, bind their sockets to an address. However, in the connection-oriented sequence, only the server performs this step. The client process does not use connect to establish a connection with the server. Instead, both the server and client send and receive datagram packets to and from a specified address. The server process sends its packets to the client address, and the client sends its packets to the server address. These events are shown in Figure 10.14.

Figure 10.14. A connectionless clientserver communication sequence.

graphics/10fig14.gif

In this example, we have used the sendto and recvfrom system calls for data exchange. The sendto call in the client is somewhat similar in function to the connect write sequence we saw in the initial Internet domain connection-oriented example. Similarly, the recvfrom call is analogous to the accept read sequence we used for the server in the same example.

The sendto call is one of several alternate ways to write data to a socket. Table 10.19 provides a summary of three calls that can write data to a socket descriptor.

Table 10.19. Summary of the send , sendto , and sendmsg System Calls.

Include File(s)


 

Manual Section

2

Summary

int send (int s, const void *msg,size_t len,
 int flags);
int sendto (int s,const void *msg,
 size_t len, int flags,
 const struct sockaddr *to, socklen_t
 tolen);
int sendmsg(int s, const struct msghdr *msg,
 int flags);

Return

Success

Failure

Sets errno

Number of bytes sent.

-1

Yes

The send call, since it contains no destination addressing information, can only be used with connected (SOCK_STREAM) sockets. The sendto and sendmsg calls can be used with either socket type but are most commonly used with datagram sockets (SOCK_DGRAM). The send and sendto calls dispatch a sequence of bytes. The sendmsg call is used to transmit data that resides in noncontiguous ( scattered ) memory locations (such as in a structure).

In all three calls, the integer argument s is a valid socket descriptor. The *msg argument references the message to be sent. In the sendmsg call, the msg reference is to a structure of type msghdr that contains additional addressing/messaging information. [12] This structure is defined as

[12] As its use is rather complex, we will only mention sendmsg (and its reciprocal recvmsg ) in passing (no pun intended!).

struct msghdr {
 void *msg_name; /* optional address */
 socklen_t msg_namelen; /* size of address */
 struct iovec *msg_iov; /* scatter/gather array */
 size_t msg_iovlen; /* # elements in msg_iov */
 void *msg_control; /* ancillary data, see below */
 socklen_t msg_controllen; /* ancillary data buffer len */
 int msg_flags; /* flags on received message */
};

where the type iovec is

struct iovec {
 void *iov_base; /* Pointer to data. */
 size_t iov_len; /* Length of data. */
 };

The len argument is the length of the message to send. Message size is limited by the underlying protocol. The sendto call contains an additional argument that references the address structure with the information of where to send the message. With sendto this argument is followed by an argument containing the size of the addressing structure. If sendto is used with a connection-oriented socket, these two arguments are ignored. All three calls have an integer-based flag argument. Bitwise OR ing the value 0 with one or more of the defined constants in Table 10.20 generates the flag value.

Table 10.20. Flags for the send , sendto , and sendmsg Calls.

Flag

Meaning

MSG_OOB

Message out of band . At present this flag is valid only for Internet stream-based sockets. Specifying MSG_OOB allows the process to send urgent data. The receiving process can choose to ignore the message.

MSG_DONTROUTE

Bypass routing tables and attempt to send message in one hop. This is often used for diagnostics purposes.

MSG_DONTWAIT

Adopt non-blocking operation for operations that block return EAGAIN.

MSG_NOSIGNAL

On stream-based socket do not send a SIGPIPE error when one end of connection is broken.

MSG_CONFIRM

With SOCK_DGRAM and SOCK_RAW sockets, in Linux 2.3+, notify the link layer of successful reply from the other side.

In some settings when the above calls are used, the network and socket library must be specified. In such settings use the -lnsl and/or -lsocket compiler option to notify the linker. These calls return the number of bytes sent or, in case of error, a -1, setting errno to one of the values found in Table 10.21. Data sent to an unbound socket is discarded.

Table 10.21. send , sendto , and sendmsg Error Messages.

#

Constant

perror Message

Explanation

4

EINTR

Interrupted system call

A signal was received by the process before data was sent.

9

EBADF

Bad file descriptor

The socket reference is invalid.

11

EWOULDBLOCK, EAGAIN

Resource temporarily unavailable

The socket is set to non-blocking, and no connections are pending.

12

ENOMEM

Cannot allocate memory

Insufficient memory to perform operation.

14

EFAULT

Bad address

Argument references location outside user address space.

22

EINVAL

Invalid argument

tolen argument contains an incorrect value.

32

EPIPE

Broken pipe

Local end of a connection-oriented socket is closed.

88

ENOTSOCK

Socket operation on non-socket

The socket argument is a file descriptor, not a socket descriptor.

90

EMSGSIZE

Message too long

Socket type requires message to be sent to be atomic (all sent at once) and the message to send is too long.

105

ENOBUFS

No buffer space available

Output queue is full.

Other than read , there are three system calls comparable to send for receiving data from a socket descriptor. These calls are recv , recvfrom , and recvmsg . Unless otherwise specified (such as with fcntl ), these calls will block if no message has arrived at the socket. Table 10.22 provides a summary of these calls.

Since it contains no sender address information, the recv network call should only be used with connection-oriented (SOCK_STREAM) sockets. The recvfrom and recvmsg calls can be used with connection-oriented or connectionless sockets. Usually, when data is written to a socket with send , sendto , or sendmsg , it is read with the corresponding recv , recvfrom , or recvmsg call.

Table 10.22. Summary of the recv , recvfrom , and recvmsg System Calls.

Include File(s)


 

Manual Section

2

Summary

int recv(int s, void *buf, size_t len, int flags);
int recvfrom(int s, void *buf, size_t len,
 int flags, struct sockaddr *from, socklen_t
 *fromlen);
int recvmsg(int s, struct msghdr *msg, int flags);

Return

Success

Failure

Sets errno

Number of bytes received

-1

Yes

In each call, the integer argument s is a valid socket descriptor. The *buffer argument references the location where the received message will be stored. The user is responsible for allocating the storage space for the received message. As with the sendmsg network call, the *msg argument for recvmsg references a msghdr structure. The len argument is the length of the receive message buffer. Remember that the message size is limited by the underlying protocol and exceedingly long messages may be truncated. The receive calls return the actual number of bytes received. If the *from argument for the recvfrom call is not NULL, it should reference a sockaddr structure containing the address information of the host that sent the message. The *fromlen argument should reference the length of this addressing structure. OR ing the value 0 with one or more of the defined flags shown in Table 10.23 forms the flag argument.

Table 10.23. Flags for the recv , recvfrom , and recvmsg Calls.

Flag

Meaning

MSG_ERRQUEUE

Receive error messages from the error message queue. The details of how to implement error message retrieval is beyond the scope of this text (see the manual page on recv for specifics).

MSG_NOSIGNAL

With a stream socket, do not raise a SIGPIPE error when the other end of the socket disappears.

MSG_OOB

Message out of band. At present, this flag is valid only for Internet stream-based sockets. Specifying MSG_OOB allows the process to read urgent out-of-band data.

MSG_PEEK

Look at the current data but do not consume it. Subsequent read -receive type calls will retrieve the same peeked -at data.

MSG_TRUNC

With datagram socket, return the real length of the message even if it exceeds specified amount.

MSG_WAITALL

Wait until the full request for data has been satisfied.

In cases of error, these calls will return a -1 and set errno to one of the values found in Table 10.24.

Table 10.24. recv , recvfrom , and recvmsg Error Messages

#

Constant

perror Message

Explanation

4

EINTR

Interrupted system call

A signal was received by process before data was received.

9

EBADF

Bad file descriptor

The socket reference is invalid.

11

EAGAIN

Resource temporarily unavailable

  • The socket is set to non-blocking, and no connections are pending.
  • Timer expired before data was received.

14

EFAULT

Bad address

Argument references a location outside user address space.

22

EINVAL

Invalid argument

An argument contains incorrect value.

88

ENOTSOCK

Socket operation on non-socket

The socket argument is a file descriptor, not a socket descriptor.

107

ENOTCONN

Transport endpoint is not connected

A connection-oriented socket has not been connected.

111

ECONNREFUSED

Connection refused

Remote host has refused the connection request.

10.5.1 A UNIX Domain Datagram Socket Example

Our UNIX domain datagram socket example is somewhat similar in function to the stream socket example presented in Section 10.4. In this example, the server creates a datagram socket (SOCK_DGRAM) in the UNIX domain and binds it to an address (file name). The client also creates a datagram socket and binds it to an address (using a different file name , unique to each client process). The client and server use the sendto and recvfrom network calls for communication. The client generates 10 messages (the output of the / usr/ games / fortune utility), which are sent to the server. The server displays the messages that it has received. The code for the server process is shown in Program 10.8.

Program 10.8 The UNIX domain connectionless server .

File : p10.8.cxx
 /*
 SERVER - UNIX domain - connectionless
 */
 #include "local_sock.h"
 +
 void clean_up(int, const char *); // Close socket and remove
 int
 main( ) {
 socklen_t clnt_len; // Length of client address
 10 int orig_sock; // Original socket descriptor
 static struct sockaddr_un
 clnt_adr, // Client address
 serv_adr; // Server address
 static char buf[BUFSIZ]; // Buffer for messages
 + // Generate socket
 if ((orig_sock = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) {
 perror("generate error");
 return 1;
 } // Assign address information
 20 serv_adr.sun_family = AF_UNIX;
 strcpy(serv_adr.sun_path,SERVER_FILE);
 unlink( SERVER_FILE); // Remove old copy if present
 // BIND the address
 if (bind(orig_sock, (struct sockaddr *) &serv_adr,
 + sizeof(serv_adr.sun_family)+strlen(serv_adr.sun_path)) < 0) {
 perror("bind error");
 clean_up(orig_sock, SERVER_FILE);
 return 2;
 } // Process
 30 for (int i = 1; i <= 10; i++) {
 recvfrom(orig_sock, buf, sizeof(buf), 0,
 (struct sockaddr *) &clnt_adr, &clnt_len);
 cout << "S receives " << buf;
 }
 + clean_up(orig_sock, SERVER_FILE);
 return 0;
 }
 void
 clean_up( int sd, const char *the_file ){
 40 close( sd ); // Close socket
 unlink( the_file ); // Remove it
 }

The code for the client process is shown in Program 10.9.

Program 10.9 The UNIX domain connectionless client .

File : p10.9.cxx
 /*
 CLIENT - UNIX domain - connectionless
 */
 #include "local_sock.h"
 +
 void clean_up(int, const char *); // Close socket and remove
 int
 main( ) {
 int orig_sock; // Original socket descriptor
 10 static struct sockaddr_un // UNIX addresses to be used
 clnt_adr, // Client address
 serv_adr; // Server address
 static char clnt_buf[BUFSIZ], // Message from client
 pipe_buf[BUFSIZ], // Output from fortune command
 + clnt_file[]="XXXXXX";// Temporary file name
 FILE *fin; // File for pipe I/O
 // Assign SERVER address information
 serv_adr.sun_family = AF_UNIX;
 strcpy(serv_adr.sun_path, SERVER_FILE);
 20 if ((orig_sock = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) {
 perror("generate error");
 return 1;
 }
 mkstemp(clnt_file);
 + clnt_adr.sun_family = AF_UNIX; // Assign CLIENT address information
 strcpy( clnt_adr.sun_path, clnt_file );
 unlink( clnt_file ); // Remove
 // BIND the address
 if (bind(orig_sock, (struct sockaddr *) &clnt_adr,
 30 sizeof(clnt_adr.sun_family)+strlen(clnt_adr.sun_path)) < 0) {
 perror("bind error");
 return 2;
 } // Process
 for (int i=0; i < 10; i++) {
 + sleep(1); // slow things down a bit
 fin = popen("/usr/games/fortune -s", "r");
 memset( pipe_buf, 0x0, BUFSIZ ); // clear buffer before reading cmd output
 read( fileno(fin), pipe_buf, BUFSIZ );
 sprintf( clnt_buf, "%d : %s", getpid(), pipe_buf );
 40 sendto( orig_sock, clnt_buf, sizeof(clnt_buf), 0,
 (struct sockaddr *) &serv_adr, sizeof(struct sockaddr) );
 }
 clean_up( orig_sock, clnt_file );
 return 0;
 + }
 void
 clean_up( int sd, const char *the_file ){
 close( sd );
 unlink( the_file );
 50 }

In the client, the mkstemp library function is used to generate a unique file name to be bound to the client's socket. This function is passed a template of XXXXXX that is replaced by a unique file name. The function also opens the file. As only the file name is needed, the file itself can be removed ( unlinked ). If there are multiple clients communicating with the server, it is imperative that each has its own unique file name for binding.

A standard compilation/output sequence using this clientserver pair is shown in Figure 10.15.

Figure 10.15 Compiling and running the UNIX domain connectionless clientserver application.

linux$ g++ p10.8.cxx -o server
linux$ g++ p10.9.cxx -o client
linux$ ./server &
[3] 31801
linux$ ./client

S receives 31802 : Go to a movie tonight. Darkness becomes you.
S receives 31802 : Out of sight is out of mind.
S receives 31802 : Q: What's tan and black and looks great on a lawyer?
 A: A doberman.
. . .
[3] Done server

Figure 10.16 shows what happens if we run two clients and use the ls command to check for the file names to which the client and server sockets are bound. Notice that the server still processes 10 messages. However, it receives half of the messages from one client and half from the other. No error message is generated when the clients continue to send their data to the unbound (closed) server socket.

Figure 10.16 Running the same application with multiple clients.

linux$ ./server &
[1] 32244
linux$ ./client & ./client & ls -l grep ^s
[2] 32248
[3] 32249
srwxr-xr-x 1 gray faculty 0 May 17 09:37 eUc0Xq

<-- 1

srwxr-xr-x 1 gray faculty 0 May 17 09:37 qOC0Tq
srwxr-xr-x 1 gray faculty 0 May 17 09:37 server_socket

S receives 31754 : Anything worth doing is worth overdoing.
S receives 31755 : Marriage causes dating problems.
S receives 31754 : A handful of patience is worth more than a bushel of brains.
S receives 31755 : OK, so you're a Ph.D. Just don't touch anything.
. . .

[3] - Done ./client
[2] - Done ./client
[1] + Done ./server

(1) Unique file names generated for client sockets.

EXERCISE

It was a slow night, and Frick and Frack were discussing UNIX domain connectionless sockets. Frick noted that he thought that the code for the client, given as Program 10.9, was excessive . Frick's client code (about 20 lines less) is shown below.

File : frick.cxx
 /*
 Frick's CLIENT - UNIX domain - connectionless
 */
 #include "local_sock.h"
 +
 int
 main( ) {
 int orig_sock;
 static struct sockaddr_un
 10 serv_adr;
 static char clnt_buf[BUFSIZ],
 pipe_buf[BUFSIZ];
 FILE *fin;
 
 + if ((orig_sock = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) {
 perror("generate error");
 return 1;
 }
 serv_adr.sun_family = AF_UNIX;
 20 strcpy( serv_adr.sun_path, SERVER_FILE );
 for (int i=0; i < 10; i++) {
 sleep(1);
 fin = popen("/usr/games/fortune -s", "r");
 memset( pipe_buf, 0x0, BUFSIZ );
 + read( fileno(fin), pipe_buf, BUFSIZ );
 sprintf( clnt_buf, "%d : %s", getpid(), pipe_buf );
 sendto( orig_sock, clnt_buf, sizeof(clnt_buf), 0,
 (struct sockaddr *) &serv_adr, sizeof(struct
 sockaddr) );
 }
 30 return 0;
 }

Does Frick's client code work with Program10.8 as the server? If Frick's code works, what are its limitations? If it does not work, what must be done to make it work?

10.5.2 An Internet Domain Datagram Socket Example

In the next example, we create a clientserver application that uses connectionless sockets. This application will act like a rudimentary chat program. A user running the server process can interactively read messages from and write messages to the user running the client program, and vice versa. When this application is run, the server program is invoked first and remains in the foreground. At startup, the server displays the port to which the client should bind . The client program, also run in the foreground, can be on a different host or in a separate window on the same host, and is passed on the command line the name of the host where the server process is executing and the port number. Once both processes are up and running, the user on the client enters a line of text and presses enter. The client's input is displayed on the screen of the server. The user running the server process then enters a response that in turn is displayed on the screen of the client, and so on. In a regimented lock-step, send and receive manner, the two users can carry on a very basic form of interactive communication. [13] The client process terminates when the user enters a ^D. The server, which is iterative, continues until removed with a kill command. The program for the server process is shown in Program 10.10.

[13] Granted, this will never replace the talk utility, IRC , or some of the current instant messaging applications, but it could serve as a base for a more sophisticated application.

Program 10.10 Internet domain connectionless server .

File : p10.10.cxx
 /*
 Program 10.10 - SERVER - Internet Domain - connectionless
 */
 #include "local_sock.h"
 + int
 main( ) {
 int sock, n;
 socklen_t server_len, client_len;
 struct sockaddr_in server, // Internet Addresses
 10 client;
 // SOCKET
 if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
 perror("SERVER socket "); return 1;
 }
 + memset(&server, 0, sizeof(server)); // Clear structure
 server.sin_family = AF_INET; // Set address type
 server.sin_addr.s_addr = htonl(INADDR_ANY);
 server.sin_port = htons(0);
 // BIND
 20 if (bind(sock, (struct sockaddr *) &server,
 sizeof(server) ) < 0) {
 perror("SERVER bind "); return 2;
 }
 server_len = sizeof(server); // Obtain address length
 + // Find picked port #
 if (getsockname(sock, (struct sockaddr *) &server,
 &server_len) < 0) {
 perror("SERVER getsocketname "); return 3;
 }
 30 cout << "Server using port " << ntohs(server.sin_port) << endl;
 while ( 1 ) { // Loop forever
 client_len = sizeof(client); // set the length
 memset(buf, 0, BUFSIZ); // clear the buffer
 if ((n=recvfrom(sock, buf, BUFSIZ, 0, // get the client's msg
 + (struct sockaddr *) &client, &client_len)) < 0){
 perror("SERVER recvfrom ");
 close(sock); return 4;
 }
 write(fileno(stdout), buf, n); // display msg on server
 40 memset(buf, 0, BUFSIZ); // clear the buffer
 if ( read(fileno(stdin), buf, BUFSIZ) != 0 ){// get server's msg
 if ((sendto(sock, buf, strlen(buf) ,0, // send to client
 (struct sockaddr *) &client, client_len)) <0){
 perror("SERVER sendto ");
 + close(sock); return 5;
 }
 }
 }
 return 0;
 50 }

Keep in mind that for communications to occur between cooperating processes, a unique association must be established. In the Internet domain, the association is characterized by a quintuple consisting of

protocol, local address, local port, remote address, remote port

In the server program, a datagram (connectionless) socket is created with the socket call. The address family is set to AF_INET, and by default the protocol (which was set to 0) will be UDP. The addressing information for the server is assigned next. The defined constant INADDR_ANY, a wildcard address, indicates the server can use (receive messages at) any valid address for this protocol. Setting the port number to 0 (line 18) directs the system to select a port. When passed a 0 value, the system picks a port that is not in use and is greater than IPPORT_USERRESERVED. On our system this constant is set to 5000. Additional information about port numbers is stored in the file ip_local_port_range found in the /proc/sys/net/ipv4 subdirectory. The first value stored in this file is the number of the first local port for TCP and UDP traffic on the system. The second value is the last local port number. On our system this files contains the values 32768 and 61000.

The getsockname call (line 26) is issued to determine which port the system selected. Note it is important to initialize the third argument of this call to the length of the address argument before the call to getsockname is made (see line 24). The server process displays the port number so a user running a client process will know which port to specify (a more elegant solution would be to store the port number in an environment varible). The server program then enters an endless loop. It clears a receiving buffer and issues a recvfrom call. The recvfrom call will, by default, cause the server process to block until information is received. Once information is received, the remaining parts of the association, the remote address and port, are realized, as this information is contained in the received data. The received message is written to standard output (the screen). The server then clears the buffer and collects the user's response with a call to read . Again, read will cause the serving process to block while awaiting input. If the user enters a non-null response, the sendto call is used to send the response to the address/port of the client from which the message was received. The server process remains active until removed with a kill command or an interrupt (^C) is entered from the keyboard.

As noted, when the client program is invoked, the name of the host running the server process and the port on the server is passed on the command line. The client then uses the gethostbyname call to obtain additional information about the server. This information, along with the passed port number, is stored in the server socket address structure of the client. In an Internet domain setting, a datagram socket is created next, the client addressing information is set, and a call to bind is issued. At this juncture, the client process has sufficient information to initiate communications with the server process. The client enters a loop. The read call is used to obtain user input. If user input does not indicate an end-of-file condition (i.e., the user has not entered ^D), the input is sent to the serving process with the sendto call. The receiving buffer is then cleared, and a call to recvfrom retrieves the response from the user running the server program. The response is displayed to the screen, the buffer cleared, and the loop repeated. If the user running the client process enters ^D, the processing loop is exited, the socket is closed, and the client process terminates.

In this example, the conventions for clientserver communications are extremely regimented. The server process is always started first. The client process must be passed the name of the host running the server process and the proper port number. The client process obtains its user input first, which it then sends to the server. The user running the server process then responds. The user running the client responds to this response, and so on. As can be seen, there is a lot of room for improvement in this application! The code for the client is shown in Program 10.11.

Program 10.11 Internet domain connectionless client .

File : p10.11.cxx
 /*
 Program 10.11 - CLIENT - Internet Domain - connectionless
 */
 #include "local_sock.h"
 + int
 main(int argc, char *argv[]){
 int sock, n;
 socklen_t server_len;
 struct sockaddr_in // Internet addresses
 10 server, client;
 struct hostent *host; // For host information
 if ( argc < 3 ) { // We need server name & port #
 cerr << "usage: " << argv[0] << "server_name port_#" << endl;
 return 1;
 + } // Server information
 if (!(host=gethostbyname(argv[1]))){
 perror("CLIENT gethostname "); return 2;
 } // Set server address info
 memset(&server, 0, sizeof(server)); // Clear structure
 20 server.sin_family = AF_INET; // Address type
 memcpy(&server.sin_addr, host->h_addr, host->h_length);
 server.sin_port = htons(atoi(argv[2]));
 // SOCKET
 if ((sock=socket(PF_INET, SOCK_DGRAM, 0)) < 0 ) {
 + perror("CLIENT socket "); return 3;
 } // Set client address info
 memset(&client, 0, sizeof(client)); // Clear structure
 client.sin_family = AF_INET; // Address type
 client.sin_addr.s_addr = htonl(INADDR_ANY);
 30 client.sin_port = htons( 0 );
 // BIND
 if (bind(sock, (struct sockaddr *) &client,
 sizeof(client)) < 0) {
 perror("CLIENT bind "); return 4;
 + }
 cout << "Client must send first message." << endl;
 while( read(fileno(stdin), buf, BUFSIZ) != 0 ){// get client's msg
 server_len=sizeof(server); // length of address
 if (sendto( sock, buf, strlen(buf), 0, // send msg to server
 40 (struct sockaddr *) &server, server_len) < 0 ){
 perror("CLIENT sendto ");
 close(sock); return 5;
 }
 memset(buf,0,BUFSIZ); // clear the buffer
 + if ((n=recvfrom(sock, buf, BUFSIZ, 0, // get server's msg
 (struct sockaddr *) &server, &server_len)) < 0){
 perror("CLIENT recvfrom ");
 close(sock); return 6;
 }
 50 write( fileno(stdout), buf, n ); // display msg on client
 memset(buf,0,BUFSIZ); // clear the buffer
 }
 close(sock);
 return 0;
 + }

A sample compilation and run of this application is shown in Figure 10.17.

Figure 10.17. A run of Programs 10.10 and 10.11 using two different hosts .

graphics/10fig17.gif

EXERCISE

Run the chat application presented in Program 10.10 and 10.11 with multiple clients. To accomplish this, you will need either access to multiple hosts or the ability to create multiple windows on the same host. Modify code for the client so that it initially requests a three-letter ID (handle) from the user. To identify the source of each message, add the handle to the beginning of all messages sent from the client to the server.

Programs and Processes

Processing Environment

Using Processes

Primitive Communications

Pipes

Message Queues

Semaphores

Shared Memory

Remote Procedure Calls

Sockets

Threads

Appendix A. Using Linux Manual Pages

Appendix B. UNIX Error Messages

Appendix C. RPC Syntax Diagrams

Appendix D. Profiling Programs



Interprocess Communication in Linux
Interprocess Communications in Linux: The Nooks and Crannies
ISBN: 0130460427
EAN: 2147483647
Year: 2001
Pages: 136

Similar book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net