URLs, InetAddresses, and URLConnections

Report
Java Network Programming
Elliotte Rusty Harold
[email protected]
http://metalab.unc.edu/javafaq/slides/
© 2000 Elliotte Rusty Harold
7/17/2015
We will learn how Java
handles






Internet Addresses
URLs
CGI
Sockets
Server Sockets
UDP
© 2000 Elliotte Rusty Harold
7/17/2015
I assume you
 Understand basic Java syntax and I/O
 Have a user’s view of the Internet
 No prior network programming
experience
© 2000 Elliotte Rusty Harold
7/17/2015
Applet Network Security
Restrictions
 Applets may:
– send data to the code base
– receive data from the code base
 Applets may not:
– send data to hosts other than the code base
– receive data from hosts other than the code
base
© 2000 Elliotte Rusty Harold
7/17/2015
Some Background




Hosts
Internet Addresses
Ports
Protocols
© 2000 Elliotte Rusty Harold
7/17/2015
Hosts
 Devices connected to the Internet are
called hosts
 Most hosts are computers, but hosts also
include routers, printers, fax machines,
soda machines, bat houses, etc.
© 2000 Elliotte Rusty Harold
7/17/2015
Internet addresses
 Every host on the Internet is identified by a
unique, four-byte Internet Protocol (IP)
address.
 This is written in dotted quad format like
199.1.32.90 where each byte is an unsigned
integer between 0 and 255.
 There are about four billion unique IP
addresses, but they aren’t very efficiently
allocated
© 2000 Elliotte Rusty Harold
7/17/2015
Domain Name System (DNS)
 Numeric addresses are mapped to names
like www.blackstar.com or
star.blackstar.com by DNS.
 Each site runs domain name server
software that translates names to IP
addresses and vice versa
 DNS is a distributed system
© 2000 Elliotte Rusty Harold
7/17/2015
The InetAddress Class
 The java.net.InetAddress class
represents an IP address.
 It converts numeric addresses to host
names and host names to numeric
addresses.
 It is used by other network classes like
Socket and ServerSocket to identify
hosts
© 2000 Elliotte Rusty Harold
7/17/2015
Creating InetAddresses
 There are no public InetAddress()
constructors. Arbitrary addresses may
not be created.
 All addresses that are created must be
checked with DNS
© 2000 Elliotte Rusty Harold
7/17/2015
The getByName() factory
method
public static InetAddress getByName(String
host) throws UnknownHostException
InetAddress utopia, duke;
try {
utopia = InetAddress.getByName("utopia.poly.edu");
duke = InetAddress.getByName("128.238.2.92");
}
catch (UnknownHostException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
Other ways to create
InetAddress objects
public static InetAddress[] getAllByName(String host)
throws UnknownHostException
public static InetAddress getLocalHost() throws
UnknownHostException
© 2000 Elliotte Rusty Harold
7/17/2015
Getter Methods




public
public
public
public
© 2000 Elliotte Rusty Harold
boolean
String
byte[]
String
isMulticastAddress()
getHostName()
getAddress()
getHostAddress()
7/17/2015
Utility Methods
 public int hashCode()
 public boolean equals(Object o)
 public String toString()
© 2000 Elliotte Rusty Harold
7/17/2015
Ports
 In general a host has only one Internet
address
 This address is subdivided into 65,536
ports
 Ports are logical abstractions that allow
one host to communicate simultaneously
with many other hosts
 Many services run on well-known ports.
For example, http tends to run on port 80
© 2000 Elliotte Rusty Harold
7/17/2015
Protocols
 A protocol defines how two hosts talk to
each other.
 The daytime protocol, RFC 867, specifies
an ASCII representation for the time
that's legible to humans.
 The time protocol, RFC 868, specifies a
binary representation, for the time that's
legible to computers.
 There are thousands of protocols,
standard and non-standard
© 2000 Elliotte Rusty Harold
7/17/2015
IETF RFCs
 Requests For Comment
 Document how much of the Internet
works
 Various status levels from obsolete to
required to informational
 TCP/IP, telnet, SMTP, MIME, HTTP,
and more
 http://www.faqs.org/rfc/
© 2000 Elliotte Rusty Harold
7/17/2015
W3C Standards
 IETF is based on “rough consensus and
running code”
 W3C tries to run ahead of
implementation
 IETF is an informal organization open to
participation by anyone
 W3C is a vendor consortium open only to
companies
© 2000 Elliotte Rusty Harold
7/17/2015
W3C Standards







HTTP
HTML
XML
RDF
MathML
SMIL
P3P
© 2000 Elliotte Rusty Harold
7/17/2015
URLs
 A URL, short for "Uniform Resource
Locator", is a way to unambiguously
identify the location of a resource on the
Internet.
© 2000 Elliotte Rusty Harold
7/17/2015
Example URLs
http://java.sun.com/
file:///Macintosh%20HD/Java/Docs/JDK%201.1.1%20docs/api/ja
va.net.InetAddress.html#_top_
http://www.macintouch.com:80/newsrecent.shtml
ftp://ftp.info.apple.com/pub/
mailto:[email protected]
telnet://utopia.poly.edu
ftp://mp3:[email protected]:21000/c%3a/stuff/mp3/
http://[email protected]/
http://metalab.unc.edu/nywc/comps.phtml?category=Choral+Wo
rks
© 2000 Elliotte Rusty Harold
7/17/2015
The Pieces of a URL
 the protocol, aka scheme
 the authority
– user info
user name
password
– host name or address
– port
 the path, a.k.a. file
 the ref, a.k.a. section or anchor
 the query string
© 2000 Elliotte Rusty Harold
7/17/2015
The java.net.URL class
 A URL object represents a URL.
 The URL class contains methods to
– create new URLs
– parse the different parts of a URL
– get an input stream from a URL so you can
read data from a server
– get content from the server as a Java object
© 2000 Elliotte Rusty Harold
7/17/2015
Content and Protocol
Handlers
 Content and protocol handlers separate the data
being downloaded from the the protocol used to
download it.
 The protocol handler negotiates with the server
and parses any headers. It gives the content
handler only the actual data of the requested
resource.
 The content handler translates those bytes into a
Java object like an InputStream or
ImageProducer.
© 2000 Elliotte Rusty Harold
7/17/2015
Finding Protocol Handlers
 When the virtual machine creates a URL
object, it looks for a protocol handler that
understands the protocol part of the URL
such as "http" or "mailto".
 If no such handler is found, the
constructor throws a
MalformedURLException.
© 2000 Elliotte Rusty Harold
7/17/2015
Supported Protocols
 The exact protocols that Java supports
vary from implementation to
implementation though http and file are
supported pretty much everywhere. Sun's
JDK 1.1 understands ten:
–
–
–
–
–
file
ftp
gopher
http
mailto
© 2000 Elliotte Rusty Harold
–appletresource
–doc
–netdoc
–systemresource
–verbatim
7/17/2015
URL Constructors
 There are four (six in 1.2) constructors in the
java.net.URL class.
public URL(String u) throws MalformedURLException
public URL(String protocol, String host, String file)
throws MalformedURLException
public URL(String protocol, String host, int port,
String file) throws MalformedURLException
public URL(URL context, String url) throws
MalformedURLException
public URL(String protocol, String host, int port,
String file, URLStreamHandler handler) throws
MalformedURLException
public URL(URL context, String url, URLStreamHandler
handler) throws MalformedURLException
© 2000 Elliotte Rusty Harold
7/17/2015
Constructing URL Objects
 An absolute URL like
http://www.poly.edu/fall97/grad.html#cs
try {
URL u = new URL(
"http://www.poly.edu/fall97/grad.html#cs");
}
catch (MalformedURLException e) {}
© 2000 Elliotte Rusty Harold
7/17/2015
Constructing URL Objects in
Pieces
 You can also construct the URL by
passing its pieces to the constructor, like
this:
URL u = null;
try {
u = new URL("http", "www.poly.edu",
"/schedule/fall97/bgrad.html#cs");
}
catch (MalformedURLException e) {}
© 2000 Elliotte Rusty Harold
7/17/2015
Including the Port
URL u = null;
try {
u = new URL("http", "www.poly.edu", 8000,
"/fall97/grad.html#cs");
}
catch (MalformedURLException e) {}
© 2000 Elliotte Rusty Harold
7/17/2015
Relative URLs
 Many HTML files contain relative URLs.
 Consider the page
http://metalab.unc.edu/javafaq/index.html
 On this page a link to “books.html" refers to
http://metalab.unc.edu/javafaq/books.html.
© 2000 Elliotte Rusty Harold
7/17/2015
Constructing Relative URLs
 The fourth constructor creates URLs
relative to a given URL. For example,
try {
URL u1 = new
URL("http://metalab.unc.edu/index.html"
);
URL u2 = new URL(u1, "books.html");
}
catch (MalformedURLException e) {}
 This is particularly useful when parsing
HTML.
© 2000 Elliotte Rusty Harold
7/17/2015
Parsing URLs
 The java.net.URL class has five
methods to split a URL into its
component parts. These are:
public
public
public
public
public
© 2000 Elliotte Rusty Harold
String
String
int
String
String
getProtocol()
getHost()
getPort()
getFile()
getRef()
7/17/2015
For example,
try {
URL u = new
URL("http://www.poly.edu/fall97/grad.html#cs ");
System.out.println("The protocol is " +
u.getProtocol());
System.out.println("The host is " + u.getHost());
System.out.println("The port is " + u.getPort());
System.out.println("The file is " + u.getFile());
System.out.println("The anchor is " + u.getRef());
}
catch (MalformedURLException e) { }
© 2000 Elliotte Rusty Harold
7/17/2015
Parsing URLs
 JDK 1.3 adds three more:
public String getAuthority()
public String getUserInfo()
public String getQuery()
© 2000 Elliotte Rusty Harold
7/17/2015
Missing Pieces
 If a port is not explicitly specified in the URL
it's set to -1. This means the default port.
 If the ref doesn't exist, it's just null, so watch
out for NullPointerExceptions. Better
yet, test to see that it's non-null before using
it.
 If the file is left off completely, e.g.
http://java.sun.com, then it's set to "/".
© 2000 Elliotte Rusty Harold
7/17/2015
Reading Data from a URL
 The openStream() method connects to the
server specified in the URL and returns an
InputStream object fed by the data from that
connection.
public final InputStream openStream() throws
IOException
 Any headers that precede the actual data are
stripped off before the stream is opened.
 Network connections are less reliable and slower
than files. Buffer with a BufferedReader or a
BufferedInputStream.
© 2000 Elliotte Rusty Harold
7/17/2015
Webcat
import java.net.*;
import java.io.*;
public class Webcat {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
try {
URL u = new URL(args[i]);
InputStream in = u.openStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String theLine;
while ((theLine = br.readLine()) != null) {
System.out.println(theLine);
}
} catch (IOException e) { System.err.println(e);}
}
}
}© 2000 Elliotte Rusty Harold
7/17/2015
The Bug in readLine()
 What readLine() does:
– Sees a carriage return, waits to see if next
character is a line feed before returning
 What readLine() should do:
– Sees a carriage return, return, throw away
next character if it's a linefeed
© 2000 Elliotte Rusty Harold
7/17/2015
Webcat
import java.net.*;
import java.io.*;
public class Webcat {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
try {
URL u = new URL(args[i]);
InputStream in = u.openStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
int c;
while ((c = br.read()) != -1) {
System.out.write(c);
}
} catch (IOException e) { System.err.println(e);}
}
}
}© 2000 Elliotte Rusty Harold
7/17/2015
CGI
 Common Gateway Interface
 A lot is written about writing server side
CGI. I’m going to show you client side
CGI.
 We’ll need to explore HTTP a little
deeper to do this
© 2000 Elliotte Rusty Harold
7/17/2015
Normal web surfing uses
these two steps:
– The browser requests a page
– The server sends the page
 Data flows primarily from the server to
the client.
© 2000 Elliotte Rusty Harold
7/17/2015
Forms
 There are times when the server needs to
get data from the client rather than the
other way around. The common way to
do this is with a form like this one:
© 2000 Elliotte Rusty Harold
7/17/2015
CGI
 The user types data into a form and hits the
submit button.
 The browser sends the data to the server
using the Common Gateway Interface, CGI.
 CGI uses the HTTP protocol to transmit the
data, either as part of the query string or as
separate data following the MIME header.
© 2000 Elliotte Rusty Harold
7/17/2015
GET and POST
 When the data is sent as a query string
included with the file request, this is
called CGI GET.
 When the data is sent as data attached to
the request following the MIME header,
this is called CGI POST
© 2000 Elliotte Rusty Harold
7/17/2015
HTTP
 Web browsers communicate with web servers
through a standard protocol known as HTTP,
an acronym for HyperText Transfer Protocol.
 This protocol defines
– how a browser requests a file from a web server
– how a browser sends additional data along with
the request (e.g. the data formats it can accept),
– how the server sends data back to the client
– response codes
© 2000 Elliotte Rusty Harold
7/17/2015
A Typical HTTP Connection
– Client opens a socket to port 80 on the server.
– Client sends a GET request including the name
and path of the file it wants and the version of
the HTTP protocol it supports.
– The client sends a MIME header.
– The client sends a blank line.
– The server sends a MIME header
– The server sends the data in the file.
– The server closes the connection.
© 2000 Elliotte Rusty Harold
7/17/2015
What the client sends to the
server
GET /javafaq/images/cup.gif HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/3.01 (Macintosh; I; PPC)
Host: www.oreilly.com:80
Accept: image/gif, image/x-xbitmap, image/jpeg, */*
© 2000 Elliotte Rusty Harold
7/17/2015
MIME
 MIME is an acronym for "Multipurpose
Internet Mail Extensions".
 an Internet standard defined in RFCs
2045 through 2049
 originally intended for use with email
messages, but has been been adopted for
use in HTTP.
© 2000 Elliotte Rusty Harold
7/17/2015
Browser Request MIME
Header
 When the browser sends a request to a
web server, it also sends a MIME header.
 MIME headers contain name-value pairs,
essentially a name followed by a colon
and a space, followed by a value.
Connection: Keep-Alive
User-Agent: Mozilla/3.01 (Macintosh; I; PPC)
Host: www.digitalthink.com:80
Accept: image/gif, image/x-xbitmap,
image/jpeg, image/pjpeg, */*
© 2000 Elliotte Rusty Harold
7/17/2015
Server Response MIME
Header
 When a web server responds to a web
browser it sends a response message and
a MIME header along with the response
that looks something like this:
HTTP/1.0 200 OK
Server: Netscape-Enterprise/2.01
Date: Sat, 02 Aug 1997 07:52:46 GMT
Accept-ranges: bytes
Last-modified: Tue, 29 Jul 1997 15:06:46 GMT
Content-length: 2810
Content-type: text/html
© 2000 Elliotte Rusty Harold
7/17/2015
Query Strings
 CGI GET data is sent in URL encoded
query strings
 a query string is a set of name=value
pairs separated by ampersands
Author=Sadie, Julie&Title=Women Composers
 separated from rest of URL by a question
mark
© 2000 Elliotte Rusty Harold
7/17/2015
URL Encoding
 Alphanumeric ASCII characters (a-z, A-Z,
and 0-9) and the $-_.!*'(), punctuation
symbols are left unchanged.
 The space character is converted into a plus
sign (+).
 Other characters (e.g. &, =, ^, #, %, ^, {,
and so on) are translated into a percent sign
followed by the two hexadecimal digits
corresponding to their numeric value.
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
 The comma is ASCII character 44
(decimal) or 2C (hex). Therefore if the
comma appears as part of a URL it is
encoded as %2C.
 The query string "Author=Sadie,
Julie&Title=Women Composers" is
encoded as:
Author=Sadie%2C+Julie&Title=Women+Composers
© 2000 Elliotte Rusty Harold
7/17/2015
The URLEncoder class
 The java.net.URLEncoder class
contains a single static method which
encodes strings in x-www-form-urlencoded format
URLEncoder.encode(String s)
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
String qs = "Author=Sadie, Julie&Title=Women Composers";
String eqs = URLEncoder.encode(qs);
System.out.println(eqs);
 This prints:
Author%3dSadie%2c+Julie%26Title%3dWomen+Composers
© 2000 Elliotte Rusty Harold
7/17/2015
String eqs = "Author=" +
URLEncoder.encode("Sadie, Julie");
eqs += "&";
eqs += "Title=";
eqs += URLEncoder.encode("Women Composers");
 This prints the properly encoded query
string:
Author=Sadie%2c+Julie&Title=Women+Composers
© 2000 Elliotte Rusty Harold
7/17/2015
The URLDecoder class
 In Java 1.2 the
java.net.URLDecoder class contains
a single static method which decodes
strings in x-www-form-url-encoded
format
URLEncoder.decode(String s)
© 2000 Elliotte Rusty Harold
7/17/2015
GET URLs
String eqs =
"Author=" + URLEncoder.encode("Sadie, Julie");
eqs += "&";
eqs += "Title=";
eqs += URLEncoder.encode("Women Composers");
try {
URL u = new
URL("http://www.superbooks.com/search.cgi?" + eqs);
InputStream in = u.openStream();
//...
}
catch (IOException e) { //...
© 2000 Elliotte Rusty Harold
7/17/2015
URLConnections
 The java.net.URLConnection class
is an abstract class that handles
communication with different kinds of
servers like ftp servers and web servers.
 Protocol specific subclasses of
URLConnection handle different kinds
of servers.
 By default, connections to HTTP URLs
use the GET method.
© 2000 Elliotte Rusty Harold
7/17/2015
URLConnections vs. URLs
 Can send output as well as read input
 Can post data to CGIs
 Can read headers from a connection
© 2000 Elliotte Rusty Harold
7/17/2015
URLConnection five steps:
1. The URL is constructed.
2. The URL’s openConnection() method
creates the URLConnection object.
3. The parameters for the connection and the
request properties that the client sends to the
server are set up.
4. The connect() method makes the connection
to the server. (optional)
5. The response header information is read using
getHeaderField().
© 2000 Elliotte Rusty Harold
7/17/2015
I/O Across a URLConnection
 Data may be read from the connection in
one of two ways
– raw by using the input stream returned by
getInputStream()
– through a content handler with
getContent().
 Data can be sent to the server using the
output stream provided by
getOutputStream().
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
try {
URL u = new URL("http://www.wwwac.org/");
URLConnection uc = u.openConnection();
uc.connect();
InputStream in = uc.getInputStream();
// read the data...
}
catch (IOException e) { //...
© 2000 Elliotte Rusty Harold
7/17/2015
Reading Header Data
 The getHeaderField(String name)
method returns the string value of a named
header field.
 Names are case-insensitive.
 If the requested field is not present, null is
returned.
String lm = uc.getHeaderField("Last-modified");
© 2000 Elliotte Rusty Harold
7/17/2015
getHeaderFieldKey()
 The keys of the header fields are returned by
the getHeaderFieldKey(int n)
method.
 The first field is 1.
 If a numbered key is not found, null is
returned.
 You can use this in combination with
getHeaderField() to loop through the
complete header
© 2000 Elliotte Rusty Harold
7/17/2015
For example
String key = null;
for (int i=1; (key =
uc.getHeaderFieldKey(i))!=null); i++) {
System.out.println(key + ": " +
uc.getHeaderField(key));
}
© 2000 Elliotte Rusty Harold
7/17/2015
getHeaderFieldInt() and
getHeaderFieldDate()
 Utility methods that read a named header and
convert its value into an int and a long
respectively.
public int getHeaderFieldInt(String name, int default)
public long getHeaderFieldDate(String name, long
default)
© 2000 Elliotte Rusty Harold
7/17/2015
 The long returned by
getHeaderFieldDate() can be converted
into a Date object using a Date() constructor
like this:
long lm = uc.getHeaderFieldDate("Last-modified", 0);
Date lastModified = new Date(lm);
© 2000 Elliotte Rusty Harold
7/17/2015
Six Convenience Methods
 These return the values of six
particularly common header fields:
public
public
public
public
public
public
© 2000 Elliotte Rusty Harold
int
String
String
long
long
long
getContentLength()
getContentType()
getContentEncoding()
getExpiration()
getDate()
getLastModified()
7/17/2015
try {
URL u = new URL("http://www.sdexpo.com/");
URLConnection uc = u.openConnection();
uc.connect();
String key=null;
for (int n = 1;
(key=uc.getHeaderFieldKey(n)) != null;
n++) {
System.out.println(key + ": " +
uc.getHeaderField(key));
}
}
catch (IOException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
Writing data to a
URLConnection
 Similar to reading data from a URLConnection.
 First inform the URLConnection that you plan to
use it for output
 Before getting the connection's input stream, get
the connection's output stream and write to it.
 Commonly used to talk to CGIs that use the
POST method
© 2000 Elliotte Rusty Harold
7/17/2015
Eight Steps:
1.Construct the URL.
2.Call the URL’s openConnection() method
to create the URLConnection object.
3.Pass true to the URLConnection’s
setDoOutput() method
4.Create the data you want to send, preferably
as a byte array.
© 2000 Elliotte Rusty Harold
7/17/2015
5.Call getOutputStream() to get an
output stream object.
6.Write the byte array calculated in step 5
onto the stream.
7.Close the output stream.
8.Call getInputStream() to get an
input stream object. Read from it as
usual.
© 2000 Elliotte Rusty Harold
7/17/2015
POST CGIs
 A typical POST request to a CGI looks like
this:
POST /cgi-bin/booksearch.pl HTTP/1.0
Referer: http://www.macfaq.com/sampleform.html
User-Agent: Mozilla/3.01 (Macintosh; I; PPC)
Content-length: 60
Content-type: text/x-www-form-urlencoded
Host: utopia.poly.edu:56435
username=Sadie%2C+Julie&realname=Women+Composers
© 2000 Elliotte Rusty Harold
7/17/2015
A POST request includes
 the POST line
 a MIME header which must include
– content type
– content length
 a blank line that signals the end of the
MIME header
 the actual data of the form, encoded in xwww-form-urlencoded format.
© 2000 Elliotte Rusty Harold
7/17/2015
 A URLConnection for an http URL will
set up the request line and the MIME
header for you as long as you set its
doOutput field to true by invoking
setDoOutput(true).
 If you also want to read from the
connection, you should set doInput to
true with setDoInput(true) too.
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
URLConnection uc = u.openConnection();
uc.setDoOutput(true);
uc.setDoInput(true);
© 2000 Elliotte Rusty Harold
7/17/2015
 The request line and MIME header are
sent as soon as the URLConnection
connects. Then getOutputStream()
returns an output stream on which you
can write the x-www-form-urlencoded
name-value pairs.
© 2000 Elliotte Rusty Harold
7/17/2015
HttpURLConnection
 java.net.HttpURLConnection is
an abstract subclass of URLConnection
that provides some additional methods
specific to the HTTP protocol.
 URL connection objects that are returned
by an http URL will be instances of
java.net.HttpURLConnection.
© 2000 Elliotte Rusty Harold
7/17/2015
Recall
 a typical HTTP response from a web
server begins like this:
HTTP/1.0 200 OK
Server: Netscape-Enterprise/2.01
Date: Sat, 02 Aug 1997 07:52:46 GMT
Accept-ranges: bytes
Last-modified: Tue, 29 Jul 1997 15:06:46 GMT
Content-length: 2810
Content-type: text/html
© 2000 Elliotte Rusty Harold
7/17/2015
Response Codes
 The getHeaderField() and
getHeaderFieldKey() don't return the
HTTP response code
 After you've connected, you can retrieve the
numeric response code--200 in the above
example--with the getResponseCode()
method and the message associated with it-OK in the above example--with the
getResponseMessage() method.
© 2000 Elliotte Rusty Harold
7/17/2015
HTTP Protocols
 Java 1.0 only supports GET and POST
requests to HTTP servers
 Java 1.1/1.2 supports GET, POST, HEAD,
OPTIONS, PUT, DELETE, and TRACE.
 The protocol is chosen with the
setRequestMethod(String method)
method.
 A java.net.ProtocolException, a
subclass of IOException, is thrown if an
unknown protocol is specified.
© 2000 Elliotte Rusty Harold
7/17/2015
getRequestMethod()
 The getRequestMethod() method
returns the string form of the request
method currently set for the
URLConnection. GET is the default
method.
© 2000 Elliotte Rusty Harold
7/17/2015
disconnect()
 The disconnect() method of the
HttpURLConnection class closes the
connection to the web server.
 Needed for HTTP/1.1 Keep-alive
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
try {
URL u = new URL(
"http://www.metalab.unc.edu/javafaq/books.
html" );
HttpURLConnection huc =
(HttpURLConnection) u.openConnection();
huc.setRequestMethod("PUT");
huc.connect();
OutputStream os = huc.getOutputStream();
int code = huc.getResponseCode();
if (code >= 200 && < 300) {
// put the data...
}
huc.disconnect();
}
catch (IOException e) { //...
© 2000 Elliotte Rusty Harold
7/17/2015
usingProxy
 The boolean usingProxy() method
returns true if web connections are being
funneled through a proxy server, false if
they're not.
© 2000 Elliotte Rusty Harold
7/17/2015
Redirect Instructions
 Most web servers can be configured to
automatically redirect browsers to the
new location of a page that's moved.
 To redirect browsers, a server sends a 300
level response and a Location header that
specifies the new location of the requested
page.
© 2000 Elliotte Rusty Harold
7/17/2015
GET /~elharo/macfaq/index.html HTTP/1.0
HTTP/1.1 302 Moved Temporarily
Date: Mon, 04 Aug 1997 14:21:27 GMT
Server: Apache/1.2b7
Location: http://www.macfaq.com/macfaq/index.html
Connection: close
Content-type: text/html
<HTML><HEAD>
<TITLE>302 Moved Temporarily</TITLE>
</HEAD><BODY>
<H1>Moved Temporarily</H1>
The document has moved <A
HREF="http://www.macfaq.com/macfaq/index.html">he
re</A>.<P>
</BODY></HTML>
© 2000 Elliotte Rusty Harold
7/17/2015
 HTML is returned for browsers that
don't understand redirects, but most
modern browsers jump straight to the
page specified in the Location header
instead.
 Because redirects can change the site
which a user is connecting without their
knowledge so redirects are not arbitrarily
followed by URLConnections.
© 2000 Elliotte Rusty Harold
7/17/2015
Following Redirects
HttpURLConnection.setFollowRedirects
(true) method says that connections will follow
redirect instructions from the web server.
Untrusted applets are not allowed to set this.
HttpURLConnection.getFollowRedirects
() returns true if redirect requests are honored,
false if they're not.
© 2000 Elliotte Rusty Harold
7/17/2015
Datagrams
 Before data is sent across the Internet
from one host to another using TCP/IP, it
is split into packets of varying but finite
size called datagrams.
 Datagrams range in size from a few dozen
bytes to about 60,000 bytes.
 Packets larger than this, and often smaller
than this, must be split into smaller pieces
before they can be transmitted.
© 2000 Elliotte Rusty Harold
7/17/2015
Packets Allow Error
Correction
 If one packet is lost, it can be
retransmitted without requiring redelivery
of all other packets.
 If packets arrive out of order they can be
reordered at the receiving end of the
connection.
© 2000 Elliotte Rusty Harold
7/17/2015
Abstraction
 Datagrams are mostly hidden from the
Java programmer.
 The host's native networking software
transparently splits data into packets on
the sending end of a connection, and then
reassembles packets on the receiving end.
 Instead, the Java programmer is
presented with a higher level abstraction
called a socket.
© 2000 Elliotte Rusty Harold
7/17/2015
Sockets
 A socket is a reliable connection for the
transmission of data between two hosts.
 Sockets isolate programmers from the
details of packet encodings, lost and
retransmitted packets, and packets that
arrive out of order.
 There are limits. Sockets are more likely to
throw IOExceptions than files, for example.
© 2000 Elliotte Rusty Harold
7/17/2015
Socket Operations
 There are four fundamental operations a
socket performs. These are:
1. Connect to a remote machine
2. Send data
3. Receive data
4. Close the connection
 A socket may not be connected to more than
one host at a time.
 A socket may not reconnect after it's closed.
© 2000 Elliotte Rusty Harold
7/17/2015
The java.net.Socket class
 The java.net.Socket class allows you to
create socket objects that perform all four
fundamental socket operations.
 You can connect to remote machines; you can
send data; you can receive data; you can close
the connection.
 Each Socket object is associated with exactly
one remote host. To connect to a different host,
you must create a new Socket object.
© 2000 Elliotte Rusty Harold
7/17/2015
Constructing a Socket
 Connection is accomplished through the
constructors.
public Socket(String host, int port) throws
UnknownHostException, IOException
public Socket(InetAddress address, int port)
throws IOException
public Socket(String host, int port, InetAddress
localAddr, int localPort) throws IOException
public Socket(InetAddress address, int port,
InetAddress localAddr, int localPort) throws
IOException
© 2000 Elliotte Rusty Harold
7/17/2015
Opening Sockets
 The Socket() constructors do not just
create a Socket object. They also attempt
to connect the underlying socket to the
remote server.
 All the constructors throw an
IOException if the connection can't be
made for any reason.
© 2000 Elliotte Rusty Harold
7/17/2015
 You must at least specify the remote host and
port to connect to.
 The host may be specified as either a string like
"utopia.poly.edu" or as an InetAddress
object.
 The port should be an int between 1 and 65535.
Socket webMetalab = new Socket("metalab.unc.edu",
80);
© 2000 Elliotte Rusty Harold
7/17/2015
 You cannot just connect to any port on any
host. The remote host must actually be
listening for connections on that port.
 You can use the constructors to determine
which ports on a host are listening for
connections.
© 2000 Elliotte Rusty Harold
7/17/2015
public static void scan(InetAddress remote) {
String hostname = remote.getHostName();
for (int port = 0; port < 65536; port++) {
try {
Socket s = new Socket(remote, port);
System.out.println("There is a server on port "
+ port + " of " + hostname);
s.close();
}
catch (IOException e) {
// The remote host is not listening on this port
}
}
}
© 2000 Elliotte Rusty Harold
7/17/2015
Picking an IP address
 The last two constructors also specify the
host and port you're connecting from.
 On a system with multiple IP addresses,
like many web servers, this allows you to
pick your network interface and IP
address.
© 2000 Elliotte Rusty Harold
7/17/2015
Choosing a Local Port
 You can also specify a local port number,
 Setting the port to 0 tells the system to randomly
choose an available port.
 If you need to know the port you're connecting
from, you can always get it with
getLocalPort().
Socket webMetalab = new Socket("metalab.unc.edu",
80, "calzone.oit.unc.edu", 0);
© 2000 Elliotte Rusty Harold
7/17/2015
Sending and Receiving Data
 Data is sent and received with output and
input streams.
 There are methods to get an input stream for
a socket and an output stream for the socket.
public InputStream getInputStream() throws
IOException
public OutputStream getOutputStream() throws
IOException
 There's also a method to close a socket.
public synchronized void close() throws
IOException
© 2000 Elliotte Rusty Harold
7/17/2015
Reading Input from a Socket
 The getInputStream() method returns an
InputStream which reads data from the
socket.
 You can use all the normal methods of the
InputStream class to read this data.
 Most of the time you'll chain the input stream
to some other input stream or reader object to
more easily handle the data.
© 2000 Elliotte Rusty Harold
7/17/2015
For example
 The following code fragment connects to the
daytime server on port 13 of metalab.unc.edu,
and displays the data it sends.
try {
Socket s = new Socket("metalab.unc.edu", 13);
InputStream in = s.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String theTime = br.readLine();
System.out.println(theTime);
}
catch (IOException e) {
return (new Date()).toString();
}
© 2000 Elliotte
Rusty Harold
7/17/2015
Writing Output to a Socket
 The getOutputStream() method
returns an output stream which writes
data to the socket.
 Most of the time you'll chain the raw
output stream to some other output stream
or writer class to more easily handle the
data.
© 2000 Elliotte Rusty Harold
7/17/2015
Discard
byte[] b = new byte[128];
try {
Socket s = new Socket("metalab.unc.edu", 9);
OutputStream theOutput = s.getOutputStream();
while (true) {
int n = theInput.available();
if (n > b.length) n = b.length;
int m = theInput.read(b, 0, n);
if (m == -1) break;
theOutput.write(b, 0, n);
}
s.close();
}
catch (IOException e) {}
© 2000 Elliotte Rusty Harold
7/17/2015
Reading and Writing to a
Socket
 It's unusual to only read from a socket. It's
even more unusual to only write to a
socket.
 Most protocols require the client to both
read and write.
© 2000 Elliotte Rusty Harold
7/17/2015
 Some protocols require the reads and the
writes to be interlaced. That is:
–
–
–
–
–
–
write
read
write
read
write
read
© 2000 Elliotte Rusty Harold
7/17/2015
 Other protocols, such as HTTP 1.0, have
multiple writes, followed by multiple
reads, like this:
–
–
–
–
–
–
–
write
write
write
read
read
read
read
© 2000 Elliotte Rusty Harold
7/17/2015
 Other protocols don't care and allow client
requests and server responses to be freely
intermixed.
 Java places no restrictions on reading and
writing to sockets.
 One thread can read from a socket while
another thread writes to the socket at the
same time.
© 2000 Elliotte Rusty Harold
7/17/2015
try {
URL u = new URL(args[i]);
if (u.getPort() != -1) port = u.getPort();
if (!(u.getProtocol().equalsIgnoreCase("http"))) {
System.err.println("I only understand http.");
}
Socket s = new Socket(u.getHost(), u.getPort());
OutputStream theOutput = s.getOutputStream();
PrintWriter pw = new PrintWriter(theOutput, false);
pw.print("GET " + u.getFile() + " HTTP/1.0\r\n");
pw.print("Accept: text/plain, text/html, text/*\r\n");
pw.print("\r\n");
pw.flush();
InputStream in = s.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
int c;
while ((c = br.read()) != -1) {
System.out.write(c);
}
}
© 2000 Elliotte Rusty Harold
7/17/2015
catch (MalformedURLException
e) {
Socket Options
 Several methods set various socket options.
Most of the time the defaults are fine.
public void setTcpNoDelay(boolean on) throws
SocketException
public boolean getTcpNoDelay() throws SocketException
public void setSoLinger(boolean on, int val) throws
SocketException
public int getSoLinger() throws SocketException
public synchronized void setSoTimeout(int timeout)
throws SocketException
public synchronized int getSoTimeout() throws
SocketException
© 2000 Elliotte Rusty Harold
7/17/2015
 These methods to return information
about the socket:
public
public
public
public
InetAddress getInetAddress()
InetAddress getLocalAddress()
int getPort()
int getLocalPort()
 Finally there's the usual toString()
method:
public String toString()
© 2000 Elliotte Rusty Harold
7/17/2015
Servers
 There are two ends to each connection: the
client, that is the host that initiates the
connection, and the server, that is the host
that responds to the connection.
 Clients and servers are connected by sockets.
 A server, rather than connecting to a remote
host, a program waits for other hosts to
connect to it.
© 2000 Elliotte Rusty Harold
7/17/2015
Server Sockets
 A server socket binds to a particular port on
the local machine.
 Once it has successfully bound to a port, it
listens for incoming connection attempts.
 When a server detects a connection attempt, it
accepts the connection. This creates a socket
between the client and the server over which
the client and the server communicate.
© 2000 Elliotte Rusty Harold
7/17/2015
Multiple Clients
 Multiple clients can connect to the same port on
the server at the same time.
 Incoming data is distinguished by the port to
which it is addressed and the client host and port
from which it came.
 The server can tell for which service (like http or
ftp) the data is intended by inspecting the port.
 It can tell which open socket on that service the
data is intended for by looking at the client
address and port stored with the data.
© 2000 Elliotte Rusty Harold
7/17/2015
Queueing
 Incoming connections are stored in a
queue until the server can accept them.
 On most systems the default queue length
is between 5 and 50.
 Once the queue fills up further incoming
connections are refused until space in the
queue opens up.
© 2000 Elliotte Rusty Harold
7/17/2015
The java.net.ServerSocket
Class
 The java.net.ServerSocket class
represents a server socket.
 A ServerSocket object is constructed on a
particular local port. Then it calls accept()
to listen for incoming connections.
 accept() blocks until a connection is
detected. Then accept() returns a
java.net.Socket object that performs the
actual communication with the client.
© 2000 Elliotte Rusty Harold
7/17/2015
Constructors
 There are three constructors that let you
specify the port to bind to, the queue
length for incoming connections, and the
IP address to bind to:
public ServerSocket(int port) throws IOException
public ServerSocket(int port, int backlog)
throws IOException
public ServerSocket(int port, int backlog,
InetAddress networkInterface) throws
IOException
© 2000 Elliotte Rusty Harold
7/17/2015
Constructing Server Sockets
 Normally you only specify the port you
want to listen on, like this:
try {
ServerSocket ss = new ServerSocket(80);
}
catch (IOException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
 When a ServerSocket object is created, it
attempts to bind to the port on the local host
given by the port argument.
 If another server socket is already listening to
the port, then a
java.net.BindException, a subclass of
IOException, is thrown.
 No more than one process or thread can listen
to a particular port at a time. This includes
non-Java processes or threads.
 For example, if there's already an HTTP server
running on port 80, you won't be able to bind
© 2000 Elliotte Rusty Harold
7/17/2015
 On Unix systems (but not Windows or the
Mac) your program must be running as root to
bind to a port between 1 and 1023.
 0 is a special port number. It tells Java to pick
an available port.
 The getLocalPort() method tells you what
port the server socket is listening on. This is
useful if the client and the server have already
established a separate channel of
communication over which the chosen port
number can be communicated.
 FTP
© 2000 Elliotte Rusty Harold
7/17/2015
Expanding the Queue
 If you think you aren't going to be
processing connections very quickly you may
wish to expand the queue when you
construct the server socket. For example,
try {
ServerSocket httpd = new ServerSocket(80,
50);
}
catch (IOException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
Choosing an IP address
 Many hosts have more than one IP
address.
 By default, a server socket binds to all
available IP addresses on a given port.
 You can modify that behavior with this
constructor:
public ServerSocket(int port, int backlog,
InetAddress bindAddr)throws IOException
© 2000 Elliotte Rusty Harold
7/17/2015
Example
try {
InetAddress ia =
InetAddress.getByName("199.1.32.90");
ServerSocket ss = new ServerSocket(80, 50, ia);
}
catch (IOException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
 On a server with multiple IP addresses,
the getInetAddress() method tells
you which one this server socket is
listening to.
public InetAddress getInetAddress()
 The getLocalPort() method tells you
which port you're listening to.
public int getLocalPort()
© 2000 Elliotte Rusty Harold
7/17/2015
 The accept() and close() methods
provide the basic functionality of a server
socket.
public Socket accept() throws IOException
public void close() throws IOException
 A server socket can’t be reopened after it’s
closed
© 2000 Elliotte Rusty Harold
7/17/2015
Reading Data with a
ServerSocket
 ServerSocket objects use their accept()
method to connect to a client.

public Socket accept() throws IOException
 There are no getInputStream() or
getOutputStream() methods for
ServerSocket.
 accept() returns a Socket object, and its
getInputStream() and
getOutputStream() methods provide
streams.
© 2000 Elliotte Rusty Harold
7/17/2015
Example
try {
ServerSocket ss = new ServerSocket(2345);
Socket s = ss.accept();
PrintWriter pw = new
PrintWriter(s.getOutputStream());
pw.println("Hello There!");
pw.println("Goodbye now.");
s.close();
}
catch (IOException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
Better Example
try {
ServerSocket ss = new ServerSocket(2345);
Socket s = ss.accept();
PrintWriter pw = new
PrintWriter(s.getOutputStream());
pw.print("Hello There!\r\n");
pw.print("Goodbye now.\r\n");
s.close();
}
catch (IOException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
Writing Data to a Client
try {
ServerSocket ss = new ServerSocket(port);
while (true) {
try {
Socket s = ss.accept();
PrintWriter pw = new PrintWriter(s.getOutputStream());
pw.print("Hello " + s.getInetAddress() + " on port "
+ s.getPort() + "\r\n");
pw.print("This is " + s.getLocalAddress() + " on port
"
+ s.getLocalPort() + "\r\n");
pw.flush();
s.close();
}
catch (IOException e) {}
}
}
catch
(IOException
e) { System.err.println(e);
}
©
2000 Elliotte
Rusty Harold
7/17/2015
Interacting with a Client
 More commonly, a server needs to both
read a client request and write a response.
© 2000 Elliotte Rusty Harold
7/17/2015
Threading
 No more than one server socket can listen
to a particular port at one time.
 Since a server may need to handle many
connections at once, server programs tend
to be heavily multi-threaded.
 Generally the server socket passes off the
actual processing of connections to a
separate thread.
© 2000 Elliotte Rusty Harold
7/17/2015
Adding Threading to a Server
 It's better to make your server multithreaded.
 There should be a loop which continually
accepts new connections.
 Rather than handling the connection
directly the socket should be passed to a
Thread object that handles the
connection.
© 2000 Elliotte Rusty Harold
7/17/2015
Adding a Thread Pool to a
Server
 Multi-threading is a good thing but it's still not a
perfect solution.
 Look at this accept loop:
while (true) {
try {
Socket s = ss.accept();
ThreadedEchoServer tes = new ThreadedEchoServer(s)
tes.start();
}
catch (IOException e) {}
© 2000 Elliotte Rusty Harold
7/17/2015
 Every pass through this loop, a new thread
gets created. Every time a connection is
finished the thread is disposed of.
 Spawning a new thread for each
connection takes a non-trivial amount of
time, especially on a heavily loaded server.
 Too many simultaneous threads overload a
VM.
© 2000 Elliotte Rusty Harold
7/17/2015
Thread Pools
 Create a pool of threads when the server
launches, store incoming connections in a
queue, and have the threads in the pool
progressively remove connections from the
queue and process them.
 The main change you need to make to
implement this is to call accept() in the
run() method rather than in the main()
method.
© 2000 Elliotte Rusty Harold
7/17/2015
Setting Server Socket
Options
 There are three methods to set and get
various options. The defaults are generally
fine.
public synchronized void setSoTimeout(int
timeout) throws SocketException
public synchronized int getSoTimeout() throws
IOException
public static synchronized void
setSocketFactory(SocketImplFactory fac)
throws IOException
© 2000 Elliotte Rusty Harold
7/17/2015
Utility Methods
 Finally, there's the usual toString()
method:
public String toString()
© 2000 Elliotte Rusty Harold
7/17/2015
UDP
 Unreliable Datagram Protocol
 Packet Oriented, not stream oriented like
TCP/IP
 Much faster but no error correction
 NFS, TFTP, and FSP use UDP/IP
 Must fit data into packets of about 8K or
less
© 2000 Elliotte Rusty Harold
7/17/2015
The UDP Classes
 Java's support for UDP is contained in two
classes:
java.net.DatagramSocket
java.net.DatagramPacket
 A datagram socket is used to send and
receive datagram packets.
© 2000 Elliotte Rusty Harold
7/17/2015
java.net.DatagramPacket
 a wrapper for an array of bytes from
which data will be sent or into which data
will be received.
 also contains the address and port to
which the packet will be sent.
© 2000 Elliotte Rusty Harold
7/17/2015
java.net.DatagramSocket
 A DatagramSocket object is a local
connection to a port that does the sending and
receiving.
 There is no distinction between a UDP socket
and a UDP server socket.
 Also unlike TCP sockets, a DatagramSocket
can send to multiple, different addresses.
 The address to which data goes is stored in the
packet, not in the socket.
© 2000 Elliotte Rusty Harold
7/17/2015
UDP ports
 Separate from TCP ports.
 Each computer has 65,536 UDP ports as
well as its 65,536 TCP ports.
 A server socket can be bound to TCP port
20 at the same time as a datagram socket
is bound to UDP port 20.
© 2000 Elliotte Rusty Harold
7/17/2015
Two DatagramPacket
Constructors
public DatagramPacket(byte[] data,
int length)
public DatagramPacket(byte[] data,
int length, InetAddress remote,
int port)
 First is for receiving, second is for sending
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
String s = "My first UDP Packet";
byte[] b = s.getBytes();
DatagramPacket dp = new DatagramPacket(b, b.length);
© 2000 Elliotte Rusty Harold
7/17/2015
With a destination:
try {
InetAddress metalab =
InetAddress.getByName("metalab.unc.edu");
int chargen = 19;
String s = "My second UDP Packet";
byte[] b = s.getBytes();
DatagramPacket dp = new DatagramPacket(b,
b.length, metalab, chargen);
}
catch (UnknownHostException e) {
System.err.println(e);
}
© 2000 Elliotte Rusty Harold
7/17/2015
DatagramPackets are not
immutable.
public synchronized void
setAddress(InetAddress remote)
public synchronized void setPort(int port)
public synchronized void setData(byte[] data)
public synchronized void setLength(int
length)
public synchronized InetAddress getAddress()
public synchronized int getPort()
public synchronized byte[] getData()
public synchronized int getLength()
 These methods are primarily useful when
you're receiving datagrams.
© 2000 Elliotte Rusty Harold
7/17/2015
java.net.DatagramSocket
public DatagramSocket() throws
SocketException
public DatagramSocket(int port) throws
SocketException
public DatagramSocket(int port, InetAddress
laddr) throws SocketException
 The first is for client datagram sockets;
that is sockets that send datagrams before
receiving any.
 The second two are for server datagram
sockets since they specify the port and
optionally the IP address of the socket
© 2000 Elliotte Rusty Harold
7/17/2015
Sending UDP Datagrams
 To send data to a particular server
– Convert the data into byte array.
– Pass this byte array, the length of the data in
the array (most of the time this will be the
length of the array) and the InetAddress
and port to which you wish to send it into the
DatagramPacket() constructor.
– Next create a DatagramSocket and pass the
packet to its send() method
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
InetAddress metalab =
InetAddress.getByName("metalab.unc.edu");
int chargen = 19;
String s = "My second UDP Packet";
byte[] b = s.getBytes();
DatagramPacket dp = new DatagramPacket(b,
b.length, ia, chargen);
DatagramSocket sender = new DatagramSocket();
sender.send(dp);
© 2000 Elliotte Rusty Harold
7/17/2015
Receiving UDP Datagrams
 Construct a DatagramSocket object on
the port on which you want to listen.
 Pass an empty DatagramPacket object to
the DatagramSocket's receive()
method.
public synchronized void receive(DatagramPacket
dp) throws IOException
 The calling thread blocks until a datagram is
received.
© 2000 Elliotte Rusty Harold
7/17/2015
 dp is filled with the data from that
datagram.
 Use getPort() and and getAddress()
to tell where the packet came from,
getData() to retrieve the data, and
getLength() to see how many bytes were
in the data.
 If the received packet was too long for the
buffer, it's truncated to the length of the
buffer.
© 2000 Elliotte Rusty Harold
7/17/2015
For example,
try {
byte buffer = new byte[65536];
DatagramPacket incoming = new
DatagramPacket(buffer, buffer.length);
DatagramSocket ds = new
DatagramSocket(2134);
ds.receive(incoming);
byte[] data = incoming.getData();
String s = new String(data, 0,
data.getLength());
System.out.println("Port " +
incoming.getPort() + " on " +
incoming.getAddress() + " sent this
message:");
System.out.println(s);
}
catch (IOException e) {
System.err.println(e);
© 2000 Elliotte Rusty Harold
7/17/2015
}
Receiving Multiple Packets
 Watch out for strictly decreasing packet
sizes
DatagramPacket incoming = new
DatagramPacket(new byte[8192], 8192);
DatagramSocket ds = new
DatagramSocket(2134);
while (true) {
try {
incoming.setLength(8192);
ds.receive(incoming);
byte[] data = incoming.getData();
String s = new String(data, 0,
data.getLength());
System.out.println("Port
" +
© 2000 Elliotte Rusty Harold
7/17/2015
To Learn More
 Java Network Programming
– O’Reilly & Associates, 1997
– ISBN 1-56592-227-1
 Java I/O
– O’Reilly & Associates, 1999
– ISBN 1-56592-485-1
 Web Client Programming with Java
– http://www.digitalthink.com/catalog/cs/cs
308/index.html
© 2000 Elliotte Rusty Harold
7/17/2015
Questions?
© 2000 Elliotte Rusty Harold
7/17/2015

similar documents