Out of Band Messages
There are occasions when a sending process needs to notify the recipient process of an urgent message. The MSG_OOB flag is used with the send and receive calls to indicate and process urgent messages. At present, only stream-based sockets support out of band messaging.
As with MSG_PEEK, we can modify Program 10.6 to show how the server process might process an urgent message that has been sent by a client. Since the modifications are somewhat more extensive , the entire server program is shown in Program 10.16. Modified sections of code are highlighted in bold (lines 5, 15, 16, and 4268) and are in gray.
Program 10.16 Internet domain connection-oriented server using MSG_OOB.
File : p10.16.cxx /* Internet domain, connection-oriented SERVER - MSG_PEEK */ #include "local_sock.h" + #include // For nanosleep void signal_catcher(int); int main( ) { int orig_sock, // Original socket in server 10 new_sock; // New socket from connect socklen_t clnt_len; // Length of client address struct sockaddr_in // Internet addr client & server clnt_adr, serv_adr; int len, i, // Misc counters, etc. + urg, mark; // Flag reception of OOB msg and to // note its location in the stream. // Catch when child terminates if (signal(SIGCHLD , signal_catcher) == SIG_ERR) { perror("SIGCHLD"); 20 return 1; } if ((orig_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("generate error"); return 2; + } memset( &serv_adr, 0, sizeof(serv_adr) ); // Clear structure serv_adr.sin_family = AF_INET; // Set address type serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); // Any interface serv_adr.sin_port = htons(PORT); // Use our fake port 30 // BIND if (bind( orig_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) < 0){ perror("bind error"); close(orig_sock); + return 3; } if (listen(orig_sock, 5) < 0 ) { // LISTEN perror("listen error"); close (orig_sock); 40 return 4; } struct timespec req, rem; // For nanosleep do { clnt_len = sizeof(clnt_adr); // ACCEPT a connect + if ((new_sock = accept( orig_sock, (struct sockaddr *) &clnt_adr, &clnt_len)) < 0) { perror("accept error"); close(orig_sock); return 5; 50 } if ( fork( ) == 0 ) { // Generate a CHILD urg = mark = 0; do { req.tv_sec = 5; req.tv_nsec = 0; // set time to sleep + nanosleep( &req, &rem); // slow down the server if ( (len=recv(new_sock, buf, BUFSIZ, MSG_OOB)) > 0) { write( fileno(stdout), "URGENT msg pending ", 19); urg = 1; } 60 if ( urg ) ioctl(new_sock, SIOCATMARK, &mark); if ( mark ) { write( fileno(stdout), " <-- the URGENT msg ",20); mark = urg = 0; } + if ((len=recv(new_sock, buf, BUFSIZ, 0)) > 0) { if ( !strncmp(buf, ".done", len-1) ) break; write( fileno(stdout), buf, len); } } while( 1 ); 70 write( fileno(stdout),"Leaving child process ",22); close(new_sock); // In CHILD process return 0; } else close(new_sock); // In PARENT process + } while( true ); // FOREVER return 0; } void signal_catcher(int the_sig){ 80 signal(the_sig, signal_catcher); // reset wait(0); // keep the zombies at bay }
In the server program (10.16), the integer variables urg and mark have been added to facilitate the processing of urgent messages. These variables act as flags to indicate when an urgent message has been received ( urg ) and actually processed ( mark ). In the processing loop of the server, these variables are initially set to 0. An inner do-while loop starts with a call to nanosleep . The nanosleep call is added to slow down server processing to clearly demonstrate the receipt sequence of client messages. The nanosleep call was chosen (rather than sleep ), as it is standardized by POSIX, does not impact other signals, and provides a finer granularity for pausing.
Table 10.27. Summary of the nanosleep System Call.
Include File(s) |
Manual Section |
2 |
||
Summary |
int nanosleep(const struct timespec *req, struct timespec *rem); |
|||
Return |
Success |
Failure |
Sets errno |
|
-1 |
Yes |
The nanosleep call uses a reference to a timespec structure for each of its two arguments. The first argument, req , is the required sleep time (in seconds and nanoseconds). The second argument, rem , is the remaining time should the call be interrupted by a signal. When successful, the call returns a 0; if it fails, it returns a 1 and sets errno as shown in Table 10.28.
Table 10.28. nanosleep Error Messages.
# |
Constant |
perror Message |
Explanation |
---|---|---|---|
4 |
EINTR |
Interrupted system call |
A non-blocked signal was received by process before the full time expired . The value in rem is the remaining time to sleep. |
22 |
EINVAL |
Invalid argument |
One of the time limit values is out of range (0 to 999,999,999) or tv_sec value is negative. |
When we run this program, we will see that the notification of the receipt of an urgent message is received prior to messages that have already been sent but not yet received. A recv call with the flags argument set to MSG_OOB is made next . If notification of an urgent message has been received, this call returns a value greater than 0 (i.e., 1). When the server receives notification, it displays, to standard output, the message URGENT msg pending and sets the urg variable to 1. Following this, the urg variable is checked. If it is set (i.e., is nonzero), a call to ioctl is made. With the addition of the ioctl call, we must include the header file in the local_sock.h file. The ioctl call is passed the socket descriptor, the flag SIOCATMARK, [17] and the address of the mark variable. With this argument set, the ioctl call assigns the variable mark a positive value if the next I/O call will process data that is beyond the urgent data; otherwise , it assigns mark a 0 value. The contents of the mark variable are tested next. If the server is beyond the processing of the urgent message data, the string <-- the URGENT msg is appended to the data currently displayed and the mark and urg variables cleared by resetting them to 0. In either event, a second call to recv is made to receive and process pending messages from the client. If a message is not the string .done , the message is displayed; otherwise, a message indicating the child process is exiting is generated, the socket descriptor is closed, and the child process exits.
[17] This constant is defined in the file (yet another one of the include files that is automatically drawn upon when including the standard include files for sockets).
The code for the client must be changed minimally . If the first character of the data entered by the user is an exclamation mark ( ! ), the remaining data is considered urgent and sent with the flag argument set to MSG_OOB; otherwise, the data is sent with the flag argument set to 0. Program 10.17 shows the modified client program (10.7).
Program 10.17 Internet domain connection-oriented client using MSG_OOB.
File : p10.17.cxx /* Internet domain, connection-oriented CLIENT - MSG_OOB */ . . // Same a Program 10.7 . + do { // Process write(fileno(stdout),"> ", 3); // Prompt the user if ((len=read(fileno(stdin), buf, BUFSIZ)) > 0) { if ( buf[0] == '!' ){ write(fileno(stdout), "URGENT msg sent ", 16); 40 send(orig_sock, buf, len, MSG_OOB ); } else send(orig_sock, buf, len, 0 ); } } while( strncmp(buf, ".done", len-1) ); // until end of input . . // Same as Program 10.7 .
Figure 10.19 shows what occurs when this MSG_OOB clientserver application is run.
Figure 10.19 Using MSG_OOB in two separate windows on separate hosts .
linux$ g++ p10.16.cxx -o server linux$ g++ p10.17.cxx -o client linux$ server perseus$ client linux a > a b > b URGENT msg pending > c c > !help !help <-- the URGENT msg URGENT msg sent d > d e > e Leaving child process > .done ^C perseus$
The server process is established first, then the client is established. The user running the client program enters the letters a , b , c , the string !help followed by the letters d , e , and then the string .done . The server process begins to process the messages from the client (remember, we added a call to nanosleep in the server to slow it down). After it has processed the initial message, it receives notice that an urgent message is pending. However, it does not actually receive the urgent message at this time. The urgent message, which is eventually received and flagged by the server process with the words <-- URGENT msg , is received in its proper order. If we want to obtain the urgent message at the time of notification, we must either buffer the intervening messages or discard them.