示例由三个类组成:
程序的入口方法是HttpServer类中的main函数,创建一个自身的实例并调用await的方法。await方法主要功能就是在一个指定的端口上等待HTTP请求,直到接收到shutdown命令。
程序的主要功能是响应HTTP请求,并发送静态资源Html和图片文件。
程序中HttpServer用来接收HTTP请求,使用Requset类处理HTTP请求,找到HTTP请求的资源url(如,静态的Html文件)。通过ServerSocket类的输出对象(output)创建Response类,读取Requset对象分析出的请求资源,通过output对象将请求资源发送给发出HTTP请求的客户端。
HttpServer类
HttpServer代表一个Web服务器
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File;
public class HttpServer {
/** * WEB_ROOT is the directory where our HTML and other files reside. For this * package, WEB_ROOT is the "webroot" directory under the working directory. * The working directory is the location in the file system from where the * java command was invoked. */
public static final String WEB_ROOT = System.getProperty("user.dir")
+ File.separator + "webroot";
// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
// the shutdown command received
private boolean shutdown = false;
public static void main(String[] args) {
HttpServer server = new HttpServer();
server.await();
}
public void await() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1,
InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// Loop waiting for a request
while (!shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
// create Request object and parse
Request request = new Request(input);
request.parse();
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
// Close the socket
socket.close();
// check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
Web服务器能提供公共静态final变量WEB_ROOT所在的目录和它下面所有的子目录下的静态资源。
为了请求一个静态资源,在你的浏览器的地址栏或者网址框里边敲入以下的URL:
http://machineName:port/staticResource
如果你要从一个不同的机器上发送请求到你的应用程序正在运行的机器上,machineName应该是正在运行应用程序的机器的名称或者IP地址。假如你的浏览器在同一台机器上,你可以使用localhost作为machineName。端口是8080,staticResource是你需要请求的文件的名称,且必须位于WEB_ROOT里边。
HttpServer类首先创建一个ServerSocket实例,然后进入一个while循环,直到accept()方法时停下来,它会在接收到一个来8080端口的HTTP请求时返回。
接收到请求时它会返回一个Socket实例,取得java.io.InputStream和java.io.OutputStream对象,分别代表请求和服务器响应请求后返回的资源。HttpServer类就是分析请求并返回请求中的资源,返回请求资源后,关闭这个Socket,调用socket.close()方法。
Request和Response类主要处理的对象就是Socket实例的java.io.InputStream和java.io.OutputStream对象,即分析请求并处理请求的资源。这只是一个简单的示例,在Tomcat中Request和Response类还有很多其它的功能,在这个示例中Request对象只读取了HTTP请求中请求资源的URL,并没分析处理HTTP请求其它的信息。
Request类
Request类代表一个HTTP请求。从负责与客户端通信的Socket中传递过来InputStream对象来构造这个类的一个实例。调用InputStream对象其中一个read方法来获取HTTP请求的原始数据。
import java.io.InputStream;
import java.io.IOException;
public class Request {
private InputStream input;
private String uri;
public Request(InputStream input) {
this.input = input;
}
public void parse() {
// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try {
i = input.read(buffer);
} catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j = 0; j < i; j++) {
request.append((char) buffer[j]);
}
System.out.print(request.toString());
uri = parseUri(request.toString());
}
private String parseUri(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
}
public String getUri() {
return uri;
}
}
parse方法解析HTTP请求里边的原始数据。这个方法没有做很多事情。它唯一可用的信息是通过调用HTTP请求的私有方法parseUri获得的URI。parseUri方法在uri变量里边存储URI。公共方法getUri被调用并返回HTTP请求的URI。
Response类
.Response类代表一个HTTP响应
package ex01.pyrmont;
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;
/* HTTP Response = Status-Line *(( general-header | response-header | entity-header ) CRLF) CRLF [ message-body ] Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
public class Response {
private static final int BUFFER_SIZE = 1024;
Request request;
OutputStream output;
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
}
public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try {
File file = new File(HttpServer.WEB_ROOT, request.getUri());
if (file.exists()) {
fis = new FileInputStream(file);
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch != -1) {
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
} else {
// file not found
String errorMessage = "HTTP/1.1 404 File Not Found\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 23\r\n" + "\r\n"
+ "<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
} catch (Exception e) {
// thrown if cannot instantiate a File object
System.out.println(e.toString());
} finally {
if (fis != null)
fis.close();
}
}
}
它的构造方法接收一个java.io.OutputStream类,这个参数是从建立连接的Socket类中获取的,将所请求的资源以byte形式传递到这个输出流对象中就可以把资源传输到发出HTTP请求的客户端。Response类有两个公共方法:setRequest和sendStaticResource。setRequest方法用来传递一个Request对象给Response对象。 sendStaticResource方法是用来发送一个静态资源,例如一个HTML文件。它首先通过传递上一级目录的路径和子路径给File累的构造方法来实例化java.io.File类。
需要注意的是,在这种情况下,静态资源是以原始数据发送给浏览器的。
运行应用程序
index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to BrainySoftware</title>
</head>
<body>
<img src="./images/logo.gif">
<br> Welcome to BrainySoftware.
</body>
</html>
图片路径需要另行设置,还需要注意的是360浏览器在接收到响应时会以文本形式显示,用Google的Chrome可以正常显示,具体是什么原因造成的还不清楚。