HTTP是个大协议,完整功能的HTTP服务器必须响应资源请求,将URL转换为本地系统的资源名。响应各种形式的HTTP请求(GET、POST等)。处理不存在的文件请求,返回各种形式的状态码,解析MIME类型等。但许多特定功能的HTTP服务器并不需要所有这些功能。例如,很多网站只是想显示“建设中“的消息。很显然,Apache对于这样的网站是大材小用了。这样的网站完全可以使用只做一件事情的定制服务器。Java网络类库使得编写这样的单任务服务器轻而易举。
定制服务器不只是用于小网站。大流量的网站如Yahoo,也使用定制服务器,因为与一般用途的服务器相比,只做一件事情的服务器通常要快得多。针对某项任务来优化特殊用途的服务器很容易;其结果往往比需要响应很多种请求的一般用途服务器高效得多。例如,对于重复用于多页面或大流量页面中的图标和图片,用一个单独的服务器处理会更好(并且还可以避免在请求时携带不必要的Cookie,因而可以减少请求/响应数据,从而减少下载带宽,提升速度);这个服务器在启动时把所有图片文件读入内存,从RAM中直接提供这些文件,而不是每次请求都从磁盘上读取。此外,如果你不想在包含这些图片的页面请求之外单独记录这些图片,这个单独服务器则会避免在日志记录上浪费时间。
本篇为大家简要演示三种HTTP服务器:
(1) 简单的单文件服务器
(2) 重定向服务器
(3) 完整功能的HTTP服务器
简单的单文件服务器
该服务器的功能:无论接受到何种请求,都始终发送同一个文件。这个服务器命名为SingleFileHTTPServer,文件名、本地端口和内容编码方式从命令行读取。如果缺省端口,则假定端口号为80。如果缺省编码方式,则假定为ASCII。import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class SingleFileHTTPServer extends Thread { private byte[] content; private byte[] header; private int port=80; private SingleFileHTTPServer(String data, String encoding, String MIMEType, int port) throws UnsupportedEncodingException { this(data.getBytes(encoding), encoding, MIMEType, port); } public SingleFileHTTPServer(byte[] data, String encoding, String MIMEType, int port)throws UnsupportedEncodingException { this.content=data; this.port=port; String header="HTTP/1.0 200 OK\r\n"+ "Server: OneFile 1.0\r\n"+ "Content-length: "+this.content.length+"\r\n"+ "Content-type: "+MIMEType+"\r\n\r\n"; this.header=header.getBytes("ASCII"); } public void run() { try { ServerSocket server=new ServerSocket(this.port); System.out.println("Accepting connections on port "+server.getLocalPort()); System.out.println("Data to be sent:"); System.out.write(this.content); while (true) { Socket connection=null; try { connection=server.accept(); OutputStream out=new BufferedOutputStream(connection.getOutputStream()); InputStream in=new BufferedInputStream(connection.getInputStream()); StringBuffer request=new StringBuffer(); while (true) { int c=in.read(); if (c=='\r'||c=='\n'||c==-1) { break; } request.append((char)c); } //如果检测到是HTTP/1.0及以后的协议,按照规范,需要发送一个MIME首部 if (request.toString().indexOf("HTTP/")!=-1) { out.write(this.header); } out.write(this.content); out.flush(); } catch (IOException e) { // TODO: handle exception }finally{ if (connection!=null) { connection.close(); } } } } catch (IOException e) { System.err.println("Could not start server. Port Occupied"); } } public static void main(String[] args) { try { String contentType="text/plain"; if (args[0].endsWith(".html")||args[0].endsWith(".htm")) { contentType="text/html"; } InputStream in=new FileInputStream(args[0]); ByteArrayOutputStream out=new ByteArrayOutputStream(); int b; while ((b=in.read())!=-1) { out.write(b); } byte[] data=out.toByteArray(); //设置监听端口 int port; try { port=Integer.parseInt(args[1]); if (port<1||port>65535) { port=80; } } catch (Exception e) { port=80; } String encoding="ASCII"; if (args.length>2) { encoding=args[2]; } Thread t=new SingleFileHTTPServer(data, encoding, contentType, port); t.start(); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Usage:java SingleFileHTTPServer filename port encoding"); }catch (Exception e) { System.err.println(e);// TODO: handle exception } } }
SingleFileHTTPServer类本身是Thread的子类。它的run()方法处理入站连接。此服务器可能只是提供小文件,而且只支持低吞吐量的web网站。由于服务器对每个连接所需完成的所有工作就是检查客户端是否支持HTTP/1.0,并为连接生成一两个较小的字节数组,因此这可能已经足够了。另一方面,如果你发现客户端被拒绝,则可以使用多线程。许多事情取决于所提供文件的大小,每分钟所期望连接的峰值数和主机上Java的线程模型。对弈这个程序复杂写的服务器,使用多线程将会有明显的收益。