A server socket is a stream that you can read or write raw bytes to, at a specified IP address and port. You can deal with data and not worry about media types, packet sizes, and so on. A server socket is yet another network abstraction intended to make the programmer’s job a bit easier. The philosophy that sockets take on—that every- thing should look like file input/output (I/O) to the developer—comes from the Por- table Operating System Interface for UNIX (POSIX) family of standards and has been adopted by most major operating systems in use today.
We’ll move on to higher levels of network communication in a bit, but we’ll start with a raw socket. For that, we need a server listening on a particular port. The
Listing 6.1 The onStart method of the NetworkExplorer main Activity
Figure 6.2 The output of the NetworkInfotoString() method
EchoServer code shown in the next listing fits the bill. This example isn’t an Android- specific class; rather, it’s an oversimplified server that can run on any host machine with Java. We’ll connect to it later from an Android client.
public final class EchoServer extends Thread { private static final int PORT = 8889;
private EchoServer() {}
public static void main(String args[]) { EchoServer echoServer = new EchoServer();
if (echoServer != null) { echoServer.start();
} }
public void run() { try {
ServerSocket server = new ServerSocket(PORT, 1);
while (true) {
Socket client = server.accept();
System.out.println("Client connected");
while (true) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(
client.getInputStream()));
System.out.println("Read from client");
String textLine = reader.readLine() + "\n";
if (textLine.equalsIgnoreCase("EXIT\n")) {
System.out.println("EXIT invoked, closing client");
break;
}
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
client.getOutputStream()));
System.out.println("Echo input to client");
writer.write("ECHO from server: "
+ textLine, 0, textLine.length() + 18);
writer.flush();
}
client.close();
}
} catch (IOException e) { System.err.println(e);
} }
}
The EchoServer class we’re using is fairly basic Java I/O. It extends Thread and imple- ments run, so that each client that connects can be handled in its own context. Then we use a ServerSocket B to listen on a defined port. Each client is then an imple- mentation of a Socket. The client input is fed into a BufferedReader that each line is read from C. The only special consideration this simple server has is that if the input
Listing 6.2 A simple echo server for demonstrating socket usage
B
Use java.net.ServerSocket
C
Read input with BufferedReader
EXIT, break D
the loop
is EXIT, it breaks the loops and exits D. If the input doesn’t prompt an exit, the server echoes the input back to the client’s OuputStream with a BufferedWriter.
This example is a good, albeit intentionally basic, representation of what a server does. It handles input, usually in a separate thread, and then responds to the client, based on the input. To try out this server before using Android, you can telnet to the specified port (after the server is running, of course) and type some input; if all is well, it will echo the output.
To run the server, you need to invoke it locally with Java. The server has a main method, so it’ll run on its own; start it from the command line or from your IDE. Be aware that when you connect to a server from the emulator (this one or any other), you need to connect to the IP address of the host you run the server process on, not the loopback (not 127.0.0.1). The emulator thinks of itself as 127.0.0.1, so use the nonloopback address of the server host when you attempt to connect from Android.
(You can find out the IP address of the machine you’re on from the command line by entering ifconfig on Linux or Mac and ipconfig on Windows.)
The client portion of this example is where NetworkExplorer itself begins, with the callSocket() method of the SimpleSocketActivity, shown in the next listing.
public class SimpleSocket extends Activity {
. . . View variable declarations omitted for brevity @Override
public void onCreate(final Bundle icicle) { super.onCreate(icicle);
this.setContentView(R.layout.simple_socket);
. . . View inflation omitted for brevity
this.socketButton.setOnClickListener(new OnClickListener() { public void onClick(final View v) {
socketOutput.setText("");
String output = callSocket(
ipAddress.getText().toString(), port.getText().toString(), socketInput.getText().toString());
socketOutput.setText(output);
} });
}
private String callSocket(String ip, String port, String socketData) { Socket socket = null;
BufferedWriter writer = null;
BufferedReader reader = null;
String output = null;
try {
socket = new Socket(ip, Integer.parseInt(port));
writer = new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream()));
reader = new BufferedReader(
Listing 6.3 An Android client invoking a raw socket server resource, the echo server
Use callSocket method
B
Create client Socket
C
new InputStreamReader(
socket.getInputStream()));
String input = socketData;
writer.write(input + "\n", 0, input.length() + 1);
writer.flush();
output = reader.readLine();
this.socketOutput.setText(output);
// send EXIT and close writer.write("EXIT\n", 0, 5);
writer.flush();
. . . catches and reader, writer, and socket closes omitted for brevity . . . onCreate omitted for brevity
return output;
}
In this listing, we use the onCreate() method to call a private helper callSocket() method B and set the output to a TextView. Within the callSocket() method, we create a socket to represent the client side of our connection C, and we establish a writer for the input and a reader for the output. With the housekeeping taken care of, we then write to the socket D, which communicates with the server, and get the out- put value to return E.
A socket is probably the lowest-level networking usage in Android you’ll encounter.
Using a raw socket, though abstracted a great deal, still leaves many of the details up to you, especially the server-side details of threading and queuing. Although you might run up against situations in which you either have to use a raw socket (the server side is already built) or elect to use one for one reason or another, higher-level solutions such as leveraging HTTP usually have decided advantages.