在Android设备上实现一个基于服务的Web服务器,可以监听HTTP请求并作出响应,本文将详细介绍如何通过服务来监听HTTP请求,包括代码示例、关键步骤和常见问题的解答。
一、实现
要在Android设备上实现一个Web服务器,首先需要创建一个服务(Service),该服务将负责监听指定的端口上的HTTP请求,当接收到请求时,通过新建线程来处理这些请求,并根据不同的请求类型作出相应的响应。
二、关键步骤与代码实现
1. 创建服务类
继承Service
类,并实现Runnable
接口,以便在服务中启动一个新线程来监听端口。
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); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { int attempts = 10; 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) { 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) { e.printStackTrace(); } } @Override public IBinder onBind(Intent intent) { return null; } @Override public void run() { while (isRunning) { try { Socket clientSocket = listenSocket.accept(); new SessionThread(clientSocket).start(); } catch (IOException e) { myLog.e(e.getMessage()); } } } }
2. 注册服务
在AndroidManifest.xml
中注册服务。
<service android:name=".WebServer" />
3. 创建SessionThread类处理请求
public class SessionThread extends Thread { private Socket clientSocket = null; private final int BUFFER_MAX = 8192; private DataHandle dataHandle = null; private MyLog myLog = new MyLog(getClass().getName()); public SessionThread(Socket clientSocket) { this.clientSocket = clientSocket; } public void closeSocket() { if (clientSocket == null) { return; } try { clientSocket.close(); } catch (IOException e) { myLog.e(e.getMessage()); } } public void run() { try { InputStream socketInput = clientSocket.getInputStream(); byte[] buffer = new byte[BUFFER_MAX]; socketInput.read(buffer); dataHandle = new DataHandle(buffer); byte[] content = dataHandle.fetchContent(); sendResponse(clientSocket, content); } catch (Exception e) { myLog.l(Log.DEBUG, "Exception in TcpListener"); } } private void sendResponse(Socket clientSocket, byte[] content) { try { OutputStream socketOut = clientSocket.getOutputStream(); byte[] header = dataHandle.fetchHeader(content.length); socketOut.write(header); socketOut.write(content); socketOut.close(); clientSocket.close(); } catch (Exception e) { } } }
4. 处理HTTP请求头和响应逻辑
public class DataHandle { private String receiveInfo = ""; private HttpHeader httpHeader = null; private String encoding = "utf-8"; private String serverName = "Simple Web Server"; private String responseCode = "200 OK"; private String contentType = "text/html"; public DataHandle(byte[] recieveData) { try { this.receiveInfo = new String(recieveData, encoding); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } httpHeader = new HttpHeader(receiveInfo); } public byte[] fetchContent() { byte[] backData = null; if (!isSupportMethod()) { backData = fetchNotSupportMethodBack(); return backData; } String filePath = fetchFilePath(); boolean hasFile = FileSp.isExist(filePath); if (!hasFile) { backData = fetchNotFoundBack(); return backData; } if (!isSupportExtension()) { backData = fetchNotSupportFileBack(); return backData; } backData = fetchContent(filePath); return backData; } }
三、相关问题与解答栏目
问题1:如何在Android设备上检查端口是否被占用?
答:可以使用nmap
命令来扫描本地端口,要扫描本地的80端口,可以使用以下命令:nmap -p 80 127.0.0.1
,如果要扫描远程设备的端口,可以将IP地址替换为目标设备的IP地址,也可以使用Termux应用,它提供了一个Linux环境,可以在其中安装和使用大量的Linux工具,包括nmap。
问题2:如何处理不同类型的HTTP请求方法?
答:在DataHandle
类中,可以根据请求的方法来决定返回什么样的响应,如果服务端仅支持GET和POST方法,而收到DELETE等不支持的方法,则可以返回501状态码表示不支持的方法,具体的实现可以参考fetchContent
方法中的isSupportMethod
函数。
小伙伴们,上文介绍了“android版web服务器实现 使用服务来监听http请求”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。
原创文章,作者:K-seo,如若转载,请注明出处:https://www.kdun.cn/ask/637529.html