单文件服务器
导语
在研究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
"
+ "Server: OneFile
"
+ "Content-length: "+ content.length +"
"
+ "Content-Type: " + mimeType + ";charset=" + encoding + "
";
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 == '
' || c == '
' || 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。