Time Service: Handling Client Connections
step1's handleClient() (Listing Two, available electronically; see www.ddj.com/code/) is in charge of the client conversation. It runs in a loop, acting much like a command shell. There are only three valid commands:
- now retrieves the current time in CST.
- help shows a brief help message.
- quit ends the conversation.
handleClient() shows the basics of using APR to read from and write to a socket. It breaks the loop when the client enters quit or explicitly breaks the connection at the socket level. (In telnet, do the latter by typing control-], then quit.)
apr_socket_recv() (1) reads data from the client socket. The size parameter serves a dual purpose: On the way into the function, it states the size of the input buffer; on the way out, it states how many characters were written to the buffer (that is, read from the client). This call blocks if there is no data waiting to be read. You can call apr_socket_timeout_set() to set a socket's read/write maximum wait time, or set the option APR_SO_NONBLOCK to make the socket completely nonblocking.
Note that the sample code cheats somewhat: All commands are a single line of plaintext, and are expected to be smaller than the receive buffer. In a more complex protocol, the service may require several reads from the client, storing the results in a temporary buffer, in order to build a complete request. (Consider, for example, a large HTTP POST operation.)
The function CommandInterpreter::processCommand() (2) returns a numeric constant based on the input string read from the client. This conveniently dovetails with a switch() (3) to determine the action: Send the time, disconnect, send a help message, or send an error message. Each case: label appends some data to a std::stringstream buffer to construct the response message.
After the switch() block, handleClient() sends the buffer's contents back to the client. Similar to receiving data, the call to apr_socket_send() (4) uses the size parameter to declare both the buffer's size (on the way in) and how many bytes were written (on the way out). Note that it's not necessary to pass the function a NULL-terminated string, because you tell it how many characters to read from the buffer.