Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








10.1 The ServerSocket Class


The ServerSocket class contains everything needed to write
servers in Java. It has constructors that create new
ServerSocket objects, methods that listen for
connections on a specified port, methods that configure the various
server socket options, and the usual miscellaneous methods such as
toString( ).

In Java, the basic life cycle of a
server program is:

A new ServerSocket is created on a particular port
using a ServerSocket( ) constructor.

The ServerSocket listens for incoming connection
attempts on that port using its accept( ) method.
accept( ) blocks until a client attempts to make a
connection, at which point accept( ) returns a
Socket object connecting the client and the
server.

Depending on the type of server, either the
Socket's getInputStream() method, getOutputStream( ) method, or
both are called to get input and output streams that communicate with
the client.

The server and the client interact according to an agreed-upon
protocol until it is time to close the connection.

The server, the client, or both close the connection.

The server returns to step 2 and waits for the next connection.

If step 4 is likely to take a long or
indefinite amount of time, traditional Unix servers such as wu-ftpd
create a new process to handle each connection so that multiple
clients can be serviced at the same time. Java programs should spawn
a thread to interact with the client so that the server can be ready
to process the next connection sooner.
A thread places a far smaller load on the
server than a complete child process. In fact, the overhead of
forking too many processes is why the typical Unix FTP server
can't handle more than roughly 400 connections
without slowing to a crawl. On the other hand, if the protocol is
simple and quick and allows the server to close the connection when
it's through, then it will be more efficient for the
server to process the client request immediately without spawning a
thread.


Although threads are lighter-weight than processes on most systems
(Linux is the notable exception), too many threads can still be a
performance problem. For instance, on most VMs each thread requires
about a megabyte of RAM above and beyond what the rest of the program
needs. Thus, on a typical modern server with about a gigabyte of RAM,
anything close to or beyond a thousand threads is likely to slow down
dramatically and eventually crash as the CPU violently and frequently
swaps data into and out of RAM. Spawning too many threads is one of
the few ways you can reliably crash any Java virtual machine.

Java 1.4 introduces a
ServerSocketChannel class that provides non-blocking,
multiplexed I/O based on channels rather than streams. With channels,
a single thread can process multiple connections, thereby requiring
many fewer threads and placing a much smaller load on the VM. This
can be highly advantageous for high volume servers on some operating
systems. I'll discuss these kinds of servers in
Chapter 12. For simple, low-volume servers or
any servers that need to run with Java 1.3 or earlier, the techniques
discussed in this chapter should be used.

The operating system stores incoming connection requests addressed to
a particular port in a first-in, first-out queue. The default length
of the queue is normally 50, although it can vary from operating
system to operating system. Some operating systems (not Solaris) have
a maximum queue length, typically five. On these systems, the queue
length will be the largest possible value less than or equal to 50.
After the queue fills to capacity with unprocessed connections, the
host refuses additional connections on that port until slots in the
queue open up. Many (though not all) clients will try to make a
connection multiple times if their initial attempt is refused. The
operating system manages incoming connections and the queue; your
program does not need to worry about it. Several
ServerSocket constructors allow you to change the
length of the queue if its default length isn't
large enough; however, you won't be able to increase
the queue beyond the maximum size that the operating system supports.


10.1.1 The Constructors


There
are four public ServerSocket constructors:

public ServerSocket(int port) throws BindException, IOException
public ServerSocket(int port, int queueLength)
throws BindException, IOException
public ServerSocket(int port, int queueLength, InetAddress bindAddress)
throws IOException
public ServerSocket( ) throws IOException // Java 1.4

These constructors let you specify the port, the length of the queue
used to hold incoming connection requests, and the local network
interface to bind to. They pretty much all do the same thing, though
some use default values for the queue length and the address to bind
to. Let's explore these in order.

10.1.1.1 public ServerSocket(int port) throws BindException, IOException


This constructor creates a server socket on the port specified by the
argument. If you pass 0 for the port number, the system selects an
available port for you. A port chosen for you by the system is
sometimes called an anonymous
port since you don't know its
number. For servers, anonymous ports aren't very
useful because clients need to know in advance which port to connect
to; however, there are a few situations (which we will discuss later)
in which an anonymous port might be useful.

For example, to create a server socket that would be used by an HTTP
server on port 80, you would write:

try {
ServerSocket httpd = new ServerSocket(80);
}
catch (IOException ex) {
System.err.println(ex);
}

The constructor throws an IOException
(specifically, a BindException) if the socket
cannot be created and bound to the requested port. An
IOException when
creating a ServerSocket almost always means one of
two things. Either another server socket, possibly from a completely
different program, is already using the requested port, or
you're trying to connect to a port from 1 to 1,023
on Unix (including Linux and Mac OS X) without root (superuser)
privileges.

You can use this constructor to write a variation on the
PortScanner programs of the previous chapter.
Example 10-1 checks for ports on the local machine by
attempting to create ServerSocket objects on them
and seeing on which ports that fails. If you're
using Unix and are not running as root, this program works only for
ports 1,024 and
above.


Example 10-1. Look for local ports


import java.net.*;
import java.io.*;
public class LocalPortScanner {
public static void main(String[] args) {
for (int port = 1; port <= 65535; port++) {
try {
// the next line will fail and drop into the catch block if
// there is already a server running on the port
ServerSocket server = new ServerSocket(port);
}
catch (IOException ex) {
System.out.println("There is a server on port " + port + ".");
} // end catch
} // end for
}
}

Here's the output I got when running
LocalPortScanner on my Windows NT 4.0 workstation:

D:\JAVA\JNP2\examples\11>java LocalPortScanner
There is a server on port 135.
There is a server on port 1025.
There is a server on port 1026.
There is a server on port 1027.
There is a server on port 1028.

10.1.1.2 public ServerSocket(int port, int queueLength) throws IOException, BindException


This
constructor opens a server socket on the specified port with a queue
length of your choosing. If the machine has multiple network
interfaces or IP addresses, then it listens on this port on all those
interfaces and IP addresses. The queueLength
argument sets the length of the queue for incoming connection
requeststhat is, how many incoming connections can be stored
at one time before the host starts refusing connections. Some
operating systems have a maximum queue length, typically five. If you
try to expand the queue past that maximum number, the maximum queue
length is used instead. If you pass 0 for the port number, the system
selects an available port.

For example, to create a server socket on port 5,776 that would hold
up to 100 incoming connection requests in the queue, you would write:

try {
ServerSocket httpd = new ServerSocket(5776, 100);
}
catch (IOException ex) {
System.err.println(ex);
}

The constructor throws an IOException
(specifically, a BindException) if the socket
cannot be created and bound to the requested port. However, no
exception is thrown if the queue length is larger than the host OS
supports. Instead, the queue length is simply set to the maximum size
allowed.

10.1.1.3 public ServerSocket(int port, int queueLength, InetAddress bindAddress) throws BindException, IOException


This constructor binds a
server socket to the specified port with the specified queue length.
It differs from the other two constructors in binding only to the
specified local IP address. This constructor is useful for servers
that run on systems with several IP addresses because it allows you
to choose the address to which you'll listen. That
is, the server socket only listens for incoming connections on the
specified address; it won't listen for connections
that come in through the host's other addresses. The
previous two constructors bind to all local IP addresses by default.

For example, login.ibiblio.org is a particular
Linux box in North Carolina. It's connected to the
Internet with the IP address 152.2.210.122. The same box has a second
Ethernet card with the local IP address 192.168.210.122 that is not
visible from the public Internet, only from the local network. If for
some reason I wanted to run a server on this host that only responded
to local connections from within the same network, I could create a
server socket that listens on port 5,776 of 192.168.210.122 but not
on port 5,776 of 152.2.210.122, like so:

try {
ServerSocket httpd = new ServerSocket(5776, 10,
InetAddress.getByName("192.168.210.122"));
}
catch (IOException ex) {
System.err.println(ex);
}

The constructor throws an IOException (again,
really a BindException) if the socket cannot be
created and bound to the requested port or network interface.

10.1.1.4 public ServerSocket( ) throws IOException // Java 1.4


The public no-args constructor is new
in Java 1.4. It creates a ServerSocket object but
does not actually bind it to a port so it cannot initially accept any
connections. It can be bound later using the bind() methods also introduced in Java 1.4:

public void bind(SocketAddress endpoint) throws IOException // Java 1.4
public void bind(SocketAddress endpoint, int queueLength) // Java 1.4
throws IOException

The primary use for this feature is to allow programs to set server
socket options before binding to a port. Some options are fixed after
the server socket has been bound. The general pattern looks like
this:

ServerSocket ss = new ServerSocket( );
// set socket options...
SocketAddress http = new InetSocketAddress(80);
ss.bind(http);

You can also past null for the SocketAddress to
select an arbitrary port. This is like passing 0 for the port number
in the other constructors.


10.1.2 Accepting and Closing Connections


A ServerSocket customarily operates in a loop
that repeatedly accepts connections. Each pass through the loop
invokes the accept( ) method. This returns a
Socket object representing the connection between
the remote client and the local server. Interaction with the client
takes place through this Socket object. When the
transaction is finished, the server should invoke the
Socket object's close() method. If the client closes the connection while the
server is still operating, the input and/or output streams that
connect the server to the client throw an
InterruptedIOException on the next read or write.
In either case, the server should then get ready to process the next
incoming
connection. However, when the server needs to shut
down and not process any further incoming connections, you should
invoke the ServerSocket object's
close( ) method.

10.1.2.1 public Socket accept( ) throws IOException


When server setup is done and
you're ready to accept a connection, call the
ServerSocket's accept() method. This method
"blocks"; that is, it stops the
flow of execution and waits until a client connects. When a client
does connect, the accept( ) method returns a
Socket object. You use the streams returned by
this Socket's
getInputStream( ) and getOutputStream(
)
methods to communicate with the client. For example:

ServerSocket server = new ServerSocket(5776);
while (true) {
Socket connection = server.accept( );
OutputStreamWriter out
= new OutputStreamWriter(connection.getOutputStream( ));
out.write("You've connected to this server. Bye-bye now.\r\n");
connection.close( );
}

If you don't want the program to halt while it waits
for a connection, put the call to accept( ) in a
separate thread.


If you're using Java 1.4 or later, you have the
option to use channels and non-blocking I/O instead of threads. In
some (not all) virtual machines, this is much faster than using
streams and threads. These techniques will be discussed in Chapter 12.

When exception handling is added, the code becomes somewhat more
convoluted. It's important to distinguish between
exceptions that should probably shut down the server and log an error
message, and exceptions that should just close that active
connection. Exceptions thrown by accept( ) or the
input and output streams generally should not shut down the server.
Most other exceptions probably should. To do this,
you'll need to nest your try
blocks.

Finally, most servers will want to make sure that all sockets they
accept are closed when they're finished. Even if the
protocol specifies that clients are responsible for closing
connections, clients do not always strictly adhere to the protocol.
The call to close( ) also has to be wrapped in a
try block that catches an
IOException. However, if you do catch an
IOException when closing the socket, ignore it. It
just means that the client closed the socket before the server could.
Here's a slightly more realistic example:

try {
ServerSocket server = new ServerSocket(5776);
while (true) {
Socket connection = server.accept( );
try {
Writer out
= new OutputStreamWriter(connection.getOutputStream( ));
out.write("You've connected to this server. Bye-bye now.\r\n");
out.flush( );
connection.close( );
}
catch (IOException ex) {
// This tends to be a transitory error for this one connection;
// e.g. the client broke the connection early. Consequently,
// you don't want to break the loop or print an error message.
// However, you might choose to log this exception in an error log.
}
finally {
// Guarantee that sockets are closed when complete.
try {
if (connection != null) connection.close( );
}
catch (IOException ex) {}
}
}
catch (IOException ex) {
System.err.println(ex);
}

Example 10-2 implements a simple daytime server, as
per RFC 867. Since this server just sends a single line of text in
response to each connection, it processes each connection
immediately. More complex servers should spawn a thread to handle
each request. In this case, the overhead of spawning a thread would
be greater than the time needed to process the request.


If you run this program on Unix (including Linux and Mac OS X), you
need to run it as root in order to connect to port 13. If you
don't want to or can't run it as
root, change the port number to something above 1024say, 1313.


Example 10-2. A daytime server


import java.net.*;
import java.io.*;
import java.util.Date;
public class DaytimeServer {
public final static int DEFAULT_PORT = 13;
public static void main(String[] args) {
int port = DEFAULT_PORT;
if (args.length > 0) {
try {
port = Integer.parseInt(args[0]);
if (port < 0 || port >= 65536) {
System.out.println("Port must between 0 and 65535");
return;
}
}
catch (NumberFormatException ex) {
// use default port
}
}
try {
ServerSocket server = new ServerSocket(port);
Socket connection = null;
while (true) {
try {
connection = server.accept( );
Writer out = new OutputStreamWriter(connection.getOutputStream( ));
Date now = new Date( );
out.write(now.toString( ) +"\r\n");
out.flush( );
connection.close( );
}
catch (IOException ex) {}
finally {
try {
if (connection != null) connection.close( );
}
catch (IOException ex) {}
}
} // end while
} // end try
catch (IOException ex) {
System.err.println(ex);
} // end catch
} // end main
} // end DaytimeServer

Example 10-2 is straightforward. The first three
lines import the usual packages, java.io and
java.net, as well as
java.util.Date, which provides the time as read by
the server's internal clock. There is a single
public final
static int field (i.e., a
constant) in the class DEFAULT_PORT, which is set
to the well-known port for a daytime server (port 13). The class has
a single method, main( ), which does all the work.
If the port is specified on the command line, then
it's read from args[0].
Otherwise, the default port is used.

The outer try block traps any
IOExceptions that may arise while the
ServerSocket object server is
constructed on the daytime port or when it accepts connections. The
inner try block watches for exceptions thrown
while the connections are accepted and processed. The
accept( ) method is called within an infinite loop
to watch for new connections; like many servers, this program never
terminates but continues listening until an exception is thrown or
you stop it manually.


The command for stopping a program
manually depends on your system; under Unix, NT, and many other
systems, CTRL-C will do the job. If you are running the server in the
background on a Unix system, stop it by finding the
server's process ID and killing it with the
kill command (kill
pid).

When a client connects, accept( ) returns a
Socket, which is stored in the local variable
connection, and the program continues. It calls
getOutputStream( ) to get the output stream
associated with that Socket and then chains that
output stream to a new OutputStreamWriter,
out. A new Date object provides
the current time. The content is sent to the client by writing its
string representation on out with write().

Finally, after the data is sent or an exception has been thrown, the
finally block closes the
connection. Always close a
socket
when you're finished with it. In the previous
chapter, I said that a client shouldn't rely on the
other side of a connection to close the socket: that goes triple for
servers. Clients time out or crash; users cancel transactions;
networks go down in high-traffic periods. For any of these or a dozen
more reasons, you cannot rely on clients to close sockets, even when
the protocol requires them to, which this one
doesn't.

Sending binary,
nontext data is not significantly harder. Example 10-3 demonstrates with a time server that follows
the time protocol outlined in
RFC 868. When a client connects, the server sends a 4-byte,
big-endian, unsigned integer specifying the number of seconds that
have passed since 12:00 A.M., January 1, 1900 GMT (the epoch). Once
again, the current time is found by creating a new
Date object. However, since the
Date class counts milliseconds since 12:00 A.M.,
January 1, 1970 GMT rather than seconds since 12:00 A.M., January 1,
1900 GMT, some conversion is necessary.


Example 10-3. A time server


import java.net.*;
import java.io.*;
import java.util.Date;
public class TimeServer {
public final static int DEFAULT_PORT = 37;
public static void main(String[] args) {
int port = DEFAULT_PORT;
if (args.length > 0) {
try {
port = Integer.parseInt(args[0]);
if (port < 0 || port >= 65536) {
System.out.println("Port must between 0 and 65535");
return;
}
}
catch (NumberFormatException ex) {}
}
// The time protocol sets the epoch at 1900,
// the Date class at 1970. This number
// converts between them.
long differenceBetweenEpochs = 2208988800L;
try {
ServerSocket server = new ServerSocket(port);
while (true) {
Socket connection = null;
try {
connection = server.accept( );
OutputStream out = connection.getOutputStream( );
Date now = new Date( );
long msSince1970 = now.getTime( );
long secondsSince1970 = msSince1970/1000;
long secondsSince1900 = secondsSince1970
+ differenceBetweenEpochs;
byte[] time = new byte[4];
time[0]
= (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24);
time[1]
= (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16);
time[2]
= (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8);
time[3] = (byte) (secondsSince1900 & 0x00000000000000FFL);
out.write(time);
out.flush( );
} // end try
catch (IOException ex) {
} // end catch
finally {
if (connection != null) connection.close( );
}
} // end while
} // end try
catch (IOException ex) {
System.err.println(ex);
} // end catch
} // end main
} // end TimeServer

As with the TimeClient of the previous chapter,
most of the effort here goes into working with a data format (32-bit
unsigned integers) that Java doesn't natively
support.

10.1.2.2 public void close( ) throws IOException


If you're finished with
a server socket, you should close it, especially if the program is
going to continue to run for some time. This frees up the port for
other programs that may wish to use it. Closing a
ServerSocket should
not be confused with closing a Socket. Closing a
ServerSocket frees a port on the local host,
allowing another server to bind to the port; it also breaks all
currently open sockets that the ServerSocket has
accepted.

Server sockets are closed automatically when a program dies, so
it's not absolutely necessary to close them in
programs that terminate shortly after the
ServerSocket is no longer needed. Nonetheless, it
doesn't hurt. For example, the main loop of the
LocalPortScanner program might be better written
like this so that it doesn't temporarily occupy most
of the ports on the system:

for (int port = 1; port <= 65535; port++) {
try {
// the next line will fail and drop into the catch block if
// there is already a server running on the port
ServerSocket server = new ServerSocket(port);
server.close( );
}
catch (IOException ex) {
System.out.println("There is a server on port " + port + ".");
}
} // end for

After the server socket has been closed, it cannot be reconnected,
even to the same port.

Java 1.4 adds an isClosed( ) method that returns
true if the ServerSocket has been closed, false if
it hasn't:

public boolean isClosed( ) // Java 1.4

ServerSocket objects that were created with the
no-args ServerSocket( ) constructor and not yet
bound to a port are not considered to be closed. Invoking
isClosed( ) on these objects returns false. Java
1.4 also adds an isBound(
)
method that tells you whether the
ServerSocket has been bound to a port:

public boolean isBound( ) // Java 1.4

As with the isBound( ) method of the
Socket class discussed in the last chapter, the
name is a little misleading. isBound( ) returns
true if the ServerSocket has ever been bound to a
port, even if it's currently closed. If you need to
test whether a ServerSocket is open, you must
check both that isBound( ) returns true and that
isClosed( ) returns false. For example:

public static boolean isOpen(ServerSocket ss) {
return ss.isBound( ) && ! ss.isClosed( );
}


10.1.3 The get Methods


The ServerSocket
class provides two getter methods that tell you the local address and
port occupied by the server socket. These are useful if
you've opened a server socket on an anonymous port
and/or an unspecified network interface. This would be the case, for
one example, in the data connection of an FTP session.

10.1.3.1 public InetAddress getInetAddress( )


This method returns the address being
used by the server (the local host). If the local host has a single
IP address (as most do), this is the address returned by
InetAddress.getLocalHost( ). If the local host has
more than one IP address, the specific address returned is one of the
host's IP addresses. You can't
predict which address you will get. For example:

ServerSocket httpd = new ServerSocket(80);
InetAddress ia = httpd.getInetAddress( );

If the ServerSocket has not yet bound to a network
interface, this method returns null.

10.1.3.2 public int getLocalPort( )


The ServerSocket
constructors allow you to listen on an unspecified port by passing 0
for the port number. This method lets you find out what port
you're listening on. You might use this in a
peer-to-peer multisocket program where you already have a means to
inform other peers of your location. Or a server might spawn several
smaller servers to perform particular operations. The well-known
server could inform clients what ports they can find the smaller
servers on. Of course, you can also use getLocalPort() to find a non-anonymous port, but why would you need to?
Example 10-4
demonstrates.


Example 10-4. A random port


import java.net.*;
import java.io.*;
public class RandomPort {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(0);
System.out.println("This server runs on port "
+ server.getLocalPort( ));
}
catch (IOException ex) {
System.err.println(ex);
}
}
}

Here's the output of several runs:

D:\JAVA\JNP3\examples\10>java RandomPort
This server runs on port 1154
D:\JAVA\JNP3\examples\10>java RandomPort
This server runs on port 1155
D:\JAVA\JNP3\examples\10>java RandomPort
This server runs on port 1156

At least on this VM, the ports aren't really random,
but they are at least indeterminate until runtime.

If the ServerSocket has not yet bound to a port,
then this method returns -1.


10.1.4 Socket Options


Java 1.3 only supports one socket option for server sockets,
SO_TIMEOUT. Java 1.4 adds two more, SO_REUSEADDR and SO_RCVBUF.

10.1.4.1 SO_TIMEOUT


SO_TIMEOUT is the amount of time, in
milliseconds, that accept( ) waits for an incoming
connection before throwing a
java.io.InterruptedIOException. If SO_TIMEOUT is
0, accept( ) will never time out. The default is
to never time out.

Using SO_TIMEOUT is rather rare. You might need it if you were
implementing a complicated and secure protocol that required multiple
connections between the client and the server where responses needed
to occur within a fixed amount of time. However, most servers are
designed to run for indefinite periods of time and therefore just use
the default timeout value, 0 (never time out). If you want to change
this, the setSoTimeout( ) method sets the
SO_TIMEOUT field for this server socket object.

public void setSoTimeout(int timeout) throws SocketException
public int getSoTimeout( ) throws IOException

The countdown starts when accept()
is invoked. When the timeout expires, accept( )
throws an InterruptedIOException. (In Java 1.4, it
throws SocketTimeoutException, a subclass of
InterruptedIOException.) You should set this
option before calling accept( ); you cannot change
the timeout value while accept( ) is waiting for a
connection. The timeout argument must be greater
than or equal to zero; if it isn't, the method
throws an IllegalArgumentException. For example:

try {
ServerSocket server = new ServerSocket(2048);
server.setSoTimeout(30000); // block for no more than 30 seconds
try {
Socket s = server.accept( );
// handle the connection
// ...
}
catch (InterruptedIOException ex) {
System.err.println("No connection within 30 seconds");
}
finally {
server.close( );
}
catch (IOException ex) {
System.err.println("Unexpected IOException: " + e);
}

The getSoTimeout() method returns this
server socket's current SO_TIMEOUT value. For
example:

public void printSoTimeout(ServerSocket server) {
int timeout = server.getSoTimeOut( );
if (timeout > 0) {
System.out.println(server + " will time out after "
+ timeout + "milliseconds.");
}
else if (timeout == 0) {
System.out.println(server + " will never time out.");
}
else {
System.out.println("Impossible condition occurred in " + server);
System.out.println("Timeout cannot be less than zero." );
}
}

10.1.4.2 SO_REUSEADDR // Java 1.4


The SO_REUSEADDR option for
server sockets is very similar to the same option for client sockets,
discussed in the last chapter. It determines whether a new socket
will be allowed to bind to a previously used port while there might
still be data traversing the network addressed to the old socket. As
you probably expect, there are two methods to get and set this
option:

public void setReuseAddress(boolean on) throws SocketException  
public boolean getReuseAddress( ) throws SocketException

The default value is platform-dependent. This code fragment
determines the default value by creating a new
ServerSocket and then calling
getReuseAddress( ):

ServerSocket ss = new ServerSocket(10240);
System.out.println("Reusable: " + ss.getReuseAddress( ));

On the Linux and Mac OS X boxes where I tested this code, server
sockets were reusable.

10.1.4.3 SO_RCVBUF // Java 1.4


The SO_RCVBUF option sets the
default receive buffer size for client sockets accepted by the server
socket. It's read and written by these two methods:

public void setReceiveBufferSize(int size) throws SocketException
public int getReceiveBufferSize( ) throws SocketException

Setting SO_RCVBUF on a server socket is like calling
setReceiveBufferSize( ) on each individual socket
returned by accept( ) (except that you
can't change the receive buffer size after the
socket has been accepted). Recall from the last chapter that this
option suggests a value for the size of the individual IP packets in
the stream. Faster connections will want to use larger packets,
although most of the time the default value is fine.

You can set this option before or after the server socket is bound,
unless you want to set a receive buffer size larger than 64K. In that
case, you must set the option on an unbound
ServerSocket before binding it. For example:

ServerSocket ss = new ServerSocket( );
int receiveBufferSize = ss.getReceiveBufferSize( );
if (receiveBufferSize < 131072) {
ss.setReceiveBufferSize(131072);
}
ss.bind(new InetSocketAddress(8000));
//...

10.1.4.4 public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) // Java 1.5


Java 1.5 adds a slightly different
method for setting socket optionsthe
setPerformancePreferences() method:

public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth)

This method expresses the relative preferences given to connection
time, latency, and bandwidth. For instance, if
connectionTime is 2 and latency
is 1 and bandwidth is 3, then maximum bandwidth is
the most important characteristic, minimum latency is the least
important, and connection time is in the middle. Exactly how any
given VM implements this is implementation-dependent. Indeed, it may
be a no-op in some implementations. The API documentation for
ServerSocket even suggests using non-TCP/IP
sockets, although it's not at all clear what that
means.


10.1.5 The Object Methods


ServerSocket
overrides only one of the standard methods from
java.lang.Object, toString( ).
Thus, equality comparisons test for strict identity and server
sockets are problematic in hash tables. Normally, this
isn't a large problem.

10.1.5.1 public String toString( )


A String returned by
ServerSocket's toString(
)
method looks like this:

ServerSocket[addr=0.0.0.0,port=0,localport=5776]

addr is the address of the local network interface
to which the server socket is bound. This will be 0.0.0.0 if
it's bound to all interfaces, as is commonly the
case. port is always 0. The
localport is the local port on which the server is
listening for connections. This method is sometimes useful for
debugging, but not much more. Don't rely on it.


10.1.6 Implementation


The ServerSocket class provides two methods for
changing the default implementation of server sockets.
I'll describe them only briefly here, since
they're primarily intended for implementers of Java
virtual machines rather than application programmers.

10.1.6.1 public static void setSocketFactory(SocketImplFactory factory) throws IOException


This method sets the system's
server SocketImplFactory,
which is the factory used to create ServerSocket
objects. This is not the same factory that is used to create client
Socket objects, though the syntax is similar; you
can have one factory for Socket objects and a
different factory for ServerSocket objects. You
can set this factory only once in a program, however. A second
attempt to set the SocketImplFactory throws a
SocketException.

10.1.6.2 protected final void implAccept(Socket s) throws IOException


Subclasses of
ServerSocket use this method when they want to
override accept( ) so that it returns an instance
of their own custom Socket subclass rather than a
plain java.net.Socket. The overridden
accept( ) method passes its own unconnected
Socket object to this method to actually make the
connection. You pass an unconnected Socket object
to implAccept( ). When implAccept(
)
returns, the Socket argument
s is connected to a client. For example:

  public Socket accept( )  throws IOException {
Socket s = new MySocketSubclass( );
implAccept(s);
return s;
}

If the server needs to know that the Socket
returned by accept( ) has a more specific type
than just java.net.Socket, it must cast the return
value appropriately. For example:

ServerSocket server = new MyServerSocketSubclass(80);  
while (true) {
MySocketSubclass socket = (MySocketSubclass) server.accept( );;
// ...
}


/ 164