Tải bản đầy đủ
3 What time is it? The DayTime Server

3 What time is it? The DayTime Server

Tải bản đầy đủ

371

What time is it? The DayTime Server

In addition to the TCP socket interactions, our application logs requests to a SQLite
database. Why? Because we can! The purpose of this application is to demonstrate
nontrivial activities in the Android/Linux environment, including the use of the
SQLite system library. Let’s get started by examining the DayTime Server application.

13.3.2 daytime.c
The DayTime Server application can be broken into two basic functional parts. The
first is the TCP socket server.
Our DayTime Server application binds to TCP port 1024 when looking for new
connections. Ordinarily, a daytime service binds to TCP port 13, but Linux has a security feature where only trusted users can bind to any port below 1023. The second feature is the insertion of data into a SQLite database. The following listing shows the
code for the DayTime Server application.
Listing 13.8 daytime.c
#include
#include
Import
#include
required
#include
headers
#include
#include
#include
#include
Listening
#include "sqlite3.h"
port number
int PORTNUMBER = 1024;
#define htons(a)
( ((a & 0x00ff) << 8) | ((a & 0xff00) >> 8))
Define
void RecordHit(char * when)
helpful macro
{
int rc;
sqlite3
*db;
char *zErrMsg = 0;
char sql[200];
rc = sqlite3_open("daytime_db.db",&db);
if( rc )
{
printf( "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
Interact
return;
with SQLite
}
bzero(sql,sizeof(sql));
sprintf(sql,"insert into hits values (DATETIME('NOW'),'%s');",when);
rc = sqlite3_exec(db, sql, NULL, 0, &zErrMsg);
if( rc!=SQLITE_OK )
{
printf( "SQL error: %s\n", zErrMsg);
}
sqlite3_close(db);
}

B

C

D

E

372

CHAPTER 13

Building Android applications in C

int main(int argc, char **argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buf[100];
time_t ticks;
int done = 0;
int rc;
fd_set readset;
int result;
struct timeval tv;
printf("Daytime Server\n");
listenfd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
Set up and
servaddr.sin_addr.s_addr = INADDR_ANY;
listen on socket
servaddr.sin_port = htons(PORTNUMBER);
rc = bind(listenfd, (struct sockaddr *) &servaddr,sizeof(servaddr));
if (rc != 0)
{
printf("after bind,rc = [%d]\n",rc);
return rc;
}
listen(listenfd,5);
while (!done)
{
printf("Waiting for connection\n");
while (1)
{
bzero(&tv,sizeof(tv));
tv.tv_sec = 2;
FD_ZERO(&readset);
FD_SET(listenfd, &readset);
result = select(listenfd + 1, &readset, &readset, NULL, &tv);
if (result >= 1)
{
printf("Incoming connection!\n");
break;
}
else if (result == 0)
{
printf("Timeout.\n");
continue;
}
else
{
printf("Error, leave.\n");
return result;
}
}
printf("Calling accept:\n");
Accept socket
connfd = accept(listenfd,
connection
(struct sockaddr *) NULL, NULL);
printf("Connecting\n");
ticks = time(NULL);

G

F

373

What time is it? The DayTime Server
sprintf(buf,"%.24s",ctime(&ticks));
printf("sending [%s]\n",buf);
write(connfd,buf,strlen(buf));
close(connfd);
RecordHit(buf);

H

Record
activity

}
return 0;
}

As with many C language applications, a number of headers B are required, including definitions and prototypes for time functions, SQLite functions, and TCP sockets.
Note that the sqlite3.h header file isn’t provided in the CodeSourcery tool chain. This
file was acquired from a sqlite3 distribution, and the file was copied into the local
directory along with daytime.c. This is why the include file is delimited with quotation
marks rather than <>, which is used for finding include files in the system or compiler
path. The htons function is typically implemented in the library named socket (libsocket.so). Android doesn’t provide this library, nor was this found in any of the system libraries. Therefore htons is defined here as a macro D. This macro is required
to get the network byte ordering correct. When the application is running, we can verify this port by running netstat –tcp on the command line in the adb shell.
The standard TCP port for a DayTime Server is port 13. In C, the application is
using port 1024 because our application can’t bind to any port numbered 1023 or
below. Only system processes may bind to ports below 1024.
In the RecordHit() function, you see SQLite interaction E. The RecordHit()
function is responsible for inserting a record into the SQLite database created for this
application.
Jumping into the main function, you see the socket functions in use to listen on a
socket for incoming connections F. When a connection is accepted G, the current
system time is sent to the calling client. After this, the application makes a record of
the transaction by calling the RecordHit() function H.
That’s all the code necessary to implement our Android/Linux DayTime Server
application. Let’s look next at the SQLite 3 database interaction in more detail.

13.3.3 The SQLite database
This application employs a simple database structure created with the SQLite 3 application. We interact with SQLite 3 from the adb shell environment, as shown in figure 13.7.
The purpose of this database is to record data each time the DayTime Server processes an incoming request. From a data perspective, this sample is boring, as it simply
records the system time along with the text returned to the client (this text is a ctimeformatted time string). Though somewhat redundant from a data perspective, the
purpose is to demonstrate the use of SQLite from our C application, utilizing the
Android/Linux resident sqlite3 library, libsqlite.so.

374

CHAPTER 13

Figure 13.7

Building Android applications in C

Interact with SQLite 3 from the command line in the adb shell.

The previous section of code outlined the syntax for inserting a row into the database;
this section shows how to interact with the database using the SQLite 3 tool. The
sequence shown in figure 13.7 is broken out and explained in the following listing.
Listing 13.9

Interacting with a SQLite database

# pwd
pwd
Connect to
/data/ch13
database file
# sqlite3 daytime_db.db
sqlite3 daytime_db.db
SQLite version 3.5.0
Enter ".help" for instructions
sqlite> .databases
.databases
Examine
seq name
file
database structure
--- --------------- ------------------------------------------------0
main
/data/ch13/daytime_db.db
sqlite> .tables
.tables
hits
sqlite> .schema hits
.schema hits
CREATE TABLE hits (hittime date,hittext text);
Create statement
sqlite> .header on
.header on

B

D

C

What time is it? The DayTime Server

375

sqlite> .mode column
.mode column
sqlite> select * from hits;
Select rows
select * from hits;
hittime
hittext
------------------- -----------------------2008-07-29 07:31:35 Tue Jul 29 07:31:35 2008
2008-07-29 07:56:27 Tue Jul 29 07:56:27 2008
2008-07-29 07:56:28 Tue Jul 29 07:56:28 2008
2008-07-29 07:56:29 Tue Jul 29 07:56:28 2008
2008-07-29 07:56:30 Tue Jul 29 07:56:30 2008
sqlite> .exit
.exit
#

E

The SQLite database operates in a similar fashion to other, modern SQL-based environments. In listing 13.9, you see the output from an interactive session where the
database for this chapter’s sample application is opened B. A series of commands
given at the sqlite> prompt C display the contents of the database in terms of structure. The schema command dumps the DDL (Data Definition Language) for a particular table. In this case, you see the CREATE TABLE instructions for the hits table D.
Viewing the data is simple with the use of the familiar select statement E.
To run the sample code yourself, you’ll want to execute the following command
sequence from an adb shell:
cd /data/ch13
sqlite3 daytime_db.db
create table hits (hittime date,hittext text);
.exit

The SQLite database engine is known for its simplicity. This section displayed a simple
interaction and just how easy it is to employ. In addition, the SQLite 3 database may be
pulled from the Android Emulator and used on the development machine, as shown
in figure 13.8.

Figure 13.8

The SQLite database on the development machine

376

CHAPTER 13

Building Android applications in C

This feature makes Android a compelling platform for mobile data collection
applications because syncing data can be as simple as copying a database file that’s
compatible across multiple platforms.

13.3.4 Building and running the DayTime Server
To build this application, we need to combine the components of the previous few
sections. We know that the application requires a startup component and must also
link against multiple libraries. Because the application interacts with the SQLite database, we must link against the sqlite library in addition to the c and android_runtime
libraries. The full build script is shown in the next listing.
Listing 13.10 Daytime application build script
arm-none-linux-gnueabi-gcc -c daytime.c
arm-none-linux-gnueabi-gcc -c -o crt0.o crt.S
arm-none-linux-gnueabi-ld --entry=_start --dynamic-linker /system/bin/linker
-nostdlib -rpath /system/lib -rpath-link \android\system\lib -L
\android\system\lib -l c -l android_runtime -l sqlite -o daytime
daytime.o crt0.o
C:\software\google\\tools\adb
push daytime /data/ch13
g:\tools\adb shell "chmod 777 /data/ch13/daytime"

The build script begins by compiling the main source file, daytime.c. The next line
compiles the crt.S file, which we introduced in listing 13.7 for our C runtime initialization. The linker command contains a number of switches to create the desired application. Note the parameter to the linker to include the sqlite library. Note also the
inclusion of both daytime.o and crt0.o object files as inputs to the linker. Both are
required to properly construct the DayTime Server application. The input files are
found in local (to the development machine) copies of the libraries. And adb is
employed to push the executable file to the Android Emulator and to modify the permissions, saving a manual step.
Running the DayTime Server application is the easy and fun part of this exercise.
Here’s a rundown of the sequence shown in figure 13.9:
1
2

3
4

5

6

Start the shell by running adb shell.
Change directories to /data/ch13, where the application resides, previously
pushed there with an adb push command.
Run the ./daytime application.
The application binds to a port and begins listening for an incoming
connection.
A timeout occurs prior to a connection being made. The application displays
the timeout and returns to look for connections again.
A connection is detected and subsequently accepted.

What time is it? The DayTime Server

Figure 13.9
7
8
9

10
11

377

DayTime Server running in the shell

The time string is constructed and sent to the client.
A record is inserted into the database with the shown sql statement.
You kill the application and restart the shell. Note that this is because you didn’t
build a clean way of killing the DayTime Server. A proper version of the application would be to convert it to a daemon, which is beyond the scope of our discussion here.
Run sqlite3 to examine the contents of the application’s database.
Perform a select against the hits table, where you see the recently inserted
record.

You’ve built an Android/Linux application that implements a variant of the traditional
DayTime Server application as well as interacts with a SQL database. Not too shabby
when you consider that this is a telephone platform! Let’s move on to examine the
Android/Java application used to exercise the DayTime Server, our Daytime Client.

378

CHAPTER 13

Building Android applications in C

13.4 Daytime Client
One of the stated objectives for this chapter is to connect the Java UI to our DayTime
Server application. This section demonstrates the construction of a Daytime Client
application, which communicates with our DayTime Server via TCP sockets.

13.4.1 Activity
The Daytime Client application has a single
Activity, which presents a single Button and a
TextView, as shown in figure 13.10.
When a user clicks the Button, the Activity
initiates the DayTime Server query and replaces
the text of the TextView with the information
received from the DayTime Server. There’s not
much to it, but that’s fine, as all we’re after in this
sample is to demonstrate connectivity between
the two applications. The following listing shows
the onCreate() method for this Activity.

Figure 13.10

The Daytime Client app

Listing 13.11 UI elements of DaytimeClient.java
Handler h;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Declare,
implement
setContentView(R.layout.main);
Handler
final TextView statuslabel = (TextView)
findViewById(R.id.statuslabel);
h = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Log.d("CH13","data [" + (String) msg.obj + "]");
statuslabel.setText((String) msg.obj);
break;
}
super.handleMessage(msg);
}
};
Button test = (Button) findViewById(R.id.testit);
Implement
click listener
test.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
try {
Requester r = new Requester();
Create
r.start();
Requester instance
} catch (Exception e) {
Log.d("CH13 exception caught : ",e.getMessage());
}
}
});
}

B

C

D

379

Daytime Client

This application is all about detecting the selection of a button C and initiating an
action based on that click. The action is the creation of an instance of the Requester
class D, which we discuss in the next section. We handle the response from the socket
server with the assistance of a Handler B. The Handler has a single role: updating the
UI with textual data stored in the obj member of a Message object.
Although the UI of this application is simple, the more interesting side of this
Activity is the interaction with the DayTime Server, which takes place in the
Requester class, which we’ll look at next.

13.4.2 Socket client
The DayTime Server application listens on a TCP port for incoming connections. To
request the date and time, the Daytime Client must establish a client socket connection to the DayTime Server. It’s hard to imagine a simpler TCP service than this—open
a socket to the server, and read data until the socket connection is closed. There’s no
additional requirement. Most of the networking examples in this book have focused
on a higher-level protocol, HTTP, where the request and response are clearly defined
with headers and a specific protocol to observe. In this example, the communications
involve a lower-level socket connection, essentially raw, if you will, because there’s no
protocol associated with it beyond being a TCP stream (as opposed to UDP). The following listing demonstrates this lower-level socket communication.
Listing 13.12 Requester class implementation
public class Requester extends Thread {
Extend
Socket requestSocket;
Thread class
String message;
StringBuilder returnStringBuffer = new StringBuilder();
Message lmsg;
int ch;
Communicate
public void run() {
on Socket
try {
requestSocket = new Socket("localhost", 1024);
InputStreamReader isr = new
InputStreamReader(requestSocket.getInputStream(),
"ISO-8859-1");
while ((ch = isr.read()) != -1) {
returnStringBuffer.append((char) ch);
}
Create
message = returnStringBuffer.toString();
Message object
lmsg = new Message();
lmsg.obj = (Object) message;
Send Message
lmsg.what = 0;
to main thread
h.sendMessage(lmsg);
requestSocket.close();
} catch (Exception ee) {
Log.d("CH13","failed to read data" + ee.getMessage());
}
}
}

B

C

D

E

380

CHAPTER 13

Building Android applications in C

The Requestor B class extends the Thread class by implementing the run() method.
Communications take place via an instance of the Socket class C, which is found in
the java.net package. Note the port number being used—1024, just like our socket
server! A Message D is used to communicate back to the UI thread. Once the Message
object is initialized, it’s sent back to the calling thread E.
With the Daytime Client now coded, it’s time to test the application. In order for
the Daytime Client to access a TCP socket, a special permission entry is required in the
AndroidManifest.xml file: android:name="android.permission. INTERNET">.

13.4.3 Testing the Daytime Client
The first step in testing the Daytime Client is to ensure that the DayTime Server application is running, as described in section 13.3.4. Once you know the DayTime Server
is running, you can run the Daytime Client.
If you’re unclear on how to build and run the Daytime Client, refer to
chapter 2 for information on properly setting up the Android development
environment in Eclipse.
NOTE

Figure 13.11 demonstrates the Daytime Client running, alongside a view of the DayTime Server. Note how the TextView of the Android application is updated to reflect
the date and time sent by the DayTime Server.
The DayTime Server is exercising both TCP socket functionality and SQLite database record insertions, all running in the Android Emulator. A production-ready
Android/Linux application would need to be converted to run as a daemon, which is
beyond our aim for this chapter.

13.5 Summary
This chapter hopefully stretched your imagination for the kinds of applications possible with the versatile and open platform of Android. We had the goal of writing an
application outside the Android SDK and demonstrating how that kind of application
may be leveraged by a standard Android Java application. To write for the Android/
Linux layer, we turned to the C programming language.
Developing C language applications for Android/Linux is a cross-platform compilation exercise using the freely available CodeSourcery tool chain. This chapter demonstrated using that toolset in conjunction with the adb utility provided in the
Android SDK. The adb utility enabled you to push the application to the Android
Emulator for testing, as well as extract the Android system libraries essential for linking the application with the Android resident libraries. You used the adb shell to interact directly with the Android Emulator to run the C application.
Our sample application exercised TCP socket communications. The TCP capability
proved to be a ready interface mechanism between the Android/Java layer and the
Android/Linux foundation of the environment in the Daytime Client and server

Summary

Figure 13.11

381

Testing the Daytime Client

applications, respectively. TCP socket communications may also take place from the
Android/Linux environment to external, remote systems such as email servers or
directory servers, opening up a world of possibilities.
The DayTime Server sample application also demonstrated the use of an Android
resident library to manipulate a SQLite database used to store transaction data. The
impact of this step shouldn’t be minimized, as it satisfies three important development challenges. The first and most basic accomplishment of this functionality is that
we’ve demonstrated linking against, and employing, an Android resident system
library. This is significant because it shows how future applications may leverage
Android functionality such as Open GL or media services. Second, using a device-resident database that’s also accessible from the Java layer means you have an additional
(and persistent) interface mechanism between the Java and Linux environments on
the platform. Third, Android is a mobile platform. Anytime there’s a mobile application, the topic of sharing and syncing data bubbles up. We demonstrated in this chapter the ease with which an SQL-capable database was shared between the Android
Emulator and a personal computer—and all without complex synchronization programming. Synchronization is a broad topic, but the capability of moving a single file