Communicating with a server socket

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 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.

Listing 6.2 A simple echo server for demonstrating socket usage 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();

} O Implement public void run () { <1-1 run to start try { Q USe

ServerSocket server = new ServerSocket (PORT, 1); <1-1 java.net.ServerSocket while (true) {

System. out .println ("Client connected"); for each client while (true) {

Use java.net.Socket

BufferedReader reader =

new Buf f eredReader (new InputStreamReader ( E Read input with client.getInputStream() )) ; <1-1 BufferedReader

System.out.println("Read from client"); String textLine = reader.readLine() + "\n";

if (textLine.equalsIgnoreCase("EXIT\n")) { <-

System.out.println("EXIT invoked, closing client");

}break; EXIT, break the loop 0

BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(

client.getOutputStream() )) ; <-1 jend echo with

System. out .println ("Echo input to client"); ^fc BufferedWriter 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 run O, so that each client that connects can be handled in its own context. Then we use a ServerSocket Q to listen on a defined port. Each client is then an implementation of a Socket Q. The client input is fed into a BufferedReader that each line is read from Q. The only special consideration this simple server has is that if the input is EXIT, it breaks the loops and exits Q. If the input does not prompt an exit, the server echoes the input back to the client's OuputStream with a BufferedWriter Q.

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 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 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.

Listing 6.3 An Android client invoking a raw socket server resource, the echo server 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(), socketlnput.getText().toString()); socketOutput.setText(output); <-} C Set view output

private String callSocket(String ip, String port, String socketData) { Socket socket = null; BufferedWriter writer = null; BufferedReader reader = null; String output = null;

A Use callSocket <_I method try {

socket = new Socket(ip, Integer .parseInt (port) ) ; <1-1 Create client writer = new Buf feredWriter ( © Socket new OutputStreamWriter( socket.getOutputStream())) ; <—© Establish BufferedWriter for input reader = new BufferedReader( new InputStreamReader( socket.getInputStream())) ; <—F Establish BufferedReader for output

String input = socketData;

writer. write (input + "\n", 0, input. length () + 1); <1-1 Write to writer. flush () ; © socket output = reader. readLine () ; <1-1 Get socket this . socketOutput. setText (output) ; © 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 O 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 ©, which communicates with the server, and get the output value to return ©.

A socket is probably the lowest-level networking usage in Android you will encounter. 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.

6.4 Working with HTTP

As we discussed in the previous section, you can use a raw socket to transfer IP data to and from a server with Android. This is an important approach to be aware of so that you know you have that option and so that you understand a bit about the underlying details. Nevertheless, you may want to avoid this technique where possible and instead take advantage of existing server products to send your data. The most common way to do this is to use a web server and leverage HTTP.

Here we are going to take a look at making HTTP requests from an Android client and sending them to an HTTP server. We will let the HTTP server handle all the socket details, and we will focus on our client Android application.

The HTTP protocol itself is fairly involved. If you are unfamiliar with it and or want the complete details, they are readily available via RFCs (such as for version 1.1:

http://www.w3.org/Protocols/rfc2616/rfc2616.html). The short story is that the protocol is stateless and involves several different methods that allow users to make requests to servers, and those servers return responses. The entire web is, of course, based on HTTP. Beyond the most basic concepts, there are ways to pass data into and out of requests and responses and to authenticate with servers. Here we are going to use some of the most common methods and concepts to talk to network resources from Android applications.

To begin we will retrieve data using HTTP GET requests to a simple HTML page using the standard java.net API. From there we will look at using the Android-included Apache HttpClient API. After we use HttpClient directly to get a feel for it, we will also make a helper class, HttpRequestHelper, that we can use to simplify the process and encapsulate the details. This class—and the Apache networking API in general—has a few advantages over rolling your own networking with java.net, as we shall see. Once the helper class is in place, we will use it to make additional HTTP and HTTPS requests, both GET and POST, and we will look at basic authentication.

Our first HTTP request will be an HTTP GET call using a HttpUrlConnection.

6.4.1 Simple HTTP and java.net

The most basic HTTP request method is a GET. In this type of request any data that is sent is embedded in the URL using the query string. The next class in our NetworkExplorer application, which is shown in listing 6.4, has an Activity that demonstrates this.

Listing 6.4 The SimpleGet Activity showing java.net.UrlConnection public class SimpleGet extends Activity {

. . . other portions of onCreate omitted for brevity this.getButton.setOnClickListener (new OnClickListener() { public void onClick(View v) { getOutput.setText(""); String output =

getHttpResponse (getInput. getText () . toString () ) ; <1— if (output != null) {

getOutput. setText (output) ; Invoke

} getHttpResponse method O

private String getHttpResponse(String location) { String result = null; URL url = null;

try { C Construct URL

} catch (MalformedURLException e) { / / log and or handle

try { © °pen connection using HttpURLConnection

HttpURLConnection urlConn =

(HttpURLConnection) url.openConnection(); BufferedReader in =

new BufferedReader( new InputStreamReader ( © Create BufferedReader urlConn.getInputStream () )) ; <-I for output

String inputLine;

int lineCount = 0; // limit lines for example while ((lineCount < 10)

&& ((inputLine = in. readLine () ) != null)) { <—© Read data lineCount++;

result += "\n" + inputLine; <- Append to result

in .close () ; H Close reader urlConn.disconnect () ; y and connection

// log and or handle

return result;

In order to get an HTTP response and show the first few lines of it in our SimpleGet class, we are calling a getHttpResponse method that we have built O. Within this method we construct a java.net.URL object ©, which takes care of many of the details for us, and then we open a connection to a server using an HttpURLConnection ©.

We then use a BufferedReader © to read data from the connection one line at a time ©. Keep in mind that as we are doing this, we are using the same thread as the UI and therefore blocking the UI. This isn't a good idea. We are doing this here only to demonstrate the network operation; we will explain more about how to use a separate thread for this shortly. Once we have the data, we append it to the result String that our method returns G, and we close the reader and the connection H. Using the plain and simple java.net support that has been ported to Android this way provides quick and dirty access to HTTP network resources.

Communicating with HTTP this way is fairly easy, but it can quickly get cumbersome when you need to do more than just retrieve simple data, and, as noted, the blocking nature of the call is bad form. We could get around some of the problems with this approach on our own by spawning separate threads and keeping track of them and by writing our own small framework/API structure around that concept for each HTTP request, but we don't have to. Fortunately, Android provides another set of APIs in the form of the Apache HttpClient library that abstract the java.net classes further and that are designed to offer more robust HTTP support and help handle the separate-thread issue.

6.4.2 Robust HTTP with HttpClient

To get started with HttpClient we are going to look at using core classes to perform HTTP GET and POST method requests. Here we will concentrate on making network requests in a Thread separate from the UI, using a combination of the Apache ResponseHandler and Android Handler (for different but related purposes, as we shall see). Listing 6.5 shows our first example of using the HttpClient API.

Listing 6.5 Apache HttpClient with Android Handler and Apache ResponseHandler private final Handler handler = new Handler() { public void handleMessage(Message msg) { progressDialog.dismiss(); String bundleResult =

msg.getData().getString("RESPONSE"); output.setText(bundleResult);

A Create Android _I Handler

Use Handler to update UI

. . . onCreate omitted for brevity private void performRequest () {

final ResponseHandler<String> responseHandler = new ResponseHandler<String> () { <1-

public String handleResponse(HttpResponse response) { StatusLine status = response.getStatusLine(); HttpEntity entity = response.getEntity(); String result = null; try {

result = StringUtils.inputStreamToString(

Message message = handler.obtainMessage(); F Bundle bundle = new Bundle(); bundle.putString("RESPONSE", result); message.setData(bundle); handler.sendMessage(message); } catch (IOException e) { / / log and or handle

return result;

D Create

ResponseHandler for asynchronous HTTP

Implement onResponse callback E

Get HTTP response payload

this.progressDialog = ProgressDialog.show( this, "working . "performing HTTP request");

Use a separate Thread for HTTP call new Thread() {

DefaultHttpClient client = new DefaultHttpClient() HttpGet httpMethod = new HttpGet( urlChooser.getSelectedItem().toString());

Create

HttpGet object

client.execute( httpMethod, responseHandler); } catch (ClientProtocolException e) {

/ / log and or handle } catch (IOException e) { // log and or handle

Execute HTTP with HttpClient

The first thing we do in our initial HttpClient example is create a Handler that we can send messages to from other threads O. This is the same technique we have used in previous examples, and it is used to allow background tasks to send Message objects to hook back into the main UI thread ©. After we create an Android Handler, we also create an Apache ResponseHandler ©. This class can be used with HttpClient HTTP requests to pass in as a callback point. When an HTTP request that is fired by HttpClient completes, it will call the onResponse method (if a ResponseHandler is used) ©. When the response does come in, we then get the payload using the HttpEntity the API returns This in effect allows the HTTP call to be made in an asynchronous manner—we don't have to block and wait the entire time between when the request is fired and when it completes. The relationship of the request, response, Handler, Response-Handler, and separate threads is diagrammed in figure 6.3.

Now that you have seen HttpClient at work and understand the basic approach, the next thing we will do is encapsulate a few of the details into a convenient helper class so that we can call it over and over without having to repeat a lot of the setup.

Figure 6.3 HttpClient, ResponseHandler, and Android Handler relationship diagram

6.4.3 Creating an HTTP and HTTPS helper

The next Activity in our NetworkExplorer application, which is shown in listing 6.6, is a lot more straightforward and pure Android focused than our other HTTP-related classes up to this point. This is made possible by the helper class we mentioned previously, which hides some of the complexity (we will examine the helper class itself after we look at this first class that uses it).

Listing 6.6 Using Apache HttpClient via a custom HttpRequestHelper public class ApacheHTTPViaHelper extends Activity {

. . . other member variables omitted for brevity ^^ Create a private final Handler handler = new Handler () { <-' Handler public void handleMessage(Message msg) { progressDialog.dismiss();

String bundleResult = msg.getData().getString("RESPONSE"); output.setText(bundleResult); <3-1

@Override public void onCreate (final Bundle icicle) { super.onCreate(icicle) ;

. . . view inflation and setup omitted for brevity this.button.setOnClickListener(new OnClickListener() { public void onClick(final View v) { output.setText("");

performRequest ( © Cal1 local urlChooser. getSelectedI tem () .toString() ) ; <-I performRequeSt:

. . . onPause omitted for brevity private void performRequest(String url) {

final ResponseHandler<String> responseHandler = HTTPRequestHelper. getResponseHandlerInstance ( © Get ReSponseHandler thishandler); <-1 from RequestHelper this.progressDialog = ProgressDialog.show( this, "working . . .", "performing HTTP request");

HTTPRequestHelper helper = new © |nstantiate RequestHelper

HTTPRequestHelper (responseHandler) ; <-1 with ResponseHand|er helper.performGet(url, null, null, null); <-1

} I Perform HTTP

First in this class we create another Handler O, and from within it we simply update a UI TextView based on data in the Message ©. Further in the code, in the onCreate method, we call a local performRequest method when the "go" button is clicked, and we pass a selected String representing a URL Q.

Inside the performRequest method we use a static convenience method to return an HttpClient ResponseHandler, passing in our Android Handler, which it will use E . We will examine the helper class next to get a look at exactly how this works, but the important part for now is that the ResponseHandler is created for us by the static method. With the ResponseHandler instance taken care of, we instantiate an HttpRequestHelper instance Q and use it to make a simple HTTP GET call (passing in only the String URL) Q. Similar to our previous example, when the request completes, the Response-Handler will fire the onResponse method, and therein our Handler will be sent a Message completing the process.

The example Activity in listing 6.6 is fairly clean and simple, and it's asynchronous and doesn't block the UI thread. The heavy lifting is taken care of by HttpClient itself and by the setup our custom HttpRequestHelper makes possible. The first part of the all-important HttpRequestHelper, which we will explore in three sections, is shown in listing 6.7.

Listing 6.7 The first part of the HttpRequestHelper class public class HTTPRequestHelper {

private static final int POST_TYPE = 1; private static final int GET_TYPE = 2;

private static final String CONTENT_TYPE = "Content-Type" public static final String MIME_FORM_ENCODED =

"application/x-www-form-urlencoded"; public static final String MIME_TEXT_PLAIN = "text/plain"

private final ResponseHandler<String> responseHandler;

Require ResponseHandler to construct o public HTTPRequestHelper(ResponseHandler<String> responseHandler) {

this . responseHandler = responseHandler; <1-

public void performGet(String url, String user, String pass, final Map<String, String> additionalHeaders) { -1 Provide performRequest (null, url, user, pass, | simple GET

additionalHeaders, null, HTTPRequestHelper. GET_TYPE); © method

public void performPost(String contentType, String url, String user, String pass,

Map<String, String> additionalHeaders, Q Provide simple

Map<String, String> params) { <1-1 POST methods performRequest(contentType, url, user, pass, additionalHeaders, params, HTTPRequestHelper.POST_TYPE);

public void performPost(String url, String user, String pass, Map<String, String> additionalHeaders,

Map<String, String> params) { performRequest(HTTPRequestHelper. MIME_FORM_ENCODED, url, user, pass, additionalHeaders, params, HTTPRequestHelper.POST_TYPE)

private void performRequest( String contentType, String url, String user, String pass,

Map<String, String> headers, Map<String, String> params, int requestType) {

Provide simple POST methods

Handle combinations in private method

DefaultHttpClient client = new DefaultHttpClient()

client.getCredentialsProvider().setCredentials(

AuthScope .ANY, new UsernamePasswordCredentials(user, pass));

Instantiate DefaultHttpClient

G Add credentials if needed final Map<String, String> sendHeaders =

new HashMap<String, String>(); if ((headers != null) && (headers.size () > 0)) { sendHeaders.putAll(headers);

if (requestType == HTTPRequestHelper.POST_TYPE) {

sendHeaders.put(HTTPRequestHelper.CONTENT_TYPE, contentType);

client.addRequestInterceptor( new HttpRequestInterceptor() { public void process( final HttpRequest request, final HttpContext context) throws HttpException, IOException {

for (String key : sendHeaders.keySet()) if (!request.containsHeader(key)) { request.addHeader(key, sendHeaders.get(key));

H Use Interceptor for <]_I request headers

. POST and GET execution in listing 6.8

The first thing of note in the HttpRequestHelper class is that a ResponseHandler is required to be passed in as part of the constructor O. This ResponseHandler will be used when the HttpClient request is ultimately invoked. After the constructor, we see a public HTTP GET-related method © and several different public HTTP POST-related methods ©. Each of these methods is a wrapper around the private performRequest method that can handle all the HTTP options ©. The performRequest method supports a content-type header value, URL, username, password, Map of additional headers, similar Map of request parameters, and request method type.

Inside the performRequest method a DefaultHttpClient is instantiated Q. Next, we check to see if the user and pass method parameters are present, and if so we set the request credentials with a UsernamePasswordCredentials type (HttpClient supports several types of credentials, see the Javadocs for details) ©. At the same time we set the credentials, we also set an AuthScope. The scope represents which server, port, authentication realm, and authentication scheme the credentials supplied are applicable for.

You can set these as fine or coarse grained as you want; we are using the default ANY scope that matches anything. What we notably have not set in all of this is the specific authentication scheme to use. HttpClient supports various schemes, including basic authentication, digest authentication, and a Windows-specific NTLM scheme. Basic authentication, meaning simple username/password challenge from the server, is the default. (Also, if you need to, you can use a preemptive form login for form-based authentication—just submit the form you need and get the token or session ID and so on.)

After the security is out of the way, we use an HttpRequestlnterceptor to add HTTP headers Q. Headers are name/value pairs, so this is pretty easy. Once we have all of these properties that apply regardless of our request method type, we then add further settings that are specific to the method. Listing 6.8, the second part of our helper class, shows the POST- and GET-specific settings and the execute method.

Listing 6.8 The second part of the HttpRequestHelper class

B Handle POST

if (requestType == HTTPRequestHelper. POST_TYPE) { <-I requests

List<NameValuePair> nvps = null; I Create HttpPost if ( (params != null) && (params . size () > 0)) { C object nvps = new ArrayList<NameValuePair>(); for (String key : params.keyset()) {

nvps.add(new BasicNameValuePair(key, params ,get (key))) ; < I Add name/value

} D parameters

method.setEntity( new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); } catch (UnsupportedEncodingException e) { // log and or handle

} } A Call execute execute (client, method); <-I method

} else if (requestType == HTTPRequestHelper. GET_TYPE) { HttpGet method = new HttpGet(url); execute(client, method);

private void execute(HttpClient client, HttpRequestBase method) { BasicHttpResponse errorResponse = new BasicHttpResponse( new ProtocolVersion ( "HTTP_ERROR" , 1, 1), © Set up an 500, "ERROR"); <-1 error handler try {

client. execute (method, this . responseHandler) ; <1-1 Call HttpClient

} catch (Exception e) { © execute errorResponse.setReasonPhrase(e.getMessage()); try {

this.responseHandler.handleResponse(errorResponse); } catch (Exception ex) { / / log and or handle

When the specified request is a POST type ©, we create an HttpPost object to deal with it ©. Then we add POST request parameters, which are another set of name/ value pairs and are built with the BasicNameValuePair object ©. After adding the parameters we are ready to perform the request, which we do with our local private execute method using the method object and the client ©.

Our execute method sets up an error response handler (we want to return a response, error or not, so we set this up in case) © and wraps the HttpClient execute method, which requires a method object (either POST or GET in our case, preestab-lished) and a ResponseHandler as input ©. If we don't get an exception when we invoke HttpClient execute, all is well and the response details are placed into the ResponseHandler. If we do get an exception, we populate the error handler and pass it through to the ResponseHandler.

We call the local private execute method with the established details for either a POST or a GET request. The GET method is handled similarly to the POST, but we don't set parameters (with GET requests we expect parameters encoded in the URL itself). Right now our class supports only POST and GET (which cover 98 percent of the requests we generally need), but it certainly could be easily expanded to support other HTTP method types.

The final part of the request helper class, shown in listing 6.9, takes us back to the first example that used the helper, as it outlines exactly what the convenience getRe-sponseHandlerInstance method returns (constructing our helper requires a ResponseHandler, and this method returns a default one).

Listing 6.9 The final part of the HttpRequestHelper class public static ResponseHandler<String>

getResponseHandlerInstance (final Handler handler) { <1-1 Require Handler final ResponseHandler<String> responseHandler = © parameter new ResponseHandler<String>() {

public String handleResponse(final HttpResponse response) { Message message = handler.obtainMessage(); Bundle bundle = new Bundle();

StatusLine status = response.getStatusLine(); HttpEntity entity = response.getEntity(); String result = null; if (entity != null) { try {

result = StringUtils . inputStreamToString( © Get response entity .getContent () ) ; <-1 content 3s String bundle.putString(

"response", result); <— Put result value into Bundle message.setData(bundle);

1 Set Bundle as data

} catch (IOException e) { i j^g Messa e bundle.putString("

RESPONSE", "Error - " + e.getMessage()); message.setData(bundle); handler.sendMessage(message);

bundle.putString("RESPONSE", "Error - "

+ response.getStatusLine().getReasonPhrase()); message.setData(bundle);

handler. sendMessage (message) ; <-, jenj Message

} | via Handler return result;

return responseHandler;

As we discuss the getResponseHandlerlnstance method of our helper, we should note that although we find it helpful, it's entirely optional. You can still make use of the helper class without using this method. To do so, construct your own Response-Handler and pass it in to the helper constructor—which is a perfectly plausible case. The getResponseHandlerlnstance method builds a convenient default Response-Handler that hooks in a Handler via a parameter © and parses the response as a String ©. The response String is sent back to the caller using the Handler Bundle and Message pattern we have seen used time and time again to pass messages between threads in our Android screens.

With the gory HttpRequestHelper details out of the way, and having already explored basic usage, we will next turn to more involved uses of this class in the context of web service calls.

0 0

Post a comment