Introduction
One of the most basic network programming tasks you’ll likely face as
a Java programmer is performing socket functions. You may have to create
a network client that talks to a server via a socket connection. Or, you may
have to create a server that listens for socket connections. Either way, sooner
or later you’re going to deal with sockets. What is a socket you ask?
Think of a socket as the basic communication interface between networked computers.
Sockets allow you the programmer to treat the network connection as you would
any other I/O. In Java, sockets are the lowest level of network coding.
During the next few paragraphs, we’ll work through some examples of socket
programming in Java: a simple client, a simple server that takes one connection
at a time, and a server that allows multiple socket connections.
SocketClient: A Simple TCP/IP Socket Client
package bdn;
/* The java.net package contains the basics needed for network operations. */
import java.net.*;
/* The java.io package contains the basics needed for IO operations. */
import java.io.*;
/** The SocketClient class is a simple example of a TCP/IP Socket Client.
*
*/
public class SocketClient {
Let’s start by creating a class called SocketClient. We’ll put
this into a package called bdn. The only packages that we’re going to
need in this example are java.net and java.io. If you’ve not dealt with
the java.net. package before, as it’s name implies, it contains the basic
classes and methods you’ll need for network programming (see your JBuilder
help files or http://java.sun.com/j2se/1.5.0/docs/api/java/net/package-summary.html
for more information).
One of the cool things about java is the consistent use of InputStreams and
OutputStreams to read and write I/O, regardless of the device. In other words,
you can almost always be assured that if you are reading from any input source,
you will use an InputStream...when writing to output sources you'll use an OutputStream.
This means reading and writing across a network is almost the same as reading
and writing files. For this reason, we need import that java.io package into
our program.
Some House Keeping
public static void main(String[] args) {
/** Define a host server */
String host = "localhost";
/** Define a port */
int port = 19999;
StringBuffer instr = new StringBuffer();
String TimeStamp;
System.out.println("SocketClient initialized");
In order to make a socket connection, you need to know a couple of pieces of
information. First you need a host to connect to. In this example we’re
going to be running the client(s) and the server on the same machine. We define
a String host as localhost.
Note: we could have used the TCP/IP address 127.0.0.1
instead of localhost.
The next piece of information we need to know is the TCP/IP port that the program
is going to be communicating on. TCP/IP uses ports because it is assumed that
servers will be doing more than one network function at a time and ports provide
a way to maange this. For example: a server may be serving up web pages (port
80), it may have a FTP (port 21) server, and it may be handling a SMTP mail
server (port 25). Ports are assigned by the server. The client needs to know
what port to use for a particular service. In the TCP/IP world, each computer
with a TCP/IP address has access to 65,535 ports. Keep in mind that ports are
not physical devices like a serial, parallel, or USB port. They are an abstraction
in the computer’s memory running under the TCP/IP transport layer protocol.
Note: Ports 1 – 1023 are reserved for services such as HTTP, FTP, email,
and Telnet.
Now back to the code. We create an int called port. The server
we’re going to build later in the article will be listening on port 19999.
As a result we initialize port to 19999.
A couple of other items that we define here are a StringBuffer instr
to be used for reading our InputStream. We also define a String TimeStamp
that we’ll use to communicate with the server. Lastly, we System.out.println()
a message to let us know the program has begun…this kind of stuff is certainly
not necessary, but I’ve found occasionally logging a program status message
gives people a peace of mind that a program’s actually doing something.
Requesting a Socket and Establishing a Connection
try {
/** Obtain an address object of the server */
InetAddress address = InetAddress.getByName(host);
/** Establish a socket connetion */
Socket connection = new Socket(address, port);
/** Instantiate a BufferedOutputStream object */
Now we create a try-catch block. This block is needed because the methods of
several classes we’re going to reference here throw exceptions. In our
example, we’re primarily concerned with IOExceptions, and will specifically
capture that one (in real world situations we’d want to deal with this
exception more thoroughly). All other exceptions will be captured with a generic
Exception.
Note: You should spend some time reviewing the various javadocs on the classes
and methods you use. There are often specific exceptions that you want to catch
and deal with. Example: had we built an applet that allowed a person to enter
the servers and ports they wanted to connect to, we would have wanted to deal
with UnknownHostException in the event they keyed an invalid host.
In order to establish a connection with a server, we must first obtain the
server’s 32-bit IP address. We obtain the IP address by invoking the InetAddress.getByName()
method. As it describes, we pass this method the name of the host we’re
looking to connect to. It returns an InetAddress object address containing the
host name/IP address pair (i.e. Using localhost in the getByName() method will
return localhost/127.0.0.1 in the InetAddress object).
Once we’ve obtained the InetAddress object, we’re ready to establish
a socket connection with our server. We create a Socket called connection
by instantiating a new Socket object with the InetAdress object address and
our previously created int port. If the server is not responding
on the port we’re looking for, we’ll get a “java.netConnectException:
Connection refused:..” message.
We’ve established our connection. Now we want to write some information
to the server. As mentioned previously, Java treats reading and writing sockets
is much like reading and writing files. Subsequently we start by establishing
an OutputStream object. In general TCP stacks use buffers to improve performance
within the network. And, although it’s not necessary, we might as well
use BufferedInputStreams and BufferedOutputStreams when reading and writing
data across the network. We instantiate a BufferedOutputStream object bos
by requesting an OutputStream from our socket connection.
Writing to the Socket
BufferedOutputStream bos = new BufferedOutputStream(connection.
getOutputStream());
/** Instantiate an OutputStreamWriter object with the optional character
* encoding.
*/
OutputStreamWriter osw = new OutputStreamWriter(bos, "US-ASCII");
We could use the BufferedOutputStream.write() method to write bytes across
the socket. I prefer to use OutputStreamWriter objects to write on because I’m
usually dealing in multiple platforms and like to control the character encoding.
Also, with OutputStreamWriter you can pass objects such as Strings without converting
to byte, byte arrays, or int values…ok I’m lazy…so what.
Note: If you don’t handle the character encoding and
are reading and writing to an IBM mainframe from a Windows platform, you’ll
probably end up with garbage because IBM mainframes tend to encode characters
as EBCDIC and Windows encodes characters as ASCII.
We create an OutputStreamWriter osw by instantiating it with
our BufferedOutputStream bos and optionally the character encoding
US-ASCII.
TimeStamp = new java.util.Date().toString();
String process = "Calling the Socket Server on "+ host + " port " + port +
" at " + TimeStamp + (char) 13;
/** Write across the socket connection and flush the buffer */
osw.write(process);
osw.flush();
As shown above, we’re creating two Strings TimeStamp and process to be
written to the server. We call the osw.write() method from our OutputStreamWriter
object, passing the String process to it. Please note that we placed a char(13)
at the end of process...we’ll use this to let the server know we’re
at the end of the data we’re sending. The last item we need to take care
of is flushing the buffer. If we don’t do this, then we can’t guarantee
that the data will be written across the socket in a timely manner.
Reading from the Socket
/** Instantiate a BufferedInputStream object for reading
/** Instantiate a BufferedInputStream object for reading
* incoming socket streams.
*/
BufferedInputStream bis = new BufferedInputStream(connection.
getInputStream());
/**Instantiate an InputStreamReader with the optional
* character encoding.
*/
InputStreamReader isr = new InputStreamReader(bis, "US-ASCII");
/**Read the socket's InputStream and append to a StringBuffer */
int c;
while ( (c = isr.read()) != 13)
instr.append( (char) c);
/** Close the socket connection. */
connection.close();
System.out.println(instr);
}
catch (IOException f) {
System.out.println("IOException: " + f);
}
catch (Exception g) {
System.out.println("Exception: " + g);
}
}
}
The last thing we want to do is read the server’s response. As mentioned
before, most networks buffer socket traffic to improve performance. For this
reason we’re using the BufferedInputStream class. We start by instantiating
a BufferInputStream object bis, calling the getInputStream()
method of our Socket object connection. We instantiate an InputStreamReader
object isr, passing our BufferedInputStream object bis
and an optional character encoding of US-ASCII.
We create an int c that will be used for reading bytes from
the BufferedInputStream. We create a while…loop reading bytes and stuffing
them into a StringBuffer object instr until we encounter a
char(13), signaling the end of our stream. Once we’ve read the socket,
we close the socket and process the information…in this example we’re
just going to send it to the console. The last thing we do is create code in
our catch block to deal with exceptions.
Now that we’ve created the client, what do we do with it? Compiling and
running it will give us the following message:

Figure 1: SocketClient Running without Server
We get this message because we don’t have a server listening on port
19999 and therefore can’t establish a connection with it.
Now it’s time to create the server…
SingleSocketServer: A Server That Process One Socket at a Time
package bdn;
import java.net.*;
import java.io.*;
import java.util.*;
public class SingleSocketServer {
static ServerSocket socket1;
protected final static int port = 19999;
static Socket connection;
static boolean first;
static StringBuffer process;
static String TimeStamp;
We start by importing the same packages we did with our SocketClient class.
We set up a few variables. Of note, this time we’re setting up a ServerSocket
object called socket1. As its name implies, we use the ServerSocket
class to set up a new server. As we’ll see later, ServerSockets can be
created to listen on a particular port and accept and deal with incoming sockets.
Depending on the type of server we build, we can also process InputStreams and
OutputStreams.
public static void main(String[] args) {
try{
socket1 = new ServerSocket(port);
System.out.println("SingleSocketServer Initialized");
int character;
In the main() method, we start with a try…catch block. Next we instantiate
a new ServerSocket object socket1 using the port value of 19999…the
same port that we were looking to connect to with our SocketClient class. Finally,
we send a message to the console to let the world know we’re running.
while (true) {
connection = socket1.accept();
BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
InputStreamReader isr = new InputStreamReader(is);
process = new StringBuffer();
while((character = isr.read()) != 13) {
process.append((char)character);
}
System.out.println(process);
//need to wait 10 seconds for the app to update database
try {
Thread.sleep(10000);
}
catch (Exception e){}
TimeStamp = new java.util.Date().toString();
String returnCode = "SingleSocketServer repsonded at "+ TimeStamp + (char) 13;
BufferedOutputStream os = new BufferedOutputStream(connection.getOutputStream());
OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
osw.write(returnCode);
osw.flush();
}
}
catch (IOException e) {}
try {
connection.close();
}
catch (IOException e) {}
}
}
Since we’re running a server, we can assume that it’s always ready
to accept and process socket connections. Using a while(true)…loop helps
us accomplish this task. Within this block of code, we start with the ServerSocket
socket1’s accept() method. The accept() method basically
stops the flow of the program and waits for an incoming socket connection. When
a client connects, our Socket object connection is instantiated.
Once this is done, our program continues through the code.
Once a Connection is Made
We start by processing the InputStream coming from our socket. (For details
on this see the explanation for InputStreams in the Reading from the Socket
section of this article).
Note: I’ve thrown a line of code that says Thread.sleep(10000). All I’m
doing here is putting the current thread to sleep for 10 seconds. I added this
piece of code is purely for the purpose of demonstrating socket connections.
It would not be used in a real-world server application.
After we’ve processed the information in the incoming socket, we want
to return some information, in the form of an OutputStream, back to the client.
The process here is the same process we used in the SocketClient class (see
the section entitled: Writing to the Socket). Once the OutputStream is written,
the server is now ready to accept another socket.
Running The SingleSocketServer and SocketClient Classes Together
Let’s look at what happens when we run SingleSocketServer and SocketClient
together. Executing the programs in JBuilder X will look like this:

Figure 2: SingleSocketServer After One SocketClient Connection

Figure 3: SocketClient After Connecting with SingleSocketServer
Notice the results. Figure 2 shows messages for the SingleSocketServer class.
From the message, we can see that the SockeClient called the server at 20:36:33.
Looking at Figure 3 we see the messages for the SocketClient class. The message
tells us that the server responded at 20:36:43...10 seconds later. SocketClient
has ended and SocketServer is waiting for another connection.
Let’s take the same scenario and add a second instance of SocketClient
to the mix (to do this, start SocketClient, and while it’s running start
a second instance).

Figure 4: SingleSocketServer with Two SocketClient Instances

Figure 5: First Instance of SocketClient

Figure 6: Second Instance of SocketClient
This time look at the results. Figure 4 shows that SingleSocketServer logs
two messages, showing the TimeStamp that each instance of SocketClient. Figure
5 shows the first instance of SocketClient we executed. Notice that the time
in Figure 5 (20:38:54) is 10 seconds later than the time in the first message
in Figure 4 (20:38:44)...as expected
Now let’s look at the second instance of SocketClient. From the time
in Figure 4 we see that the second instance was launched 5 seconds after the
first one. However, looking at the time in Figure 6, we can see that SingleSocketServer
didn’t respond to the second instance until 10 seconds after it responded
to the first instance of SocketClient…13 seconds later. The reason for
this is simple. SingleSocketServer is running in a single thread and each incoming
socket connection must be completed before the next one can start.
Now, you might be asking, “Why in the world would someone want process
only one socket at a time?” Recently I was working on a project where
a non-java program had to be launched across a network from a java program.
And, only one instance of the non-java program could be running at a time. I
used a similar solution to solve that problem. Also, it can be a handy way of
keeping multiple instances of the same java program from being launched at the
same time. “That’s fine” you say, “but I want my server
to accept multiple sockets. How do you do that?”
Well, let’s take a look.
MultipleSocketServer: A Server That Handles Multiple Client Connections
package bdn;
import java.net.*;
import java.io.*;
import java.util.*;
public class MultipleSocketServer implements Runnable {
private Socket connection;
private String TimeStamp;
private int ID;
public static void main(String[] args) {
int port = 19999;
int count = 0;
try{
ServerSocket socket1 = new ServerSocket(port);
System.out.println("MultipleSocketServer Initialized");
while (true) {
Socket connection = socket1.accept();
Runnable runnable = new MultipleSocketServer(connection, ++count);
Thread thread = new Thread(runnable);
thread.start();
}
}
catch (Exception e) {}
}
MultipleSocketServer(Socket s, int i) {
this.connection = s;
this.ID = i;
}
We’re not going to spend much time examining all the code here since
we’ve already been through most of it. But, I do want to highlight some
things for you. Let’s start with the class statement. We’re introducing
a new concept here. This time we’re implementing the Runnable interface.
Without diving deep into the details of threads, suffice it to say that a thread
represents a single execution of a sequential block of code.
In our previous example, the SingleSocketServer class had a while(true) block
that started out by accepting an incoming socket connection. After a connection
was received, another connection could not happen until the code looped back
to the connection = socket1.accept(); statement. This block code represents
a single thread…or at least part of a single thread of code. If we want
to take this block of code and make it into many threads…allowing for
multiple socket connections...we have a couple of options: extending the implementing
the Runnable interface or extending the Thread class. How you decide which one
to use is entirely up to your needs. The Thread class has a lot of methods to
do various things like control thread behavior. The Runnable interface has a
single method run() (Thread has this method too). In this example, we’re
only concerned with the run() method…we’ll stick with the Runnable
interface.
Let’s continue…
Looking through the main() method, we still have to set up our server to allow
for connections on Port 19999, there’s still a while(true) block, and
we’re still accepting connections. Now comes the difference. After the
connection is made, we instantiate a Runnable object runnable
using a constructor for MultipleSocketServer that has 2 arguments: a Socket
object connection for the socket that we’ve just accepted,
and an int count, representing the count of open sockets. The
concept here is simple: we create a new socket object for each socket connection,
and we keep a count of the number of open connections. Although we’re
not going to worry about the number of connections in this example, you could
easily use count to limit the number of sockets that could be open at once.
Once we’ve instantiated runnable, we instantiate a new
Thread thread by passing runnable to the Thread
class. We call the start() method of thread and we’re ready go. Invoking
the start() method spawns a new thread and invokes the object’s run()
method. The actual work within the thread happens within the run() method. Let’s
take a quick look at that now.
The run() Method
public void run() {
try {
BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
InputStreamReader isr = new InputStreamReader(is);
int character;
StringBuffer process = new StringBuffer();
while((character = isr.read()) != 13) {
process.append((char)character);
}
System.out.println(process);
//need to wait 10 seconds to pretend that we're processing something
try {
Thread.sleep(10000);
}
catch (Exception e){}
TimeStamp = new java.util.Date().toString();
String returnCode = "MultipleSocketServer repsonded at "+ TimeStamp + (char) 13;
BufferedOutputStream os = new BufferedOutputStream(connection.getOutputStream());
OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
osw.write(returnCode);
osw.flush();
}
catch (Exception e) {
System.out.println(e);
}
finally {
try {
connection.close();
}
catch (IOException e){}
}
}
}
The run() method’s responsibility is to run the block of code that we
want threaded. In our example the run() method executes the same block of code
we built in the SingleSocketServer class. Once again, we read the BufferedInputStream,
pretend to process the information, and write a BufferedOutputStream. At the
end of the run() method, the thread dies.
Now it’s time to run our MultipleSocketServer. Using the same scenario
we did previously. Let’s look at what happens when we run the MultipleSocketServer
and two instances of SocketClient.

Figure 7: MultipleSocketServer with Two Instances of SocketClient

Figure 8: First Instance of SocketClient

Figure 9: Second Instance of SocketClient
Looking at the messages in Figure 7 that come from the MultipleSocketServer
class, we can see that requests for socket connections were sent by the SocketClient
programs within a few seconds of each other. According to the console messages
on the first instance of SocketClient in Figure 8, the server responded 10 seconds
after the request was sent. Figure 9 shows that the second instance of SocketClient
receives a response 10 seconds after it sent the request also, thus allowing
us the capability of processing multiple socket connections simultaneously.
Summary
Network programming in Java revolves around sockets. Sockets allow us to communicate
between programs across the network. Java’s approach to socket programming
allows us to treat socket I/O the same as we do any other I/O…utilizing
InputStreams and OutputStreams. Although, the examples presented here are relatively
simple, they give you an idea of the power of Java in the Client-Server world.
Next time, we’ll move a level up from socket programming and deal with
the basics of calling objects across a network using Java’s Remote Method
Invocation (RMI) facility.
About the Author
Rick Proctor has over 20 years experience in the IT industry. He's developed
applications on more platforms than he cares to remember. Since 2001 Rick has
been Vice President of Information Technology for Thomas Nelson Publishers,
Inc. (NYSE: TNM). Rick can be reached at tech_dude@yahoo.com.
The source code for the programs in this article can be found at CodeCentral.
Connect with Us