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.

EXERCISE

Further refine the chat program to allow multiple users on different clients to communicate with one another in a manner similar in function to a party line or conference conversation. In this implementation, each user chooses a chat name, which identifies all messages subsequently typed by that user. The chat name is prefaced by the host name . A chat conversation might look like this:

[View full width]

 

[View full width]

[beta:fred] What did you think of the test yesterday? [xi:zippo] Ok, but I didn't like the question about efficiency! [rho:joe] Yeah, more or less efficient-we should have got the points on that one.

The options for invoking this version of chat are

chat

Invoke chat for eavesdropping (i.e., no talking allowedjust view messages)

chat fred

Invoke chat with the chat name fred

When in chat , the user should be able to issue commands that are acted upon rather than sent as a message to others. For example, some chat commands (lines beginning with either a . or !) might be

.re jokes

Read file jokescontents appear in the conversation

.wr cstuff

Write a copy of the conversation in a file called cstuff

.ap cstuff

Append a copy of the conversation to a file called cstuff

.en

End recording of conversation to output file

!cmd

Escape chat temporarily and execute the command cmd

.wn rho:joe

Whisper (talk only) to chat name joe at host rho

.wf

Turn whisper off

.lo

Leave chat

.wo

Display the names of all chat participants

The chat program should make use of sockets. Implement some form of non-blocking I/O to prevent possible deadlock situations. To complete the assignment, you will need to write two programs: a server program, which facilitates communications between clients and runs continuously in the background, and a client program that runs in the foreground. You will need to decide whether or not the server should fork child processes to manage communications with individual clients or keep information in some sort of table arrangement. When the user invokes the client program, it connects to the chat server. As broadcasting is frowned upon, the client program should read the environment variable CHAT to find the name of the chat server. If the entry cannot be made in the /etc/services file for the chat port, an environment variable, such as PORT, should be used to hold the number of the port. You may want to use the MSG_PEEK option to peek at data to determine if it is a command or a message. Some commands should be processed locally by the client, while others might best be done by the server, with the information returned to the appropriate client.

Категории