ServerSocket 类
Socket 类表示的是客户端的socket。无论什么时候,只要你想连接到一个远程服务器的应用,你都要构建一个socket。如果你想执行一个服务器应用程序,比如HTTP服务或者FTP服务的程序,那么你需要使用不同的途径。因为你的服务器必须一直是开机闲置,所以它不知道什么时候客户机试图来连接它。
这个时候,需要使用java.net.ServerSocket 类。它会实现一个服务器socket。一个服务器socket会等待来自客户端的连接。一旦它接收到一个连接请求,它就会创建一个 Socket 实例来处理和客户端通讯的问题。
要创建一个服务器socket,可以使用四种ServerSocket类构造方法中的一种来实现。你需要制定服务器socket监听的IP地址和端口。 典型的,IP地址如果是127.0.0.1,意味着服务器socket将监听本地机器。这个被监听的IP地址被认为是一种绑定地址。server socket的另一个重要属性是它的 backlog属性,它是在server socket拒绝连接请求前,能够接受的连接请求的最大队列长度。
ServerSocket类的构造函数之一如下:
public ServerSocket(int port, int backLog, InetAddress bindingAddress);
对于这个构造函数而言,绑定地址必须是java.net.InetAddress 的一个实例。一个简单的办法是通过调用它的静态方法getByName来构造一个InetAddres对象。该方法来一个包含主机名的字符串参数:
InetAddress.getByName("127.0.0.1");
下面一行代码构造一个ServerSocket ,它监听本地机器的8080端口,backlog设置为1。
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
一旦有了一个 ServerSocket 实例,可以通过调用accept方法来告诉它等待进来的连接请求。这个方法只有在有一个连接请求时才返回。它返回的是Socket类的实例。这个Socket对象能够发送和接受来自客户端应用的字节流,就是第一节所讲到的socket类。实际上,accept 是本文提及的唯一一个在应用中使用的方法。
Application应用
我们的web服务器应用是ex01.pyrmont包的一部分,包含三个类:
HttpServer
Request
Response
这个应用的入口(静态main方法)是HttpServer类。它创建了一个HttpServer 实例来调用它的await方法。 就象这个方法名所暗示的,await 方法在一个指定的端口等待一个HTTP请求,并处理它们,然后发送回应给客户端。它保持等待状态,直到收到一个shutdown命令。 (命令名await来代替wait的原因是wait是System.Object类中的一个用于线程方面的重要方法)
应用仅仅只发送静态资源,比如来自特定目录的HTML和图片文件。不支持动态包头 (比如日期或者cookie) 。
在下面的段落中,让我们来看看这三个类吧。
HttpServer 类
HttpServer类表示一个web服务器,且在公共静态目录WEB_ROOT及它的子目录中能为找到的那些静态资源而服务。WEB_ROOT用以下方式初始化:
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot";
这段代码指明了一个包含静态资源的webroot目录,这些资源可用来测试该应用。在该目录中也能找到servlet容器。
要请求一个静态资源,在浏览器中输入如下地址或URL:
http://machineName:port/staticResource
machineName 是运行这个应用的计算机名或者IP地址。如果你的浏览器是在同一台机器上,可以使用localhost作为机器名。端口是8080。staticResource是请求的文件夹名,它必须位于WEB-ROOT目录中。
必然,如果你使用同一个计算机来测试应用,你想向HttpServer请求发送一个index.html 文件,那么使用如下URL:
http://localhost:8080/index.html
想要停止服务器,可以通过发送一个shutdown命令。该命令是被HttpServer 类中的静态SHUTDOWN_COMMAND变量所定义:
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
因此,要停止服务,你可以使用命令:
http://localhost:8080/SHUTDOWN
现在让我们来看看前面提到的await方法。下面一个程序清单给出了解释。
Listing 1.1. The HttpServer class' await method
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;
}
}
}
await方法是通过创建一个ServerSocket实例而开始的。然后它进入了一个WHILE循环:
serverSocket = new ServerSocket(
port, 1, InetAddress.getByName("127.0.0.1"));
...
// Loop waiting for a request
while (!shutdown) {
...
}
socket = serverSocket.accept();
在收到一个请求后,await方法从accept方法返回的socket实例中获得java.io.InputStream 和java.io.OutputStream对象。
input = socket.getInputStream();
output = socket.getOutputStream();
await于是就创建一个Request对象并调用它的 parse 方法来解析原始的HTTP请求信息。
// create Request object and parse
Request request = new Request(input);
request.parse();
接下来,await 方法创建了一个Response 对象,使用setRequest方法并调用它的sendStaticResource 方法。
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
最后,await关闭该Socket。调用Request的getUri方法来检查HTTP请求的URI是否是一个shutdown命令。如果是,shutdown变量被设置为true,程序退出while循环。
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);