NML was originally designed for applications that would post data to buffers without any concern for which processes would read the buffer or send commands to buffers to be read by a process which would have no need to be concerned with which process sent the message. However some applications are built around the notion of returning reply messages only to the process that originally sent a corresponding query message. There are several ways to accomplish this using the original NML API however these extensions hopefully make building such systems easier.
Each query/reply service requires 3 NML buffers. A queued channel that to send queries to the server, a subdivided channel for replies and another queued channel used to establish unique ids for each client. The names of those buffers must end with "Query", "Reply" and "ID" and begin with the same base. (echoQuery, echoReply, and echoID are the buffer names from the example below.) The number of subdivisions for the reply buffer will determine how many clients the service will support at the same time.
To use subdivisions just add "subdiv=<# of subdivisions>" at the end of the buffer line. You may also need to increase the buffer size since the area allocated for each subdivision will be the buffer size minus some overhead divided by the number of subdivisions. Currently, subdivisions cannot be used with STCP,RPC, subscriptions or broadcast options.
There are 2 classes for providing or using the service: NML_QR_SERVER and NML_QR_CLIENT. The same program might use the NML_QR_SERVER for one service and the NML_QR_CLIENT for another service. For each service, there can be many clients but only 1 server.
The NML_QR_SERVER class provides the interface to be used by programs that will accept queries and return replies. Here is it's C++ definition.
class NML_QR_SERVER { public: // Constructor NML_QR_SERVER(NML_FORMAT_PTR f_ptr, char *qr_name, char *process_name, char *config_file); // Destructor ~NML_QR_SERVER(); // Read the next query or return 0 if there are no new queries. NMLTYPE readQuery(); // Wait for the next query and read it unless the timeout expires NMLTYPE waitForQuery(double timeout); // Return the address where queries are stored. NMLmsg *getQueryAddress(); // Send a reply to the last query. int replyToLastQuery(NMLmsg *message_to_send); // Check to see if this server and all its internal NML buffers were properly constructed. int valid(); // Delete and reconstruct each of the internal NML buffers. int reset(); };
The constructor takes arguments similiar to an NML object except that the qr_name is used to create three seperate buffer names by adding "Query","Reply" and "ID" to it. The format function passed in the first argument must handle both the query and reply message types. readQuery performs and NML::read on the query buffer and also checks for clients connecting or disconnecting. waitForQuery performs an NML::blocking_read on the query buffer and also checks for clients connecting or disconnecting. getQueryAddress returns the NML::get_address() value for the query buffer. replyToLastQuery writes the message given into the subdivision of the reply buffer indicated by the last query message recieved. valid checks to see that all the query, reply and id channels are all valid. reset will delete all the NML buffers and recreate them. reset is typically called only after valid returned 0, if the reset is successful it will return a non-zero value and the return value of valid should also become nonzero.
It is important to call readQuery() or waitForQuery() periodically or new clients will not be able to connect.
P>The NML_QR_CLIENT class provides the interface to be used by programs that will send queries and expect replies in return. Here is it's C++ definition.class NML_QR_CLIENT { public: // Constructor NML_QR_CLIENT(NML_FORMAT_PTR f_ptr, char *qr_name, char *process_name, char *config_file); // Destructor ~NML_QR_CLIENT(); // send a query message to the server int sendQuery(NML_QUERY_MSG *); // read the next reply or return 0 if no reply has been recieved. NMLTYPE readReply(); // wait for the next reply to be recieved or until the timeout occurs NMLTYPE waitForReply(double timeout); // return the address where replies are recieved NMLmsg *getReplyAddress(); // Check to see that all the internal NML buffers were properly constructed // and that the server had not used up all the subdivisions. int valid(); // Delete and recreate all the internal NML buffers and retry the server for // a valid connection. int reset(); };
The constructor takes arguments similiar to an NML object except that the qr_name is used to create three seperate buffer names by adding "Query","Reply" and "ID" to it. The format function passed in the first argument must handle both the query and reply message types. sendQuery performs and NML::write on the query buffer. readReply and waitForReply perform an NML::read and NML::blocking_read respectively on the subdivision of the reply buffer associated with this process. getReplyAddress returns the NML::get_address() value for the reply buffer. valid checks to see that all the query, reply and id channels are all valid and that the id is valid. reset will delete all the NML buffers and recreate them and attempt to get a new id from the server. reset is typically called only after valid returned 0, if the reset is successful it will return a non-zero value and the return value of valid should also become nonzero.
sendQuery takes a pointer to a NML_QUERY_MSG. NML_QUERY_MSG is derived from NMLmsg but adds an id used to for replying to the query. All query messages must be derived from NML_QUERY_MSG.
The Java versions of these classes are not yet available.
The example below is similiar to the UNIX echo server for TCP. Multiple clients type a line at a time that is then sent to the server and the server sends back a message with a copy of that line. The server also prints the line and the client id associated with each message. Notice that if you run multiple instances of the client that although the source code and therefore process_name for each client is the same the id's will be unique.
Here is the NML configuration file:
# buffers: # nametypehostsizeneut RPC# buffer# max_proc [type-spec] B echoQuerySHMEMlocalhost 100000 0x20001050 1 12 101 TCP=6001 bsem=201 queue B echoReplySHMEMlocalhost 200000 0x20001050 2 12 102 TCP=6001 bsem=202 subdiv=100 B echoIDSHMEMlocalhost 100000 0x20001050 3 12 103 TCP=6001 bsem=203 queue # processes: # namebuffer typehost opsserver timeoutmaster c_num P echoclnt echoQueryREMOTEREMOTEhost W0INF 00 P echoclnt echoReplyREMOTEREMOTEhost R0INF 00 P echoclnt echoIDREMOTEREMOTEhost R0INF 00 P echosvr echoQueryLOCALlocalhost R2INF 11 P echosvr echoReplyLOCALlocalhost W2INF 11 P echosvr echoIDLOCALlocalhost W2INF 11
Here is the header file defining the NML messages.
#ifndef ECHO_TYPES_HH #define ECHO_TYPES_HH #include "rcs.hh" #define ECHO_QUERY_TYPE 101 #define ECHO_REPLY_TYPE 102 class ECHO_QUERY: public NML_QUERY_MSG { public: ECHO_QUERY(); void update(CMS *); char line[80]; }; class ECHO_REPLY: public NMLmsg { public: ECHO_REPLY(); void update(CMS *); char line[80]; }; extern int ECHO_format(NMLTYPE type, void *buffer, CMS *cms); #endif
Here is the server C++ code:
#include "rcs.hh" #include #include #include #include #include "echo_types.hh" int echo_svr_quit = 0; void sigint_handler(int sig) { echo_svr_quit = 1; } int main() { signal(SIGINT,sigint_handler); NML_QR_SERVER echoQrServer(ECHO_format, "echo","echosvr","ex_cfg.nml"); nml_start(); printf("echosvr started . . .\n"); ECHO_QUERY *eqMsg; ECHO_REPLY erMsg; while(!echo_svr_quit) { switch(echoQrServer.waitForQuery(NML_NO_TIMEOUT)) { case ECHO_QUERY_TYPE: eqMsg = (ECHO_QUERY *) echoQrServer.getQueryAddress(); printf("Client %d sent %s\n",eqMsg->subdiv_for_reply, eqMsg->line); strncpy(erMsg.line, eqMsg->line, 80); echoQrServer.replyToLastQuery(&erMsg); break; case 0: // no query break; case -1: default: // error echo_svr_quit = 1; break; } } }
Here is the client C++ code:
#include "rcs.hh" #include #include #include "echo_types.hh" int echo_clnt_quit = 0; void sigint_handler(int sig) { echo_clnt_quit = 1; } int main() { signal(SIGINT,sigint_handler); NML_QR_CLIENT echoQrClient(ECHO_format, "echo","echoclnt","ex_cfg.nml"); printf("echoclnt started . . .\n"); while(!echo_clnt_quit) { ECHO_QUERY eqMsg; ECHO_REPLY *erMsg; fgets(eqMsg.line,80,stdin); echoQrClient.sendQuery(&eqMsg); switch(echoQrClient.waitForReply(NML_NO_TIMEOUT)) { case ECHO_REPLY_TYPE: erMsg = (ECHO_REPLY *) echoQrClient.getReplyAddress(); printf("Server sent back %s\n",erMsg->line); break; case 0: // no query break; case -1: default: // error echo_clnt_quit = 1; break; } } }
Last Modified: March 17,1999
If you have questions or comments regarding this page or you would like to be notified of changes to the RCS library via email, please contact Will Shackleford at shackle [at] cme.nist.gov (shackle[at]cme[dot]nist[dot]gov)