Android端搭建web服务器

本文介绍了如何在Android设备上搭建Web服务器,通过设定端口和路径,将本地文件作为Web项目供URL访问。主要步骤包括实现NanoHttpd类、配置端口和路径,并提供了源码链接。
摘要由CSDN通过智能技术生成


在Android假设服务器,其最终的结果就是根据预先设定好的端口和Url访问到你预先放好的资源。

步骤简单的概括几部就是


1,实现NanoHttpd类

2,设置端口和路径

3,访问实验。


先把源码放上:http://download.csdn.net/detail/u012455330/9132489


package webserve;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.PushbackInputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManagerFactory;

import webserve.NanoHTTPD.Response.IStatus;
import webserve.NanoHTTPD.Response.Status;

/**
 * A simple, tiny, nicely embeddable HTTP server in Java
 * <p/>
 * <p/>
 * NanoHTTPD
 * <p>
 * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen,
 * 2010 by Konstantinos Togias
 * </p>
 * <p/>
 * <p/>
 * <b>Features + limitations: </b>
 * <ul>
 * <p/>
 * <li>Only one Java file</li>
 * <li>Java 5 compatible</li>
 * <li>Released as open source, Modified BSD licence</li>
 * <li>No fixed config files, logging, authorization etc. (Implement yourself if
 * you need them.)</li>
 * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT
 * support in 1.25)</li>
 * <li>Supports both dynamic content and file serving</li>
 * <li>Supports file upload (since version 1.2, 2010)</li>
 * <li>Supports partial content (streaming)</li>
 * <li>Supports ETags</li>
 * <li>Never caches anything</li>
 * <li>Doesn't limit bandwidth, request time or simultaneous connections</li>
 * <li>Default code serves files and shows all HTTP parameters and headers</li>
 * <li>File server supports directory listing, index.html and index.htm</li>
 * <li>File server supports partial content (streaming)</li>
 * <li>File server supports ETags</li>
 * <li>File server does the 301 redirection trick for directories without '/'
 * </li>
 * <li>File server supports simple skipping for files (continue download)</li>
 * <li>File server serves also very long files without memory overhead</li>
 * <li>Contains a built-in list of most common MIME types</li>
 * <li>All header names are converted to lower case so they don't vary between
 * browsers/clients</li>
 * <p/>
 * </ul>
 * <p/>
 * <p/>
 * <b>How to use: </b>
 * <ul>
 * <p/>
 * <li>Subclass and implement serve() and embed to your own program</li>
 * <p/>
 * </ul>
 * <p/>
 * See the separate "LICENSE.md" file for the distribution license (Modified BSD
 * licence)
 */
public abstract class NanoHTTPD {

	/**
	 * Pluggable strategy for asynchronously executing requests.
	 */
	public interface AsyncRunner {

		void closeAll();

		void closed(ClientHandler clientHandler);

		void exec(ClientHandler code);
	}

	/**
	 * The runnable that will be used for every new client connection.
	 */
	public class ClientHandler implements Runnable {

		private final InputStream inputStream;

		private final Socket acceptSocket;

		private ClientHandler(InputStream inputStream, Socket acceptSocket) {
			this.inputStream = inputStream;
			this.acceptSocket = acceptSocket;
		}

		public void close() {
			safeClose(this.inputStream);
			safeClose(this.acceptSocket);
		}

		@Override
		public void run() {
			OutputStream outputStream = null;
			try {
				outputStream = this.acceptSocket.getOutputStream();
				TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create();
				HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream,
						this.acceptSocket.getInetAddress());
				while (!this.acceptSocket.isClosed()) {
					session.execute();
				}
			} catch (Exception e) {
				// When the socket is closed by the client,
				// we throw our own SocketException
				// to break the "keep alive" loop above. If
				// the exception was anything other
				// than the expected SocketException OR a
				// SocketTimeoutException, print the
				// stacktrace
				if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))
						&& !(e instanceof SocketTimeoutException)) {
					NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e);
				}
			} finally {
				safeClose(outputStream);
				safeClose(this.inputStream);
				safeClose(this.acceptSocket);
				NanoHTTPD.this.asyncRunner.closed(this);
			}
		}
	}

	public static class Cookie {

		public static String getHTTPTime(int days) {
			Calendar calendar = Calendar.getInstance();
			SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
			dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
			calendar.add(Calendar.DAY_OF_MONTH, days);
			return dateFormat.format(calendar.getTime());
		}

		private final String n, v, e;

		public Cookie(String name, String value) {
			this(name, value, 30);
		}

		public Cookie(String name, String value, int numDays) {
			this.n = name;
			this.v = value;
			this.e = getHTTPTime(numDays);
		}

		public Cookie(String name, String value, String expires) {
			this.n = name;
			this.v = value;
			this.e = expires;
		}

		public String getHTTPHeader() {
			String fmt = "%s=%s; expires=%s";
			return String.format(fmt, this.n, this.v, this.e);
		}
	}

	/**
	 * Provides rudimentary support for cookies. Doesn't support 'path',
	 * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported
	 * features.
	 * 
	 * @author LordFokas
	 */
	public class CookieHandler implements Iterable<String> {

		private final HashMap<String, String> cookies = new HashMap<String, String>();

		private final ArrayList<Cookie> queue = new ArrayList<Cookie>();

		public CookieHandler(Map<String, String> httpHeaders) {
			String raw = httpHeaders.get("cookie");
			if (raw != null) {
				String[] tokens = raw.split(";");
				for (String token : tokens) {
					String[] data = token.trim().split("=");
					if (data.length == 2) {
						this.cookies.put(data[0], data[1]);
					}
				}
			}
		}

		/**
		 * Set a cookie with an expiration date from a month ago, effectively
		 * deleting it on the client side.
		 * 
		 * @param name
		 *            The cookie name.
		 */
		public void delete(String name) {
			set(name, "-delete-", -30);
		}

		@Override
		public Iterator<String> iterator() {
			return this.cookies.keySet().iterator();
		}

		/**
		 * Read a cookie from the HTTP Headers.
		 * 
		 * @param name
		 *            The cookie's name.
		 * @return The cookie's value if it exists, null otherwise.
		 */
		public String read(String name) {
			return this.cookies.get(name);
		}

		public void set(Cookie cookie) {
			this.queue.add(cookie);
		}

		/**
		 * Sets a cookie.
		 * 
		 * @param name
		 *            The cookie's name.
		 * @param value
		 *            The cookie's value.
		 * @param expires
		 *            How many days until the cookie expires.
		 */
		public void set(String name, String value, int expires) {
			this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires)));
		}

		/**
		 * Internally used by the webserver to add all queued cookies into the
		 * Response's HTTP Headers.
		 * 
		 * @param response
		 *            The Response object to which headers the queued cookies
		 *            will be added.
		 */
		public void unloadQueue(Response response) {
			for (Cookie cookie : this.queue) {
				response.addHeader("Set-Cookie", cookie.getHTTPHeader());
			}
		}
	}

	/**
	 * Default threading strategy for NanoHTTPD.
	 * <p/>
	 * <p>
	 * By default, the server spawns a new Thread for every incoming request.
	 * These are set to <i>daemon</i> status, and named according to the request
	 * number. The name is useful when profiling the application.
	 * </p>
	 */
	public static class DefaultAsyncRunner implements AsyncRunner {

		private long requestCount;

		private final List<ClientHandler> running = Collections
				.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>());

		/**
		 * @return a list with currently running clients.
		 */
		public List<ClientHandler> getRunning() {
			return running;
		}

		@Override
		public void closeAll() {
			// copy of the list for concurrency
			for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) {
				clientHandler.close();
			}
		}

		@Override
		public void closed(ClientHandler clientHandler) {
			this.running.remove(clientHandler);
		}

		@Override
		public void exec(ClientHandler clientHandler) {
			++this.requestCount;
			Thread t = new Thread(clientHandler);
			t.setDaemon(true);
			t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")");
			this.running.add(clientHandler);
			t.start();
		}
	}

	/**
	 * Default strategy for creating and cleaning up temporary files.
	 * <p/>
	 * <p>
	 * By default, files are created by <code>File.createTempFile()</code> in
	 * the directory specified.
	 * </p>
	 */
	public static class DefaultTempFile implements TempFile {

		private final File file;

		private final OutputStream fstream;

		public DefaultTempFile(String tempdir) throws IOException {
			this.file = File.createTempFile("NanoHTTPD-", "", new File(tempdir));
			this.fstream = new FileOutputStream(this.file);
		}

		@Override
		public void delete() throws Exception {
			safeClose(this.fstream);
			if (!this.file.delete()) {
				throw new Exception("could not delete temporary file");
			}
		}

		@Override
		public String getName() {
			return this.file.getAbsolutePath();
		}

		@Override
		public OutputStream open() throws Exception {
			return this.fstream;
		}
	}

	/**
	 * Default strategy for creating and cleaning up temporary files.
	 * <p/>
	 * <p>
	 * This class stores its files in the standard location (that is, wherever
	 * <code>java.io.tmpdir</code> points to). Files are added to an internal
	 * list, and deleted when no longer needed (that is, when
	 * <code>clear()</code> is invoked at the end of processing a request).
	 * </p>
	 */
	public static class DefaultTempFileManager implements TempFileManager {

		private final String tmpdir;

		private final List<TempFile> tempFiles;

		public DefaultTempFileManager() {
			this.tmpdir = System.getProperty("java.io.tmpdir");
			this.tempFiles = new ArrayList<TempFile>();
		}

		@Override
		public void clear() {
			for (TempFile file : this.tempFiles) {
				try {
					file.delete();
				} catch (Exception ignored) {
					NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored);
				}
			}
			this.tempFiles.clear();
		}

		@Override
		public TempFile createTempFile(String filename_hint) throws Exception {
			DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir);
			this.tempFiles.add(tempFile);
			return tempFile;
		}
	}

	/**
	 * Default strategy for creating and cleaning up temporary files.
	 */
	private class DefaultTempFileManagerFactory implements TempFileManagerFactory {

		@Override
		public TempFileManager create() {
			return new DefaultTempFileManager();
		}
	}

	private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)";

	private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX,
			Pattern.CASE_INSENSITIVE);

	private static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)";

	private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE);

	private static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]";

	private static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern
			.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX);

	protected class HTTPSession implements IHTTPSession {

		private static final int REQUEST_BUFFER_LEN = 512;

		private static final int MEMORY_STORE_LIMIT = 1024;

		public static final int BUFSIZE = 8192;

		private final TempFileManager tempFileManager;

		private final OutputStream outputStream;

		private final PushbackInputStream inputStream;

		private int splitbyte;

		private int rlen;

		private String uri;

		private Method method;

		private Map<String, String> parms;

		private Map<String, String> headers;

		private CookieHandler cookies;

		private String queryParameterString;

		private String remoteIp;

		private String protocolVersion;

		public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
			this.tempFileManager = tempFileManager;
			this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE);
			this.outputStream = outputStream;
		}

		public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream,
				InetAddress inetAddress) {
			this.tempFileManager = tempFileManager;
			this.inputStream = new PushbackInputStream(inputStream, HTTPSession.BUFSIZE);
			this.outputStream = outputStream;
			this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1"
					: inetAddress.getHostAddress().toString();
			this.headers = new HashMap<String, String>();
		}

		/**
		 * Decodes the sent headers and loads the data into Key/value pairs
		 */
		private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms,
				Map<String, String> headers) throws ResponseException {
			try {
				// Read the request line
				String inLine = in.readLine();
				if (inLine == null) {
					return;
				}

				StringTokenizer st = new StringTokenizer(inLine);
				if (!st.hasMoreTokens()) {
					throw new ResponseException(Response.Status.BAD_REQUEST,
							"BAD REQUEST: Syntax error. Usage: GET /example/file.html");
				}

				pre.put("method", st.nextToken());

				if (!st.hasMoreTokens()) {
					throw new ResponseException(Response.Status.BAD_REQUEST,
							"BAD REQUEST: Missing URI. Usage: GET /example/file.html");
				}

				String uri = st.nextToken();

				// Decode parameters from the URI
				int qmi = uri.indexOf('?');
				if (qmi >= 0) {
					decodeParms(uri.substring(qmi + 1), parms);
					uri = decodePercent(uri.substring(0, qmi));
				} else {
					uri = decodePercent(uri);
				}

				// If there's another token, its protocol version,
				// followed by HTTP headers.
				// NOTE: this now forces header names lower case since they are
				// case insensitive and vary by client.
				if (st.hasMoreTokens()) {
					protocolVersion = st.nextToken();
				} else {
					protocolVersion = "HTTP/1.1";
					NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1.");
				}
				String line = in.readLine();
				while (line != null && line.trim().length() > 0) {
					int p = line.indexOf(':');
					if (p >= 0) {
						headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim());
					}
					line = in.readLine();
				}

				pre.put("uri", uri);
			} catch (IOException ioe) {
				throw new ResponseException(Response.Status.INTERNAL_ERROR,
						"SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
			}
		}

		/**
		 * Decodes the Multipart Body data and put it into Key/Value pairs.
		 */
		private void decodeMultipartFormData(String boundary, ByteBuffer fbuf, Map<String, String> parms,
				Map<String, String> files) throws ResponseException {
			try {
				int[] boundary_idxs = getBoundaryPositions(fbuf, boundary.getBytes());
				if (boundary_idxs.length < 2) {
					throw new ResponseException(Response.Status.BAD_REQUEST,
							"BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings.");
				}

				final int MAX_HEADER_SIZE = 1024;
				byte[] part_header_buff = new byte[MAX_HEADER_SIZE];
				for (int bi = 0; bi < boundary_idxs.length - 1; bi++) {
					fbuf.position(boundary_idxs[bi]);
					int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE;
					fbuf.get(part_header_buff, 0, len);
					ByteArrayInputStream bais = new ByteArrayInputStream(part_header_buff, 0, len);
					BufferedReader in = new BufferedReader(new InputStreamReader(bais, Charset.forName("US-ASCII")));

					// First line is boundary string
					String mpline = in.readLine();
					if (!mpline.contains(boundary)) {
						throw new ResponseException(Response.Status.BAD_REQUEST,
								"BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary.");
					}

					String part_name = null, file_name = null, content_type = null;
					// Parse the reset of the header lines
					mpline = in.readLine();
					while (mpline != null && mpline.trim().length() > 0) {
						Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(mpline);
						if (matcher.matches()) {
							String attributeString = matcher.group(2);
							matcher = CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString);
							while (matcher.find()) {
								String key = matcher.group(1);
								if (key.equalsIgnoreCase("name")) {
									part_name = matcher.group(2);
								} else if (key.equalsIgnoreCase("filename")) {
									file_name = matcher.group(2);
								}
							}
						}
						matcher = CONTENT_TYPE_PATTERN.matcher(mpline);
						if (matcher.matches()) {
							content_t
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值