A server socket is a stream that you can read or write raw bytes to, at a specified IP address and port. This lets you deal with data and not worry about media types, packet sizes, and so on. This is yet another network abstraction intended to make the job of the programmer a bit easier. The philosophy that sockets take on, that everything should look like file I/O to the developer, comes from the POSIX family of standards and has been adopted by most major operating systems in use today.
We will move on to higher levels of network communication in a bit, but first we will start with a raw socket. For that we need a server listening on a particular port.
The EchoServer code shown in listing 6.2 fits the bill. This isn’t an Android-specific Listing 6.1 The onStart method of the NetworkExplorer main Activity
Obtain manager from Context
B
C Get NetworkInfo
Figure 6.2 The output of the NetworkInfo toString method.
class; rather it’s just an oversimplified server that can run on any host machine with Java. We’ll later connect to it 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 are using is fairly basic Java I/O. It extends Thread and implements runB, so that each client that connects can be handled in its own con- text. Then we use a ServerSocketC to listen on a defined port. Each client is then
Listing 6.2 A simple echo server for demonstrating socket usage
Implement run to start
B
Use
java.net.ServerSocket
C
D Use java.net.Socket for each client
Read input with BufferedReader
E
F
EXIT, break the loop
G Send echo with BufferedWriter
an implementation of a Socket D. The client input is fed into a BufferedReader that each line is read from E. The only special consideration this simple server has is that if the input is EXIT, it breaks the loops and exits F. If the input does not prompt an exit, the server echoes the input back to the client’s OuputStream with a BufferedWriterG.
This is a good, albeit intentionally very basic, representation of what a server does.
It handles input, usually in a separate thread, 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. It has a main method, so it will 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 or any other, you need to con- nect 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 non-loopback address of the server host when you attempt to connect from Android. (You can find out the IP address of the machine you are 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 SimpleSocket Activity shown in listing 6.3.
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;
Listing 6.3 An Android client invoking a raw socket server resource, the echo server
Use callSocket method
B
C Set view output
try {
socket = new Socket(ip, Integer.parseInt(port));
writer = new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream()));
reader = new BufferedReader(
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;
}
Here we use the onCreate method to call a private helper callSocket method B
and set the output to a TextView C. Within the callSocket method we create a Socket to represent the client side of our connection D, and we establish a writer for the input E and a reader for the output F. With the housekeeping taken care of, we then write to the socket G, which communicates with the server, and get the output value to return H.
A socket is probably the lowest-level networking usage in Android you will encoun- ter. Using a raw socket, while abstracted a great deal, still leaves many of the details up to you (especially server-side details, threading, and queuing). Although you may run up against situations in which either you have to use a raw socket (the server side is already built) or you elect to use one for one reason or another, higher-level solutions such as leveraging HTTP normally have decided advantages.