Android版Web服务器实现(二)使用服务来监听HTTP请求

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请求

源码下载

posted @ 2014-03-11 10:35  _学而时习之  阅读(557)  评论(0编辑  收藏  举报