了解以下内容:
1. 如何通过和客户端保持连接来减少返回响应的系统开销;
2. 如何使用缓冲技术;
3. 返回错误和其他状态码;
4. 发送定制首部信息
5. 重定向请求
6. 处理servlet异常
7. 检测用户断开连接和记录服务器日志
持续性连接
当一个客户端想请求服务器上的Web浏览器文档时,首先要建立一个服务器的套接字连接。通过这个连接,客户端作出请求并接受服务器的响应。客户端通过发送一个空白行表示请求发送完成。作为答复,服务端用关闭套接字连接来表示响应完成。
一个更好的方式是使用持续性连接。使用这个技巧的方法是在一个服务器响应结束的地方和一个客户端开始请求的地方,客户端和服务器达成某种协议,比如可以使用空白行这样的记号。如果响应本身就含有一个空白行呢?所以持续性连接通过设置服务器响应的Content-Length头部告诉客户端响应的大小,当客户端知道在那样大的响应之后,它就能重新控制套接字连接了。
sevlet可以设置这个头部:
public void ServletResponse.setContentLength(int len);
在一个HTTP servlet中,这个方法是可选的。如果使用了这个方法,servlet就能使用持续性连接的优势,客户端也能在下载的过程中准确显示进程监控。
注意:servlet必须在发送响应体之前调用该方法;给定的长度必须完全准确。
响应缓冲
使用响应缓冲区,就可以让servlet写几个输出内容而保证这些响应不会马上发出。如果servlet找到了错误,只要缓冲区足够,状态码和首部还会被转换。
响应缓冲也为解决内容长度的预计算提供了一个简单的解决方法,servlet可以使用缓冲区自动计算内容长度。
HttpServletResponse.setBufferSize();
需要注意的是,这种缓冲响应是有代价的,可能会导致延迟客户端收到数据。
控制响应缓冲
public void ServletResponse.setBufferSize(int size)
throws IllegalStateException
使用这个方法告诉服务器,servlet能接收的最小缓冲区大小(字节数)。这个方法必须在写响应体之前使用。
public boolean ServletResponse.isCommitted()
这个方法可以判断有响应体的任何部分已经被发送,如果返回true,说明改变状态码和首部已经太晚了,Content-Length不能被自动计算。
public void ServletResponse.reset() throws IllegalStateException
如果需要重新开始响应,可以调用reset()方法来清除缓冲区内容和目前设定的状态码和首部。这个方法需要在响应被发送前调用。后面说的sendError()和sendRedirect()方法和这个功能类似,只是它们清除响应缓冲区的内容,但是不清楚响应首部。
public void ServletResponse.flushBuffer() throws IOException
flushBuffer()方法强行发送缓冲区的任何内容,使得客户端马上开始接收响应内容。一旦调用这个方法,意味着状态码和首部将被写入,而且reset()方法无法被调用。
状态码
正常的就使用setStatus(),需要在响应发送前被调用。发生错误情况时候,可以用setStatus()设置不同状态码。也可以用sendError()方法生成服务器默认的错误页。
当返回错误时,也可以自己在web.xml里配置错误页
<web-app>
<!-- ..... -->
<error-page>
<error-code>
400
</error-code>
<location>
/400.html
</location>
</error-page>
<error-page>
<error-code>
404
</error-code>
<location>
/404.html
</location>
</error-page>
</web-app>
日志
sevlet使用log()将消息和错误信息写到日志文件中。
public void GenericServlet.log(String msg)
public void GenericServlet.log(String msg, Throwable t)
报告
打印异常栈的方式
使用com.oreilly.servlet.ServletUtils类里面的getStackTraceAsString( )方法。
public static String getStackTraceAsString(Throwable t) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(bytes, true);
t.printStackTrace(writer);
return bytes.toString();
}
类似这样使用
try {
ServletUtils.returnFile(file, out);
}
catch (FileNotFoundException e) {
log("Could not find file: " + e.getMessage());
res.sendError(res.SC_NOT_FOUND);
}
catch (IOException e) {
log("Problem sending file", e);
res.sendError(res.SC_INTERNAL_SERVER_ERROR,
ServletUtils.getStackTraceAsString(e));
}