Sockets Notes
Sockets Notes
Network Programming
Topics in this section include:
• What a socket is
Introduction
The Internet is all about connecting machines together. One of the most exciting
aspects of Java is that it incorporates an easy-to-use, cross-platform model for
network communications that makes it possible to learn network programming
without years of study. This opens up a whole new class of applications to
programmers.
What is a Socket?
Java's socket model is derived from BSD (UNIX) sockets, introduced in the early
1980s for interprocess communication using IP, the Internet Protocol.
The Internet Protocol breaks all communications into packets, finite-sized chunks
of data which are separately and individually routed from source to destination. IP
allows routers, bridges, etc. to drop packets--there is no delivery guarantee. Packet
size is limited by the IP protocol to 65535 bytes. Of this, a minimum of 20 bytes
is needed for the IP packet header, so there is a maximum of 65515 bytes available
for user data in each packet.
Other common protocols are layered on top of the Internet protocol. The ones we
discuss in this chapter are the User Datagram Protocol (UDP) and the
Transmission Control Protocol (TCP). Applications can make use of these two
protocols to communicate over the network, commonly by building additional
protocols on top of the TCP or UDP base.
TCP UDP
IP
Internet Addresses
Internet addresses are manipulated in Java by the use of the InetAddress class.
InetAddress takes care of the Domain Name System (DNS) look-up and reverse
look-up; IP addresses can be specified by either the host name or the raw IP
address. InetAddress provides methods to getByName(), getAllByName(),
getLocalHost(), getAddress(), etc.
IP Addresses
Class A
Class B
Class C
Class D
Class E
Port number
Java does not have any unsigned data types; Java's short data type is 16 bits, but
its range is -32768 to 32767 since it is a signed type. Thus, short is not large
enough to hold a port number, so all classes which use or return a port number
must represent the port number as an int. In the JDK 1.1, using an int with a
value greater than 65535 will generate an IllegalArgumentException. In the
JDK 1.0.2 and earlier, values greater than 65535 are truncated and only the low-
order 16 bits are used.
Port numbers 1 through 255 are reserved by IP for well-known services. A well-
known service is a service that is widely implemented which resides at a
published, "well-known", port. If you connect to port 80 of a host, for instance,
you may expect to find an HTTP server. On UNIX machines, ports less than
1024 are privileged and can only be bound by the root user. This is so an arbitrary
user on a multi-user system can't impersonate well-known services like TELNET
(port 23), creating a security problem. Windows has no such restrictions, but you
should program as if it did so that your applications will work cross-platform.
Client/Server Computing
You can use the Java language to communicate with remote file systems using a
client/server model. A server listens for connection requests from clients across
the network or even from the same machine. Clients know how to connect to the
server via an IP address and port number. Upon connection, the server reads the
request sent by the client and responds appropriately. In this way, applications
can be broken down into specific tasks that are accomplished in separate
locations.
The data that is sent back and forth over a socket can be anything you like.
Normally, the client sends a request for information or processing to the server,
which performs a task or sends data back. You could, for example, place an SQL
shell on the server and let people talk to it via a simple client "chat" program.
The IP and port number of the server is generally well-known and advertised so
the client knows where to find the service. In contrast, the port number on client
the side is generally allocated automatically by the kernel.
Many protocols used on the Internet (HTTP for example) are designed to be
driven from the command line. They send their requests and responses across the
net in plain text. One of the easiest ways to become familiar with network
programming and/or specific protocols is to use the TELNET application to "talk"
directly to a server from the command line.
UDP packets can arrive out of order or not at all. No packet has any knowledge of
the preceding or following packet. The recipient does not acknowledge packets, so
the sender does not know that the transmission was successful. UDP has no
provisions for flow control--packets can be received faster than they can be used.
We call this type of communication connectionless because the packets have no
relationship to each other and because there is no state maintained.
The destination IP address and port number is encapsulated in each UDP packet.
These two numbers together uniquely identify the recipient and are used by the
underlying operating system to deliver the packet to a specific process
(application).
One way to think of UDP is by analogy to communications via a letter. You write
the letter (this is the data you are sending); put the letter inside an envelope (the
UDP packet); address the envelope (using an IP address and a port number); put
your return address on the envelope (your local IP address and port number); and
then you send the letter.
Like a real letter, you have no way of knowing whether a UDP packet was
received. If you send a second letter one day after the first, the second one may be
received before the first. Or, the second one may never be received.
So why use UDP if it unreliable? Two reasons: speed and overhead. UDP packets
have almost no overhead--you simply send them then forget about them. And
they are fast, since there is no acknowledgement required for each packet. Keep in
mind the degree of unreliability we are talking about. For all practical purposes, an
Ethernet breaks down if more than about 2 percent of all packets are lost. So,
when we say unreliable, the worst-case loss is very small.
UDP is appropriate for the many network services that do not require guaranteed
delivery. An example of this is a network time service. Consider a time daemon
that issues a UDP packet every second so computers on the LAN can
synchronize their clocks. If a packet is lost, it's no big deal--the next one will be
by in another second and will contain all necessary information to accomplish the
task.
3. Block until a packet is received, then extract the information you need from the
packet.
// Block on receive()
socket.receive(packet);
The server can now process the data it has received from the client, and issue an
appropriate reply in response to the client's request.
1. First allocate space to hold the data we are sending and create an instance of
DatagramPacket to hold the data.
The DatagramSocket constructor that takes no arguments will allocate a free local
port to use. You can find out what local port number has been allocated for your
socket, along with other information about your socket if needed.
The client then waits for a reply from the server. Many protocols require the
server to reply to the host and port number that the client used, so the client can
now invoke socket.receive() to wait for information from the server.
Again, unlike UDP, the destination host and port number is not sufficient to
identify a recipient of a TCP connection. There are five distinct elements that
make a TCP connection unique:
where each requested client socket is assigned a unique port number whereas the
server port number is always the same. If any of these numbers is different, the
socket is different. A server can thus listen to one and only one port, and talk to
multiple clients at the same time.
So a TCP connection is more like a telephone connection than a letter; you need to
know not only the phone number (IP address), but since the phone may be shared
by many people at that location, you also need the name of the user you want to
talk to at the other end (port number). The analogy can be taken a little further. If
you don't hear what the other person has said, a simple request ("What?") will
prompt the other end to resend or repeat the phrase. And, the connection remains
open until someone hangs up.
// Block on accept()
Socket channel = server.accept();
Now you can read and write to the socket, thus, communicating with the client.
When a server invokes the accept() method of the ServerSocket instance, the
main server thread blocks until a client connects to the server; it is then prevented
from accepting further client connections until the server has processed the client's
request. This is known as an iterative server, since the main server method handles
each client request in its entirety before moving on to the next request. Iterative
servers are good when the requests take a known, short period of time. For
example, requesting the current day and time from a time-of-day server.
Now you can read and write to the socket, thus, communicating with the server.
To overcome this problem with iterative servers, a separate thread can handle each
client session, allowing the server to deal with multiple clients simultaneously.
This is known as a concurrent server--the main server method launches a thread to
handle each client request, then continues to listen for additional clients.
Multithreaded Clients
In the previous section we learned why we needed to use threads in our server if
we wanted to simultaneously deal with multiple clients. It is not so clear why we
would need more than one thread for our client, but we generally do. The reason
for this is that the I/O operations we perform on a socket generally block. So the
client can read or write to the socket, but not both at the same time. This poses a
problem when the client does not know ahead of time how much data the server is
going to send back in response to the client's request. How many times should the
client call readLine()?
Having one thread to read and one thread to write, we can ensure that we never
enter into a situation where the client fails to read the entire server response, or
where the client is stuck waiting for too much data from the server.
Instead of creating two threads within your client, a more common scenario would
be to take advantage of the fact that the AWT handles user input in a separate
thread; you can receive user input and send data over the socket from an AWT
event handling method, while your client class receives data in its run() method.
Sending Objects
TCP is a stream protocol; we can do anything with socket streams that we can
with file streams. One way we can greatly simplify communications between
clients and servers written in Java is to use an ObjectInputStream and
ObjectOutputStream combination to read and write objects across the socket.
This is a higher level of abstraction--we don't have to write int or float data
types, but we can send a whole object. For example:
1. First, define an object to send. We'll define a class called Message to encapsulate
our communications.
2. Next, instantiate the object; wrap the socket's streams in object streams; and then
send the message across the socket.
out.writeObject(sayhey);
3. On the other side of the socket, the message can be retrieved and used by invoking
methods on the returned object.
Multicast Protocol
TCP and UDP are both unicast protocols; there is one sender and one receiver.
Multicast packets are a special type of UDP packets. But while UDP packets
have only one destination and only one receiver, multicast packets can have an
arbitrary number of receivers.
Multicast is quite distinct from broadcast; with broadcast packets, every host on
the network receives the packet. With multicast, only those hosts that have
registered an interest in receiving the packet get it.
This is similar to the way an AWTEvent and it's listeners behave in the AWT. In
the same way that an AWTEvent is sent only to registered listeners, a multicast
packet is sent only to members of the multicast group. AWTEvents, however, are
unicast, and must be sent individually to each listener--if there are two listeners,
two events are sent. With a MulticastSocket, only one is sent and it is received
by many.
A Multicast Application
Because of the nature of multicast, it doesn't fit well into the client/server model.
A "server" doesn't know how many clients (if any!) are subscribed to the
multicast group. And the whole idea of multicast is to send only one packet, not
to send packets tailored to each individual client. If the server needs to respond to
2. Next, we create a DatagramPacket and send it over the socket, just as we did in
the UDP section. This packet will be sent to every host which has joined this
group.
4. When we no longer wish to receive packets from a group, we must leave the
group.
socket.leaveGroup(group);
setTTL()) controls how many hops it can travel. This is especially important
when programming multicast applications, because you don't want to send your
packets to every host in the world--you want to restrict your packets to certain
places. For example, you might want to make sure that the packets are restricted
to you local LAN, so you set TTL to be one or two. Or, if you are writing a
multicast application for a WAN, you might need to set TTL to be 10 or more.
Neither of these books has anything to do with Java, but either presents a solid
base of understanding for network programming. Since Java's socket model derives
directly from BSD UNIX, if you read the material in these books, the java.net
package will be easily understandable.
[MML: 0.995a]
[Version: $Id: //depot/main/src/edu/modules/Sockets/sockets.mml#3 $]