《Android版Web服务器实现(一)HTTP协议请求头解析》一文中说到了HTTP协议请求头的解析,那么我们要如何得到这个HTTP请求头呢?我们需要监听端口。监听是一直要运行着的,在Android中比较好的方式就是使用服务。下面是实现的代码。
WebServer.java
package com.sparkle.webservice;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.os.IBinder;
import android.util.Log;
import com.sparkle.kits.IP;
public class WebServer extends Service implements Runnable {
private static boolean _isRunning = false;
private static Thread _serverThread = null;
private ServerSocket _listenSocket = null;
private MyLog _myLog = new MyLog(getClass().getName());
private static int _port = Defaults.getPort();
private TcpListener _tcpListener = null;
private static final int WAKE_INTERVAL_MS = 1000;
public WebServer() {
try {
Init();
} catch (IOException e) {
e.printStackTrace();
}
}
private void Init() throws IOException {
_listenSocket = new ServerSocket();
_listenSocket.setReuseAddress(true);
_listenSocket.bind(new InetSocketAddress(_port));
}
public static void Start(Context context) {
if (!_isRunning) {
_isRunning = true;
Intent intent = new Intent(context, WebServer.class);
context.startService(intent);
}
}
public static void Stop(Context context) {
if (_isRunning) {
_isRunning = false;
Intent intent = new Intent(context, WebServer.class);
context.stopService(intent);
}
}
public static boolean isRunning() {
return _isRunning;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int attempts = 10;
// The previous server thread may still be cleaning up,
// wait for it to finish.
while (_serverThread != null) {
_myLog.l(Log.WARN, "Won't start, server thread exists");
if (attempts <= 0) {
_myLog.l(Log.ERROR, "Server thread already exists");
return super.onStartCommand(intent, flags, startId);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
attempts--;
}
_myLog.l(Log.DEBUG, "Creating server thread");
_serverThread = new Thread(this);
_serverThread.start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
if (_tcpListener != null) {
_tcpListener.quit();
}
_myLog.l(Log.INFO, "onDestroy() Stopping server");
if (_serverThread == null) {
_myLog.l(Log.WARN, "Stopping with null serverThread");
return;
}
_serverThread.interrupt();
try {
_serverThread.join(10000); // wait 10 second for server thread to
// finish
} catch (InterruptedException e) {
}
if (_serverThread.isAlive()) {
_myLog.l(Log.WARN, "Server thread failed to exit");
} else {
_myLog.d("serverThread joined ok");
_serverThread = null;
}
try {
if (_listenSocket != null) {
_listenSocket.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
UiUpdater.updateClients();
_myLog.d("WebService.onDestroy() finished");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void run() {// Server thread run.
while (_isRunning) {
UiUpdater.updateClients();
if (_tcpListener == null) {
_tcpListener = new TcpListener(_listenSocket, this);
_tcpListener.start();
}
try {
Thread.sleep(WAKE_INTERVAL_MS);
} catch (InterruptedException e) {
_myLog.l(Log.DEBUG, "Thread interrupted");
}
}
}
public static InetAddress getWifiIp(Context context) {
if (context == null) {
throw new NullPointerException("Global context is null");
}
WifiManager wifiManager = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
if (!wifiManager.isWifiEnabled()) {
return null;
}
int ipAsInt = wifiManager.getConnectionInfo().getIpAddress();
if (ipAsInt == 0) {
return null;
} else {
return IP.intToInet(ipAsInt);
}
}
public static int getPort() {
return _port;
}
}
注:
1、WebServer继承自Service,内部套了一个服务的线程,所以又实现了Runnable接口。
2、重载onStartCommand方法,在该方法中启动服务线程_serverThread。在启动时,进行探测,以确保前一次启动的_serverThread已经关闭。
3、重载onDestroy方法,在该方法中关闭服务线程。
4、在run方法中,启用监听_tcpListener。TcpListener是一个封装的类,具体参看后面的代码。
5、附上getWifiIp和getPort方法,以方便调用。
6、UiUpdater是一个界面更新器,具体的请参看后文的代码。
7、服务需要在AndroidManifest.xml中注册,注册部分代码如下。
AndroidManifest.xml部分代码
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.sparkle.webservice.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.sparkle.webservice.WebServer" />
</application>
TcpListener.java
package com.sparkle.webservice;
import java.net.ServerSocket;
import java.net.Socket;
import android.util.Log;
public class TcpListener extends Thread {
private ServerSocket _listenSocket = null;
private MyLog _myLog = new MyLog(getClass().getName());
public TcpListener(ServerSocket listenSocket, WebServer webServer) {
this._listenSocket = listenSocket;
}
public void quit() {
try {
_listenSocket.close(); // if the TcpListener thread is blocked on
// accept,
// closing the socket will raise an
// exception
} catch (Exception e) {
_myLog.l(Log.DEBUG, "Exception closing TcpListener listenSocket");
}
}
public void run() {
try {
while (true) {
Socket clientSocket = _listenSocket.accept();
_myLog.l(Log.INFO, "New connection, spawned thread");
SessionThread newSession = new SessionThread(clientSocket);
newSession.start();
}
} catch (Exception e) {
_myLog.l(Log.DEBUG, "Exception in TcpListener");
}
}
}
注:
1、在run中使用accept的阻塞方法来监听。
2、在收到请求后,放到SessionThread中去处理,该部分代码请参看后文。
3、MyLog是自定义的一个日志类。
MyLog.java
package com.sparkle.webservice;
import android.util.Log;
public class MyLog {
protected String tag;
public MyLog(String tag) {
this.tag = tag;
}
public void l(int level, String str, boolean sysOnly) {
synchronized (MyLog.class) {
str = str.trim();
Log.println(level, tag, str);
}
}
public void l(int level, String str) {
l(level, str, false);
}
public void e(String s) {
l(Log.ERROR, s, false);
}
public void w(String s) {
l(Log.WARN, s, false);
}
public void i(String s) {
l(Log.INFO, s, false);
}
public void d(String s) {
l(Log.DEBUG, s, false);
}
}
注:日志输出时使用synchronized来确保日志的输出。
监听到了HTTP的请求后,需要对其进行处理以作出响应,具体请看下一篇。
转载请注明出处:Android版Web服务器实现(二)使用服务来监听HTTP请求