Sockets | CodeGuru

Sockets

Bruce Eckel’s Thinking in Java Contents | Prev | Next The socket is the software abstraction used to represent the “terminals” of a connection between two machines. For a given connection, there’s a socket on each machine, and you can imagine a hypothetical “cable” running between the two machines with each end of the “cable” […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
9 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

The


socket

is the software abstraction used to represent the “terminals” of a


connection between two machines. For a given connection, there’s a socket


on each machine, and you can imagine a hypothetical “cable” running


between the two machines with each end of the “cable” plugged into


a socket. Of course, the physical hardware and cabling between machines is


completely unknown. The whole point of the abstraction is that we don’t


have to know more than is necessary.

In


Java, you create a socket to make the connection to the other machine, then you


get an


InputStream

and


OutputStream

(or, with the appropriate converters,


Reader

and


Writer

)


from


the socket in order to be able to treat the connection as an IO stream object.


There are two stream-based socket classes: a


ServerSocket

that a server uses to “listen” for incoming connections and a


Socket

that a client uses in order to initiate a connection. Once a client makes a


socket connection, the


ServerSocket

returns (via the


accept( )
method)
a corresponding server side
Socket
through which direct communications will take place. From then on, you have a
true
Socket
to
Socket
connection and you treat both ends the same way because they
are
the same. At this point, you use the methods
getInputStream( )
and
getOutputStream( )
to produce the corresponding
InputStream
and
OutputStream
objects from each
Socket.
These must be wrapped inside buffers and formatting classes just like any other
stream object described in Chapter 10.

The


use of the term


ServerSocket

would seem to be another example of a confusing name scheme in the Java


libraries. You might think


ServerSocket

would be better named “ServerConnector” or something without the


word “Socket” in it. You might also think that


ServerSocket

and


Socket

should both be inherited from some common base class. Indeed, the two classes


do have several methods in common but not enough to give them a common base


class. Instead,


ServerSocket

’s


job is to wait until some other machine connects to it, then to return an actual


Socket

.


This is why


ServerSocket

seems to be a bit misnamed, since its job isn’t really to be a socket but


instead to make a


Socket

object when someone else connects to it.

However,


the


ServerSocket

does create a physical “server” or listening socket on the host


machine. This socket listens for incoming connections and then returns an


“established” socket (with the local and remote endpoints defined)


via the


accept( )

method. The confusing part is that both of these sockets (listening and


established) are associated with the same server socket. The listening socket


can accept only new connection requests and not data packets. So while


ServerSocket

doesn’t make much sense programmatically, it does “physically.”

When


you create a


ServerSocket

,


you give it only a port number. You don’t have to give it an IP address


because it’s already on the machine it represents. When you create a


Socket

,


however, you must give both the IP address and the port number where


you’re trying to connect. (On the other hand, the


Socket

that comes back from


ServerSocket.accept( )

already contains all this information.)


A
simple server and client

This


example makes the simplest use of servers and clients using sockets. All the


server does is wait for a connection, then uses the


Socket

produced by that connection to create an


InputStream

and


OutputStream

.


After that, everything it reads from the


InputStream

it echoes to the


OutputStream

until it receives the line END, at which time it closes the connection.

The


client makes the connection to the server, then creates an


OutputStream

.


Lines of text are sent through the


OutputStream

.


The client also creates an


InputStream

to hear what the server is saying (which, in this case, is just the words


echoed back).

Both


the server and client use the same port number and the client uses the local


loopback address to connect to the server on the same machine so you


don’t have to test it over a network. (For some configurations, you might


need to be


connected

to a network for the programs to work, even if you aren’t communicating


over that network.)

Here


is the server:

//: JabberServer.java
// Very simple server that just
// echoes whatever the client sends.
import java.io.*;
import java.net.*;
 
public class JabberServer {
  // Choose a port outside of the range 1-1024:
  public static final int PORT = 8080;
  public static void main(String[] args)
      throws IOException {
    ServerSocket s = new ServerSocket(PORT);
    System.out.println("Started: " + s);
    try {
      // Blocks until a connection occurs:
      Socket socket = s.accept();
      try {
        System.out.println(
          "Connection accepted: "+ socket);
        BufferedReader in =
          new BufferedReader(
            new InputStreamReader(
              socket.getInputStream()));
        // Output is automatically flushed
        // by PrintWriter:
        PrintWriter out =
          new PrintWriter(
            new BufferedWriter(
              new OutputStreamWriter(
                socket.getOutputStream())),true);
        while (true) {
          String str = in.readLine();
          if (str.equals("END")) break;
          System.out.println("Echoing: " + str);
          out.println(str);
        }
      // Always close the two sockets...
      } finally {
        System.out.println("closing...");
        socket.close();
      }
    } finally {
      s.close();
    }
  }
} ///:~ 

You


can see that the


ServerSocket

just needs a port number, not an IP address (since it’s running on


this

machine!). When you call


accept( )

,


the method


blocks

until some client tries to connect to it. That is, it’s there waiting for


a connection but other processes can run (see Chapter 14). When a connection is


made,


accept( )

returns with a


Socket

object representing that connection.

The


responsibility for cleaning up the sockets is crafted carefully here. If the


ServerSocket

constructor fails, the program just quits (notice we must assume that the


constructor for


ServerSocket

doesn’t leave any open network sockets lying around if it fails). For


this case,


main( )
throws
IOException

so a


try

block is not necessary. If the


ServerSocket

constructor is successful then all other method calls must be guarded in a


try-finally

block to ensure that, no matter how the block is left, the


ServerSocket

is properly closed.

The


same logic is used for the


Socket

returned by


accept( )

.


If


accept( )

fails, then we must assume that the


Socket

doesn’t exist or hold any resources, so it doesn’t need to be


cleaned up. If it’s successful, however, the following statements must be


in a


try-finally

block so that if they fail the


Socket

will still be cleaned up. Care is required here because sockets use important


non-memory resources, so you must be diligent in order to clean them up (since


there is no destructor in Java to do it for you).

Both


the


ServerSocket

and the


Socket

produced by


accept( )

are printed to


System.out

.


This means that their


toString( )

methods are automatically called. These produce:

ServerSocket[addr=0.0.0.0,PORT=0,localport=8080]
Socket[addr=127.0.0.1,PORT=1077,localport=8080]

Shortly,


you’ll see how these fit together with what the client is doing.

The


next part of the program looks just like opening files for reading and writing


except that the


InputStream

and


OutputStream

are created from the


Socket

object. Both the


InputStream

and


OutputStream

objects


are converted to Java 1.1


Reader
and
Writer
objects using the “converter” classes
InputStreamReader
and
OutputStreamWriter,
respectively. You could also have used the Java 1.0

InputStream
and
OutputStream
classes directly, but with output there’s a distinct advantage to using
the
Writer
approach. This appears with
PrintWriter,
which has an overloaded constructor that takes a second argument, a
boolean
flag
that indicates whether to automatically flush the output at the end of each
println( )
(but
not
print( ))
statement. Every time you write to
out,
its buffer must be flushed so the information goes out over the network.
Flushing is important for this particular example because the client and server
each wait for a line from the other party before proceeding. If flushing
doesn’t occur, the information will not be put onto the network until the
buffer is full, which causes lots of problems in this example.

When


writing network programs you need to be careful about using automatic flushing.


Every time you flush the buffer a packet must be created and sent. In this


case, that’s exactly what we want, since if the packet containing the


line isn’t sent then the handshaking back and forth between server and


client will stop. Put another way, the end of a line is the end of a message.


But in many cases messages aren’t delimited by lines so it’s much


more efficient to not use auto flushing and instead let the built-in buffering


decide when to build and send a packet. This way, larger packets can be sent


and the process will be faster.

Note


that, like virtually all streams you open, these are buffered. There’s an


exercise at the end of the chapter to show you what happens if you don’t


buffer the streams (things get slow).

The


infinite


while

loop reads lines from the



BufferedReader in

and


writes information to


System.out

and to the


PrintWriter
out

.


Note that these could be any streams, they just happen to be connected to the


network.

When


the client sends the line consisting of “END” the program breaks


out of the loop and closes the


Socket

.

Here’s


the client:

//: JabberClient.java
// Very simple client that just sends
// lines to the server and reads lines
// that the server sends.
import java.net.*;
import java.io.*;
 
public class JabberClient {
  public static void main(String[] args)
      throws IOException {
    // Passing null to getByName() produces the
    // special "Local Loopback" IP address, for
    // testing on one machine w/o a network:
    InetAddress addr =
      InetAddress.getByName(null);
    // Alternatively, you can use 
    // the address or name:
    // InetAddress addr = 
    //    InetAddress.getByName("127.0.0.1");
    // InetAddress addr = 
    //    InetAddress.getByName("localhost");
    System.out.println("addr = " + addr);
    Socket socket =
      new Socket(addr, JabberServer.PORT);
    // Guard everything in a try-finally to make
    // sure that the socket is closed:
    try {
      System.out.println("socket = " + socket);
      BufferedReader in =
        new BufferedReader(
          new InputStreamReader(
            socket.getInputStream()));
      // Output is automatically flushed
      // by PrintWriter:
      PrintWriter out =
        new PrintWriter(
          new BufferedWriter(
            new OutputStreamWriter(
              socket.getOutputStream())),true);
      for(int i = 0; i < 10; i ++) {
        out.println("howdy " + i);
        String str = in.readLine();
        System.out.println(str);
      }
      out.println("END");
    } finally {
      System.out.println("closing...");
      socket.close();
    }
  }
} ///:~ 

In


main( )

you can see all three ways to produce the


InetAddress

of the local loopback IP address: using


null

,


localhost

,


or the explicit reserved address


127.0.0.1

.


Of course, if you want to connect to a machine across a network you substitute


that machine’s IP address. When the


InetAddress
addr

is printed (via the automatic call to its


toString( )

method) the result is:

localhost/127.0.0.1

By


handing


getByName( )

a


null

,


it defaulted to finding the


localhost

,


and that produced the special address


127.0.0.1

.

Note


that the

Socket
called
socket
is created with both the
InetAddress
and the port number. To understand what it means when you print out one of these
Socket
objects,
remember that an Internet connection is determined uniquely by these four
pieces of data:
clientHost,
clientPortNumber,
serverHost,
and
serverPortNumber.
When the server comes up, it takes up its assigned port (8080) on the localhost
(127.0.0.1). When the client comes up, it is allocated to the next available
port on its machine, 1077 in this case, which also happens to be on the same
machine (127.0.0.1) as the server. Now, in order for data to move between the
client and server, each side has to know where to send it. Therefore, during
the process of connecting to the “known” server, the client sends a
“return address” so the server knows where to send its data. This
is what you see in the example output for the server side:
Socket[addr=127.0.0.1,port=1077,localport=8080]

This


means that the server just accepted a connection from 127.0.0.1 on port 1077


while listening on its local port (8080). On the client side:

Socket[addr=localhost/127.0.0.1,PORT=8080,localport=1077]

which


means that the client made a connection to 127.0.0.1 on port 8080 using the


local port 1077.

You’ll


notice that every time you start up the client anew, the local port number is


incremented. It starts at 1025 (one past the reserved block of ports) and keeps


going up until you reboot the machine, at which point it starts at 1025 again.


(On UNIX machines, once the upper limit of the socket range is reached, the


numbers will wrap around to the lowest available number again.)

Once


the


Socket

object has been created, the process of turning it into a


BufferedReader

and


PrintWriter

is the same as in the server (again, in both cases you start with a


Socket

).


Here, the client initiates the conversation by sending the string


“howdy” followed by a number. Note that the buffer must again be


flushed (which happens automatically via the second argument to the


PrintWriter

constructor).


If the buffer isn’t flushed, the whole conversation will hang because the


initial “howdy” will never get sent (the buffer isn’t full


enough to cause the send to happen automatically). Each line that is sent back


from the server is written to


System.out

to verify that everything is working correctly. To terminate the conversation,


the agreed-upon “END” is sent. If the client simply hangs up, then


the server throws an exception.

You


can see that the same care is taken here to ensure that the network resources


represented by the


Socket

are properly cleaned up, using a


try-finally

block.

Sockets


produce a

“dedicated”
connection that persists until it is explicitly disconnected. (The dedicated
connection can still be disconnected un-explicitly if one side, or an
intermediary link, of the connection crashes.) This means the two parties are
locked in communication and the connection is constantly open. This seems like
a logical approach to networking, but it puts an extra load on the network.
Later in the chapter you’ll see a different approach to networking, in
which the connections are only temporary.
Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.