| Home > Core Java FAQ
> Networking FAQ |
| Networking |
| URL
Connections(16) * Internet
Addresses(14) * Sockets(23)
* Security(01) * Miscellaneous
(14) |
| |
|
Q . How do I create and use sockets in Java?
|
Ans :
Create Socket
instances on the client side by specifying a host and port number
to connect to, and create ServerSocket
instances on the server side by specifying a port number at which
to wait for new connections.
Sockets
work in pairs, with a built-in asymmetry between client and
server. The Socket class in the java.net
package represents a client socket. It is always created with a
communication target: a communications port number on a specific
host machine. The client socket typically initiates interactions.
It sends messages over the net to its target, expecting that the
right kind of program will be waiting at the specified port, ready
to respond to the client's message.
The server socket, represented by
the ServerSocket class, plays this second,
wait-and-respond role. A server socket need only specify a port
number to listen at; it doesn't have to know in advance to where
it will send messages. For each incoming message, it sends its
reply back to the source address indicated in the message. In this
way, a single process at a server socket can reply to many
different clients on different machines.
The key to using sockets is to keep
in mind that they provide only a low-level abstraction of a
communication channel as a stream of bytes. The SocketExample
sample code, for instance, shows how to pack a Java String
instance into an array of bytes and then write that array to the
output stream of the socket. To receive data from the server, a
useful strategy is to wrap a DataInputStream instance
around the client socket's input stream and then use an
appropriate read method for the data type(s) you expect.
|
|
Q .
How can I get a server socket and client socket going on a stand-alone machine, not on a network?
|
Ans
:
In the JDK
1.1 (but not 1.0.2, due to a bug), you can use the loopback
interface—IP address 127.0.0.1—for socket communication
without a network.
Creating
a Socket instance requires that you specify the host
to connect to, either by name or as an InetAddress
instance. Standard TCP/IP practice reserves a special IP
address—127.0.0.1—as the loopback interface.
Data packets sent to this address don't actually leave the
machine; they are passed by software to the same machine's
receiving ports, as if they had come in off the network. Thus, you
can use this loopback address to test or run network code, even if
your machine is not on a network.
Using the loopback interface affects
code only for the client socket. It must create a Socket
instance using "127.0.0.1" as the target
address:
InetAddress loopback = InetAddress.getByName("127.0.0.1");
connection = new Socket(loopback, SERVER_PORT);
No special code is
needed for a server socket. As always, it uses whatever address
the client socket is connecting from.
The above code works in the JDK 1.1,
but a bug in the JDK 1.0.2 implementation prevents loopback
connections from working if your machine isn't on a network. In
particular, InetAddress's getByName
method in 1.0.2 requires that domain name lookup succeed before it
returns an InetAddress instance. This is a bug, not a
feature, and it prevents you from creating an InetAddress
instance by means of the "127.0.0.1"
address.
|
|
Q . Will raw sockets ever be introduced—and if not, how can I create ICMP messages so that I can do operations like `ping'?
|
Ans
:
Raw sockets
are not currently scheduled for an upcoming release, and without
them, your only option for such low-level network access is to
write your own native methods.
The
socket operations provided by the java.net classes
deliberately shelter you from the lower layers of the TCP/IP
protocol stack. Once you create a socket, you only have to worry
about sending and receiving data through it. The nitpicky details
of the lower transport layers, such as IP addresses, packet
sequence numbers, and packet checksums are managed by system code.
For the great majority of applications, this division of labor is
desirable.
For a small set of applications,
though, the higher-level abstraction provided by sockets shields
the very data—the header bytes in an individual packet—that
the application needs to control. The UNIX "ping"
program, for instance, manipulates individual packets at the level
of IP headers, in order to send packets conforming to the Internet
Control Message Protocol (ICMP). You simply can't write a ping
program using Java socket-related classes. For network control at
this level, you need to write native methods.
|
|
Q .
How do I detect if an input stream belonging to a socket has been terminated by the remote host?
|
Ans
:
Check the
return value of your read
(or readLine, etc.) method; if the
remote host has terminated the socket input stream from its end,
your invocation of read should return an end-of-stream value.
Like
other input streams, a socket's input stream signals the end of
its data by returning the standard end-of-stream value, -1,
from its read methods. The end of a socket's input
stream is determined by the remote host: the remote host marks the
end of its data by closing the connection (either the data stream
or the whole socket) at its end. On the local side, your program
may not detect the closing right away because it may still read in
some buffered data first, but your program will eventually detect
the end-of-stream mark.
The base InputStream
class returns -1 from its read methods to signal
end-of-stream. Because you can build other types of input streams
or readers on top of the basic input stream, you need to check for
the appropriate end-of-stream marker for the specific type you're
using: -1 for read methods that return int and null
for readLine methods that return String.
Note: InputStream's
available method cannot detect the closing of an input stream—it
merely reports how many bytes can be read from the input stream
without blocking. In other words, it basically tells you how many
bytes are in the TCP buffer on the local receiving end of the
current connection; it does not indicate the state of the remote
host.
|
|
Q . If the Socket class has no support for firewalls, how can I get my Java program with sockets to work through different firewalls?
|
Ans
:
Define a SocketImpl
subclass that can work through your firewall, and use Socket's
setSocketImplFactory method to
configure your application with that SocketImpl
subclass.
The
Socket class provides a front-end interface for
socket operations. It lets you open and close socket connections,
obtain input and output streams from the socket, and so on.
Although you invoke methods on a Socket object, that
object does very little of the work. Instead, it delegates
practically all of the implementation details to a separate
implementation object, which belongs to a subclass of SocketImpl.
The Socket getOutputStream method in the JDK (1.0.2
and 1.1), for example, shows how simple the delegation can be:
/* in Socket.java (JDK 1.0.2 and 1.1): */
public OutputStream getOutputStream() throws IOException {
return impl.getOutputStream();
}
Building in the
back-end know-how for communicating through firewalls requires
several steps:
- define your own
SocketImpl
subclass; override methods like bind, listen,
accept, and connect
- define a class
that implements the
SocketImplFactory interface;
this interface contains a single method createSocketImpl(),
which returns a SocketImpl instance
- invoke
Socket's
setSocketImplFactory method, providing your SocketImplFactory
object as the argument
If
you don't specify a SocketImplFactory, the JDK (1.0.2
and 1.1) provides an instance of PlainSocketImpl (a
nonpublic class in java.net) for you. This
implementation class does not conduct security checks and knows
how to handle only SOCKS firewalls.
|
|
Q .
How do I create a nonblocking server socket in Java?
|
Ans :
You can't,
exactly, but you can achieve the same affect by using a separate
thread to run ServerSocket's
accept method.
A
ServerSocket instance has only one way to wait for
incoming connections: by invoking the ServerSocket
accept method. The accept method is defined to be a blocking call,
which means that the thread invoking it will block further
execution until a connection is made. If you want to avoid
blocking some central thread in your server code, you should have
a separate thread run the accept method. The following code
fragment illustrates one approach:
class ServerThread extends Thread {
ServerSocket listenSocket;
Socket acceptSocket;
/**
* Creates a new server thread that will listen for a connection
* at the specified socket.
*/
public ServerThread(ServerSocket socket) {
listenSocket = socket;
}
/**
* Accepts a connection and then starts another server thread
* to wait for the next connection while this thread runs
* to completion.
*/
public void run() {
acceptSocket = listenSocket.accept();
new ServerThread(listenSocket).start();
// ... handle current connection
}
// ...
}
|
|
Q . Is there a standard Java way to specify the address of the host doing the listening when instantiating a ServerSocket?
|
Ans
:
In the JDK
1.0.2, you can't specify a local address for a server socket to
listen at, but the JDK 1.1 fills this gap by providing an
additional ServerSocket
constructor taking an InetAddress
parameter.
The
JDK 1.0.2 provides two public constructors for the ServerSocket
class. When creating a ServerSocket instance, you
must specify the port number to listen at for connections, and you
can optionally specify a maximum size for the waiting list of
connect requests waiting to be accepted:
ServerSocket(int port)
ServerSocket(int port, int backlog)
In the JDK 1.0.2,
there is no way to specify that the server socket should listen on
a specific IP address. This restriction can be an issue on "multihomed"
machines, that is, machines with more than one IP address.
Thanks to strong demand, the JDK 1.1
filled this gap by providing one further constructor in the ServerSocket
class:
ServerSocket(int port, int backlog, InetAddress bindAddr)
The third argument
specifies a local IP address for the server socket to listen at.
For example, the following (admittedly silly) code fragment
defines a server that only allows connections on its loopback
interface:
// ... SERVER_PORT defined elsewhere
listenSocket = new ServerSocket(SERVER_PORT, 50,
InetAddress.getByName("127.0.0.1"));
The second argument,
50, provides the default value for the request backlog allowed by
a server socket. You can set this to another value more
appropriate for your program if you wish.
|
|
Q . How do I send and/or receive datagram packets?
|
Ans
:
Create an
appropriate datagram socket and datagram packet, then send or
receive the packet via the socket.
The
java.net package supports two fundamentally different
ways to send data over the network: normal sockets and datagram
sockets. Normal sockets provide a model of reliable data transport
as a stream of bytes (or a matched pair of streams) that connects
two network locations. You first establish the connection, and
then send (and receive) data through it. The socket implementation
hides from you all the gory details of splitting the stream into
addressed data packets, ensuring that the packets all arrive at
their destination, and reassembling the stream in correct order
from the packets. The protocol underlying this model is the
Transmission Control Protocol (TCP).
Datagram sockets, in contrast,
provide a more rudimentary, lightweight service for sending out or
receiving individual data packets. There is no ongoing connection
or data stream, and the delivery mechanism specifically does not
guarantee that the data reaches its destination. Instead, for each
packet, you have to manage the details of providing a data buffer
and appropriate destination address and port number. The protocol
underlying this model is the User Datagram Protocol (UDP).
Using datagrams thus centers around
packets: either composing and sending them, or receiving and
unpacking them. To send a packet, the standard steps are:
- Create a
DatagramSocket
instance.
- Allocate and
fill a data buffer (a
byte array).
- Create a
DatagramPacket
instance that combines the data buffer with address
information.
- Send the packet
through the socket.
The following code
fragment exemplifies these steps:
InetAddress destination = ...;
int destinationPort = ...;
byte[] dataBuffer = ...;
DatagramSocket socket;
DatagramPacket packet;
try {
socket = new DatagramSocket();
packet = new DatagramPacket(dataBuffer, dataBuffer.length,
destination, destinationPort);
socket.send(packet);
socket.close();
} catch (Exception e) {
// ... handle exception
}
Notice that the DatagramSocket
instance created for sending does not require that you specify a
port number, since the DatagramPacket instance itself
conveys all the necessary routing information. The socket still
uses a port number, but it takes one that the system assigns
automatically. Another noteworthy point is that the try-catch
statement is needed because several of the DatagramSocket
methods and constructors throw exceptions.
Receiving a packet involves many of
the same components as sending, except you are preparing an empty
packet to be filled:
- Create a
datagram socket.
- Allocate a data
buffer, but don't fill it.
- Create a
datagram packet with that buffer, but with no address
information.
- Invoke receive
on the socket to wait for data to arrive and fill the packet.
The following
sample code illustrates each of these steps:
int MAX_BYTES = 576;
int SERVER_PORT = ...
DatagramSocket socket;
DatagramPacket packet;
byte[] dataBuffer = new byte[MAX_BYTES];
try {
socket = new DatagramSocket(SERVER_PORT);
packet = new DatagramPacket(dataBuffer, dataBuffer.length);
socket.receive(packet);
socket.close();
} catch (Exception e) {
// ... handle exception
}
// ... process contents of packet
Datagram
sockets distinguish most strongly between the sender and the
receiver, but they also distinguish weakly between client and
server:
- A client must
know the server's address and port in advance, in order to
make the initial contact.
- A server can
read the client's address and port from the datagram packet it
receives.
|
|
Q .
When will I be able to create a broadcast datagram packet?
|
Ans
:
Starting
with the JDK 1.1, you can create broadcast datagram packets.
Creating
a broadcast datagram packet is like creating other datagram
packets for sending, except that you use the reserved broadcast
address 255.255.255.255. The following code fragment
illustrates how:
InetAddress destination;
int destinationPort = ...
DatagramSocket socket;
DatagramPacket packet;
byte[] dataBuffer = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 };
try {
destination = InetAddress.getByName("255.255.255.255");
socket = new DatagramSocket();
packet = new DatagramPacket(dataBuffer, dataBuffer.length,
destination, destinationPort);
socket.send(packet);
socket.close();
} catch (Exception e) { /* ... */ }
The
reason this doesn't work in pre-1.1 JDK releases is that they
contain a bug in their getByName method that prevents
broadcast packets from working. Looking up an IP address that
lacked a Domain Name Service (DNS) entry generates an UnknownHostException,
even in cases (like the broadcast address) where the IP address
has a useful, well-established meaning independent of DNS.
|
|
Q .
How do I "timeout" a socket read, as the "select" function in Unix does?
|
Ans
: There are two timeouts at issue here. Use the ServerSocket.setSoTimeout(int millis) for timing out your
'accept()' calls.
For a select() like mechanism, you can poll InputStreams of your connections for available() > 0
URLConnection doesn't have a method for this, but you can do it by using multiple threads. Create a timer thread that would just track
time and close the connection after your specified timeout period.
Check the URLConnection to see if it's got any data flowing through it (calling
URLConnection.connected() is probably a sufficient test). The URLConnection should be in a separate thread.
|
|
Q . Why, when I make a call to DatagramPacket.getlength() does it returns the wrong size?
|
Ans
:
You are probably reusing a Datagram packet, and it has no way to
grow its buffer if a longer packet comes in. Take care of it by calling
setLength(max) before reuse.
Failing to do this is a very common mistake.
|
|
Q .
My socket code looks good, but is broken!
|
Ans
:
When using sockets you typically open both inward and outward
streams. A TCP connection is full duplex, but either the send or receive side may be closed independently. By default, the remote end
will take the close as indicating that the connection has simply been closed, and will close its end as well. Check whether this is happening
for you, by adding the matched pair. Use tcpdump to check this.
|
|
Q . How do I control the amount of time a socket will linger before resetting?
|
Ans
:
When a socket wishes to terminate a connection it can "linger", allowing unsent data to be transmitted, or it can "reset" which means that all unsent data will be lost. You can explicitly set a delay before a reset is sent, giving more time for data to be read, or you can specify a delay of zero, meaning a reset will be sent as the java.net.Socket.close() method is invoked.
The socket option SO_LINGER controls whether a connection will be aborted, and if so, the linger delay. Use the java.net.Socket.setSoLinger method, which accepts as parameters a boolean and an int. The boolean flag will activate/deactivate the SO_LINGER option, and the int will control the delay time.
|
|
Q .
What does the java.net.Socket.setTcpNoDelay method do, and what is Nagle's algorithm?
|
Ans
:
This method controls the socket option TCP_NODELAY, which allows applications to enable or disable Nagle's algorithm. Nagle's algorithm (described in RFC 896), conserves bandwidth by minimizing the number of segments that are sent. When applications wish to decrease network latency and increase performance, they can disable Nagle's algorithm. Data will be sent earlier, at the cost of an increase in bandwidth consumption.
|
|
Q . Why can't my applet connect via sockets, or bind to a local port?
|
Ans
:
Applets are subject to heavy security constraints when executing under the control of a browser. Applets are unable to access the local file-system, to bind to local ports, or to connect to a computer via sockets other than the computer from which the applet is loaded. While it may seem to be an annoyance for developers, there are many good reasons why such tight constraints are placed on applets. Applets could bind to well known ports, and service network clients without authorization or consent. Applets executing within firewalls could obtain privileged information, and then send it across the network. Applets could even be infected by viruses, such as the Java StrangeBrew strain. Applets might become infected without an applet author's knowledge and then send information back that might leave hosts vulnerable to attack.
|
|
Q .
What are socket options, and why should I use them?
|
Ans
:
Socket options give developers greater control over how sockets behave. Most socket behavior is controlled by the operating system, not Java itself, but as of JDK1.1, you can control several socket options, including SO_TIMEOUT, SO_LINGER, TCP_NODELAY, SO_RCVBUF and SO_SNDBUF.
These are advanced options, and many programmers may want to ignore them. That's OK, but be aware of their existence for the future. You might like to specify a timeout for read operations, to control the amount of time a connection will linger for before a reset is sent, whether Nagle's algorithm is enabled/disabled, or the send and receive buffers for datagram sockets.
|
|
Q .
When my client connects to my server, why does no data come out?
|
Ans
:
This is a common problem, made more difficult by the fact that the fault may lie in either the client, or the server, or both. The first step is to try and isolate the cause of the problem, by checking whether the server is responding correctly.
If you're writing a TCP service, then you can telnet to the port the server uses, and check to see if it is responding to data. If so, then the fault is more than likely in the client, and if not, you've found your problem. A debugger can be very helpful in tracking down the precise location of server errors. You could try jdb, which comes with JDK, or use an IDE's debugger like Visual J++ or Borland JBuilder.
If your fault looks like it is in the client, then it can often be caused by buffered I/O. If you're using a buffered stream, or a writer (such as PrintWriter), you may need to manually flush the data. Otherwise, it will be queued up but not sent, causing both client and server to stall. The problem can even be intermittent, as the buffer will flush sometimes (when it becomes full) but not other times.
|
|
Q .
How can I find out who is accessing my server?
|
Ans
:
If you're using a DatagramSocket, every packet that you receive will contain
the address and port from which it was sent.
DatagramPacket packet = null;
// Receive next packet
myDatagramSocket.receive ( packet );
// Print address + port
System.out.println ("Packet from : " +
packet.getAddress().getHostAddress() + ':' + packet.getPort());
If you're using a ServerSocket, then every socket connection you accept will
contain similar information. The Socket class has a getInetAddress() and
getPort() method which will allow you to find the same information.
Socket mySock = myServerSocket.accept();
// Print address + port
System.out.println ("Connection from : " +
mySock.getInetAddress().getHostAddress() + ':' + mySock.getPort());
|
|
Q . Should I use ServerSocket or DatagramSocket in my applications?
|
Ans
:
DatagramSocket allows a server to accept UDP packets, whereas ServerSocket allows
an application to accept TCP connections. It depends on the protocol you're trying
to implement. If you're creating a new protocol, here's a few tips
DatagramSockets communciate using UDP packets. These packets don't guarantee delivery
- you'll need to handle missing packets in your client/server
ServerSockets communicate using TCP connections. TCP guarantees delivery,
so all you need to do is have your applications read and write using a socket's
InputStream and OutputStream.
|
|
Q . Can Java sockets talk to C/C++/Perl/Foo sockets?
|
Ans
:
Yes. Java sockets use the IP protocols TCP and UDP. These are standard internet sockets, and will work with any sockets that also use TCP and UDP. You've just discovered why people get so excited talking about "open systems". I recommend that you pick up Stevens' book (in the bibliography) for an in depth look at how they work.
|
|
Q . I get a SocketException of "Permission Denied" when I try to open up a ServerSocket. What's wrong?
|
Ans
:
More than likely, you are running under Unix, and trying to open a server socket with a port number <1024. Try a port number >1024, or run your server as root.
|
|
Q . I get crashes when I use too many sockets. What's wrong?
|
Ans
:
This is a bug in Windows 95. Resources are not properly reclaimed when a socket is closed. See the microsoft web site for a patch. (http://www.microsoft.com/)
|
|
Q . Can't I use an IP address to create a socket?
|
Ans
:
Under JDK 1.0.2, this only works on some machines. This is one of those annoying platform differences that can apparently be traced back to the basic networking libraries on each machine. This is fixed in the 1.1 release. See http://www.cdt.luth.se/~peppar/java/InetAddress/ for one solution to this.
The example code we are discussing is:
Socket sock = new Socket("155.152.5.1", 23);
On Solaris and Windows NT, the IP address does not work for IP addresses that do not have associated hostnames. On Linux and Windows 95, the IP address is OK. [Note: Here I am merely repeating things that I've read on USENET. Independent confirmation of this would be appreciated.]
This is apparently related with problems in the InetAddress class. When InetAddress is instantiated with an IP address, a reverse DNS lookup is done. If the IP address is not associated with a valid hostname, the instantiation will fail. This is part of anti-spoofing, and is relaxed in Java 1.1. In the new version, the reverse lookup will not occur until the hostname is asked for. In Java 1.1,
InetAddress in =
InetAddress.getByName("155.152.5.1");
should always work.
|
|