HTTP服务器(1)

单文件服务器

导语

在研究HTTP服务器时,我们可以从一个单文件服务器开始。无论接受到什么请求,这个服务器始终发送同一个文件。下面是示例代码,绑定的端口,发送的文件名以及文件的编码从命令行读取。如果省略端口则假定为端口80,如果省略编码方式默认8080

示例代码

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Logger;

public class SingleHttpServer {
	private static final Logger logger = Logger.getLogger("SingleFileHttpServer");
	private static  Handler handler;
	private final byte[] content;
	private final byte[] header;
	private final int port;
	private final String encoding;
	
	public SingleHttpServer(String _content, String _encoding, String mimeType, int _port) {
		this(_content.getBytes(), _encoding, mimeType, _port);
	}
	
	public SingleHttpServer(byte[] _content, String _encoding, String mimeType, int _port) {
		try {
			handler = new FileHandler("SingleFileHttpServer.log");
		} catch (Exception e) {
			throw new RuntimeException("cannot create log file!");
		}
		logger.addHandler(handler);
		content = _content;
		encoding = _encoding;
		String header1 = "HTTP/1.1 200 OK\r\n"
				+ "Server: OneFile\r\n"
				+ "Content-length: "+ content.length +"\r\n"
				+ "Content-Type: " + mimeType + ";charset=" + encoding + "\r\n\r\n";
		header = header1.getBytes(Charset.forName("utf-8"));
		port = _port;
	}
	
	public void start() {
		ExecutorService pool = Executors.newFixedThreadPool(100);
		try (ServerSocket server = new ServerSocket(port)) {
			logger.info("Accepting connections on port " + server.getLocalPort());
			logger.info("Data to be sent:");
			logger.info(new String(content, encoding));
			while (true) {
				Socket socket = server.accept();
				pool.execute(new HttpHandler(socket));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private class HttpHandler implements Runnable {

		private final Socket socket;
		
		public HttpHandler(Socket _socket) {
			socket = _socket;
		}

		@Override
		public void run() {
			try (
				OutputStream out = new BufferedOutputStream(socket.getOutputStream());
				InputStream in = new BufferedInputStream(socket.getInputStream());
			) {
				StringBuilder request = new StringBuilder(80);
				while (true) {
					int c = in.read();
					if (c == '\r' || c == '\n' || c == -1)break;
					request.append((char)c);
				}
				if (request.toString().indexOf("HTTP/") != -1) {
					out.write(header);
				}
				out.write(content);
				out.flush();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) {
		
		//设置端口
		int port;
		try{
			port = Integer.parseInt(args[2]);
			if (port < 1 || port > 65535)
				port = 8080;
		} catch (RuntimeException e) {
			//如果参数中没有端口,则会发生数组越界,所以需要捕获
			port = 8080;
		}
		//设置编码方式
		String encoding = "UTF-8";
		if (args.length >= 2) encoding = args[1];
		
		try {
			Path path = Paths.get(args[0]).toAbsolutePath();
			byte[] data = Files.readAllBytes(path);
			String contentType = URLConnection.getFileNameMap().getContentTypeFor(path.toString());
			SingleHttpServer server = new SingleHttpServer(data, encoding, contentType, port);
			server.start();
		} catch (ArrayIndexOutOfBoundsException ex) {
			System.out.println("Usage: java SingleHttpServer filename [encoding [port]]");
		} catch (IOException e) {
			logger.severe(e.getMessage());
		}
	}
}

main()方法中是从命令行读取参数。从第一个命令行读取提供的文件名。如果没有指定文件或者如果文件无法打开,就显示一条错误信息,程序推出。假定文件能够读取,就是用java7引入的Path和Files类将其内容读入byte数组。URLConnection类对文件的内容类型做出合理的猜测,将猜测的结果保存到contentTyep变量中。接下来读取第二个参数,如果没有则指定编码方式为UTF-8。

posted @ 2016-12-23 22:59  被罚站的树  阅读(174)  评论(0编辑  收藏  举报