- Implementing a Client
- Parsing Strings by Using StringTokenizer
- Example: A Client to Verify E-Mail Addresses
- Example: A Network Client That Retrieves URLs
- The URL Class
- WebClient: Talking to Web Servers Interactively
- Implementing a Server
- Example: A Simple HTTP Server
- RMI: Remote Method Invocation
- Summary
17.3 Example: A Client to Verify E-Mail Addresses
One of the best ways to get comfortable with a network protocol is to open a telnet connection to the port a server is on and try out commands interactively. For example, consider connecting directly to a mail server to verify the correctness of potential e-mail addresses. To connect to the mail server, we need to know that SMTP (Simple Mail Transfer Protocol) servers generally listen on port 25, send one or more lines of data after receiving a connection, accept commands of the form expn username to expand usernames, and accept quit as the command to terminate the connection. Commands are case insensitive. For instance, Listing 17.5 shows an interaction with the mail server at apl.jhu.edu.
Listing 17.5 Talking to a Mail Server
> telnet apl.jhu.edu 25 Trying 128.220.101.100... Connected to aplcenMP.apl.jhu.edu. Escape character is '^]'. 220 aplcenMP.apl.jhu.edu ESMTP Sendmail 8.9.3/8.9.1; Sat, 10 Feb 2001 12:05:42 500 (EST) expn hall 250 Marty Hall <hall@aplcenMP.apl.jhu.edu> expn root 250-Tom Vellani <vellani@aplcenMP.apl.jhu.edu> 250 Gary Gafke <gary@aplcenMP.apl.jhu.edu> quit 221 aplcenMP.apl.jhu.edu closing connection Connection closed by foreign host.
For a network client to verify an e-mail address, the program needs to break up the address into username and hostname sections, connect to port 25 of the host, read the initial connection message, send an expn on the username, read and print the result, then send a quit. The response from the SMTP server may not consist of only a single line. Because readLine blocks if the connection is still open but no line of data has been sent, we cannot call readLine more than once. So, rather than using readLine at all, we use read to populate a byte array large enough to hold the likely response, record how many bytes were read, then use write to print out that number of bytes. This process is shown in Listing 17.6, with Listing 17.7 showing the helper class that breaks an e-mail address into separate username and hostname components.
Listing 17.6 AddressVerifier.java
import java.net.*; import java.io.*; /** Given an e-mail address of the form user@host, * connect to port 25 of the host and issue an * 'expn' request for the user. Print the results. */ public class AddressVerifier extends NetworkClient { private String username; public static void main(String[] args) { if (args.length != 1) { usage(); } MailAddress address = new MailAddress(args[0]); AddressVerifier verifier = new AddressVerifier(address.getUsername(), address.getHostname(), 25); verifier.connect(); } public AddressVerifier(String username, String hostname, int port) { super(hostname, port); this.username = username; } /** NetworkClient, the parent class, automatically establishes * the connection and then passes the Socket to * handleConnection. This method does all the real work * of talking to the mail server. */ // You can't use readLine, because it blocks. Blocking I/O // by readLine is only appropriate when you know how many // lines to read. Note that mail servers send a varying // number of lines when you first connect or send no line // closing the connection (as HTTP servers do), yielding // null for readLine. Also, we'll assume that 1000 bytes // is more than enough to handle any server welcome // message and the actual EXPN response. protected void handleConnection(Socket client) { try { PrintWriter out = SocketUtil.getWriter(client); InputStream in = client.getInputStream(); byte[] response = new byte[1000]; // Clear out mail server's welcome message. in.read(response); out.println("EXPN " + username); // Read the response to the EXPN command. int numBytes = in.read(response); // The 0 means to use normal ASCII encoding. System.out.write(response, 0, numBytes); out.println("QUIT"); client.close(); } catch(IOException ioe) { System.out.println("Couldn't make connection: " + ioe); } } /** If the wrong arguments, thn warn user. */ public static void usage() { System.out.println ("You must supply an email address " + "of the form 'username@hostname'."); System.exit(-1); } }
Listing 17.7 MailAddress.java
import java.util.*; /** Takes a string of the form "user@host" and * separates it into the "user" and "host" parts. */ public class MailAddress { private String username, hostname; public MailAddress(String emailAddress) { StringTokenizer tokenizer = new StringTokenizer(emailAddress, "@"); (continued) this.username = getArg(tokenizer); this.hostname = getArg(tokenizer); } private static String getArg(StringTokenizer tok) { try { return(tok.nextToken()); } catch (NoSuchElementException nsee) { System.out.println("Illegal email address"); System.exit(-1); return(null); } } public String getUsername() { return(username); } public String getHostname() { return(hostname); } }
Finally, here is an example of the address verifier in action, looking for the addresses of the main originators of the WWW and the Java platform.
> java AddressVerifier tbl@w3.org 250 <timbl@hq.lcs.mit.edu> > java AddressVerifier timbl@hq.lcs.mit.edu 250 Tim Berners-Lee <timbl> > java AddressVerifier gosling@mail.javasoft.com 550 gosling... User unknown