科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Java2 参考大全:第18章 网络 (7)

Java2 参考大全:第18章 网络 (7)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

Java2 参考大全:第18章 网络 (7)

作者:Herbert Schildt 著 张玉清 吴溥峰等 译 来源:清华大学出版社 2007年10月30日

关键字: 网络 java

  • 评论
  • 分享微博
  • 分享邮件

LogMessage.java
LogMessage是一个简单的接口,它只定义了一个方法log( ),该方法只有一个String型参数。它用来抽象从httpd获得消息的输出。在应用程序条件下,该方法用来打印标准应用程序起始处控制台的输出。在小应用程序情况下,数据被送到一个视窗文本缓冲区。
代码 下面是LogMessage的源程序:
interface LogMessage {
public void log(String msg);
}
httpd.java
这真是一个具有很多功能的大类。我们将一个方法一个方法的讲解它。构造函数 存在5个主要的实例变量:port, docRoot,log,cache和stopFlag,它们都是私有的。其中的三个可以由httpd的独立构造函数设置,显示如下:
httpd(int p, String dr, LogMessage lm)
它初始化监听端口,初始化检索文件的目录以及初始化发送消息的接口。第四个实例变量cache,是在RAM中保存所有文件的Hashtable,它是在对象创建时被初始化的。stopFlag 控制程序的执行。静态部分 该类中有几个重要的静态变量。MIME标头中的“Server”域报告的版本在变量version中被发现。接着定义了一些常量:HTML文件的MIME类型,mime_text_html;
MIME的结束顺序,CRLF;代替原始目录请求返回的HTML文件名,indexfile以及在输入/输出中用到的数据缓冲区的大小, buffer_size。
然后mt 定义了一系列文件扩展名和这些文件相应的MIME 类型。types Hashtable在下一个块中被静态初始化,以用来包含作为可选关键字和值的数组mt。接着可以用fnameToMimeType( )方法来返回传入的每个filename的合适的MIME类型。如果filename 不含mt 表中的任何一个扩展名,该方法返回defaultExt或“text/plain.”。统计计算器 下面,我们声明另外5个实例变量。它们是没有private 修饰符,所以一个外部监控器可以检查这些值并以图形形式显示它们(我们将在后面演示)。这些变量表示了我们的Web服务器所用的统计资料。点击数和提供的字节的原始数目被存储在hits_served 和bytes_served 中。通常存储在高速缓存中的文件和字节数被存放在files_in_cache 和bytes_in_cache中。最后,我们把成功在高速缓存外部提供服务的点击数目存放在hits_to_cache中。toBytes( ) 接着,我们有一个方便的程序,toBytes( )。该程序把它的字符串转变成一个字节数组。这是十分必要的,因为Java的String 对象是以统一编码的字符形式存储的,而Internet 协议中的混合语例如HTTP是老式的8位ASCII码。makeMimeHeader( ) MakeMimeHeader()方法是另一个方便的方法,它用来创建由一些关键字值填充的MimeHeader对象。该方法返回的MimeHeader在Date成员中含有当前时间和时期,在Server成员中有服务器的名称和版本,Content-Type成员中有type参数,Content-Length成员中有length参数。error( ) error( )方法用来格式化HTML页并返回提出不能完成请求的Web客户。第一个参数code,是返回的出错代码。一般它在400到499之间。我们的服务器返回404和405错误。它用HttpResponse 类和适当的MimeHeader来封装返回的代码。该方法返回字符串表示是与HTML页有关的响应。该页包括易于人读的错误代码信息msg 和导致错误的url请求。getRawRequest( ) GetRawRequest( )方法是很简单的。它从流读取数据直到它获得两个连续的换行符。它忽略回车符号并且只寻找换行符。一旦它已经发现了连续的两个换行符,它使字节数组转向一个String对象并返回该对象。如果输入流在结束之前没有生成两个连续的换行符,它将返回null。这说明了HTTP服务器和客户的消息是怎样被格式化的。它们以状态的一行开始然后立即跟着一个MIME头。MIME头的结尾被两个换行符从剩余的内容中分离。logEntry( ) logEntry( )方法用来报告标准格式下的HTTP服务器的每个点击数。该方法生成的格式也许看起来有一点奇怪,但是它和HTTP日志文件的当前标准相匹配。该方法有若干个用来格式化每个日志项的日期戳的辅助变量和方法。months数组用来把月份转换成字符串。当host变量接受一个给定主机的连接时它由主HTTP循环设置。fmt02d( )方法把到9的整数格式化成两位的,第一位为零的数,然后结果字符串通过LogMessage接口变量log传输。writeString( ) 另一个方便的方法writeString( ),用来隐藏字符到字节数组的转变,以使它可以被写入流。
writeUCE( ) writeUCE( )方法占取一个OutputStream和一个UrlCacheEntry。它从高速缓存项提取信息,以便给网络客户传送消息。消息中包含适当的响应代码,MIME标头和内容。serveFromCache( ) 这个布尔方法试图在高速缓存中发现一个特殊的URL。如果成功,缓存项的内容被写给客户,hits_to_cache变量递增,调用者被返回true。否则,它只返回false。
loadFile( ) 该方法占用了一个InputStream、与之相应的url以及该URL的MimeHeader。用存储在MimeHeader的信息创建一个新的UrlCacheEntry。输入流在buffer_size字节块中被读取并传入UrlCacheEntry。结果的UrlCacheEntry 存储在高速缓存中。files_in_cache和bytes_in_cache变量更新,UrlCacheEntry返回调用者。readFile( ) readFile( )方法就loadFile( )方法来说看起来有些多余,实际不然。该方法严格的从本地文件系统读取文件。在本地文件系统中,loadFile( )方法用来与各种类型的流交流。如果File对象f存在,将会为它创建一个InputStream类。文件的大小是决定了的,MIME类型来自文件名。当loadFile( )被调用来做实际的读取和缓存高速工作时,有两个变量用来创建合适的MimeHeader。
writeDiskCache( ) writeDiskCache( )方法占用一个UrlCacheEntry 对象并且把它持久的写入本地磁盘。它从URL中建立一个目录名,确保用与系统有关的separatorChar代替斜线(/)字符。然后它调用mkdirs( )方法来保证这个URL的本地磁盘路径存在。最后,它打开一个FileOutputStream,向它写入所有的数据然后关闭它。handleProxy( ) HandleProxy()程序是该服务器的两个主要模式之一。基础思想是:如果你把你的浏览器设置成把该服务器当成代理服务器,则要传给它的请求将包括完整的URL,URL中常规GET方法可删除“http://”和主机名部分。我们仅把完整的URL拆碎,寻找“://”序列,其次是斜线(/),然后是使用非标准端口号的服务器的另一个冒号(:)。一旦我们发现了这些字符,我们就可以知道已经所需要的主机和端口号以及我们需要获取的URL。然后我们可以试图从RAM高速缓存中转载一个先前保存过的文档版本。果失败,我们可以试图从文件系统装载它到RAM高速缓存并且再尝试从RAM高速缓存装载它如果此举失败,那么事情变得很有趣,因为我们必须从远程站点读取该文件。为此,我们打开一个远程站点和端口的套接字。我们发送一个GET请求,要求传给我们的URL。无论我们从远程站点获得什么响应标头信息,我们把它传给客户。如果代码是200,对于成功的文件传输,我们还把确认数据流读到一个新的UrlCacheEntry类并且把它写入客户套接字。然后,我们调用writeDiskCache( )来保存传输结果到本地磁盘。我们记录传输日志,关闭套接字,然后返回。
handleGet( ) 当http后台进程像一个普通的Web服务器一样工作时,handleGet( )被调用。它有一个服务于文件的本地磁盘文件根目录。handleGet( )的参数告诉它向何处写结果,何处访问URL以及何处请求网络浏览器的MimeHeader。这个MIME标头将包括用户代理字符串和其他有用的属性。开始,我们试图在RAM高速缓存外为URL提供服务。如果此举失败,为寻找URL,我们顺序访问文件系统。如果文件不存在或不可读,我们向Web客户报告一个错误。否则,我们就用readFile( )方法获得文件的内容并把它们输入到高速缓存。然后调用writeUCE( )方法以用来传输文件内容到客户套接字。doRequest( ) 每次连接服务器时都会调用一次 doRequest( )方法。它解析请求字符串和引入的MIME标头。它在请求字符串中是否存在“://”的基础上判定是调用handlProxy( )还是 handleGet( )方法。如果不用GET,而使用任何其他的方法例如HEAD或POST,该程序向客户返回一个405错误。注意如果stopFlag 是true时HTTP请求被忽略。run( ) run( ) 方法在服务器线程启动时被调用。它在指定端口生成一个新的ServerSocket,在服务器套接字处进入一个调用accept( )的无限循环,把结果Socket传给doRequest( )作检查。
start( ) AND stop( ) 存在两个用来启动和终止服务器过程的方法。这些方法设置stopFlag的值。
main( ) 你可以在命令行用main( )方法来运行应用程序。它为服务器自身设置LogMessage参数,然后为log( )提供简单的控制台输出执行。
代码 下面是httpd的源代码:
import java.net.*;
import java.io.*;
import java.text.*;
import java.util.*;
class httpd implements Runnable, LogMessage {
private int port;
private String docRoot;
private LogMessage log;
private Hashtable cache = new Hashtable();
private boolean stopFlag;
private static String version = "1.0";
private static String mime_text_html = "text/html";
private static String CRLF = "\r\n";
private static String indexfile = "index.html";
private static int buffer_size = 8192;
static String mt[] = { // mapping from file ext to Mime-Type
"txt", "text/plain",
"html", mime_text_html,
"htm", "text/html",
"gif", "image/gif",
"jpg", "image/jpg",
"jpeg", "image/jpg",
"class", "application/octet-stream"
};
static String defaultExt = "txt";
static Hashtable types = new Hashtable();
static {
for (int i=0; i
types.put(mt[i], mt[i+1]);
}
static String fnameToMimeType(String filename) {
if (filename.endsWith("/")) // special for index files.
return mime_text_html;
int dot = filename.lastIndexOf('.');
String ext = (dot > 0) ? filename.substring(dot + 1) : defaultExt;
String ret = (String) types.get(ext);
return ret != null ? ret : (String)types.get(defaultExt);
}
int hits_served = 0;
int bytes_served = 0;
int files_in_cache = 0;
int bytes_in_cache = 0;
int hits_to_cache = 0;
private final byte toBytes(String s)[] {
byte b[] = s.getBytes();
return b;
}
private MimeHeader makeMimeHeader(String type, int length) {
MimeHeader mh = new MimeHeader();
Date curDate = new Date();
TimeZone gmtTz = TimeZone.getTimeZone("GMT");
SimpleDateFormat sdf =
new SimpleDateFormat("dd MMM yyyy hh:mm:ss zzz");
sdf.setTimeZone(gmtTz);
mh.put("Date", sdf.format(curDate));
mh.put("Server", "JavaCompleteReference/" + version);
mh.put("Content-Type", type);
if (length >= 0)
mh.put("Content-Length", String.valueOf(length));
return mh;
}
private String error(int code, String msg, String url) {
String html_page = "" + CRLF +
"

" + code + " " + msg + "

" + CRLF;
if (url != null)
html_page += "Error when fetching URL: " + url + CRLF;
html_page += "" + CRLF;
MimeHeader mh = makeMimeHeader(mime_text_html, html_page.length());
HttpResponse hr = new HttpResponse(code, msg, mh);
logEntry("GET", url, code, 0);
return hr + html_page;
}
// Read 'in' until you get two \n's in a row.
// Return up to that point as a String.
// Discard all \r's.
private String getRawRequest(InputStream in)
throws IOException {
byte buf[] = new byte[buffer_size];
int pos=0;
int c;
while ((c = in.read()) != -1) {
switch (c) {
case '\r':
break;
case '\n':
if (buf[pos-1] == c) {
return new String(buf,0,pos);
}
default:
buf[pos++] = (byte) c;
}
}
return null;
}
static String months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
private String host;
// fmt02d is the same as C's printf("%02d", i)
private final String fmt02d(int i) {
if(i < 0) {
i = -i;
return ((i < 9) ? "-0" : "-") + i;
}
else {
return ((i < 9) ? "0" : "") + i;
}
}
private void logEntry(String cmd, String url, int code, int size) {
Calendar calendar = Calendar.getInstance();
int tzmin = calendar.get(Calendar.ZONE_OFFSET)/(60*1000);
int tzhour = tzmin / 60;
tzmin -= tzhour * 60;
log.log(host + " - - [" +
fmt02d(calendar.get(Calendar.DATE) ) + "/" +
months[calendar.get(Calendar.MONTH)] + "/" +
calendar.get(Calendar.YEAR) + ":" +
fmt02d(calendar.get(Calendar.HOUR) ) + ":" +
fmt02d(calendar.get(Calendar.MINUTE) ) + ":" +
fmt02d(calendar.get(Calendar.SECOND)) + " " +
fmt02d(tzhour) + fmt02d(tzmin) +
"] \"" +
cmd + " " +
url + " HTTP/1.0\" " +
code + " " +
size + "\n");
hits_served++;
bytes_served += size;
}
private void writeString(OutputStream out, String s)
throws IOException {
out.write(toBytes(s));
}
private void writeUCE(OutputStream out, UrlCacheEntry uce)
throws IOException {
HttpResponse hr = new HttpResponse(200, "OK", uce.mh);
writeString(out, hr.toString());
out.write(uce.data, 0, uce.length);
logEntry("GET", uce.url, 200, uce.length);
}
private boolean serveFromCache(OutputStream out, String url)
throws IOException {
UrlCacheEntry uce;
if ((uce = (UrlCacheEntry)cache.get(url)) != null) {
writeUCE(out, uce);
hits_to_cache++;
return true;
}
return false;
}
private UrlCacheEntry loadFile(InputStream in, String url,
MimeHeader mh)
throws IOException {
UrlCacheEntry uce;
byte file_buf[] = new byte[buffer_size];
uce = new UrlCacheEntry(url, mh);
int size = 0;
int n;
while ((n = in.read(file_buf)) >= 0) {
uce.append(file_buf, n);
size += n;
}
in.close();
cache.put(url, uce);
files_in_cache++;
bytes_in_cache += uce.length;
return uce;
}
private UrlCacheEntry readFile(File f, String url)
throws IOException {
if (!f.exists())
return null;
InputStream in = new FileInputStream(f);
int file_length = in.available();
String mime_type = fnameToMimeType(url);
MimeHeader mh = makeMimeHeader(mime_type, file_length);
UrlCacheEntry uce = loadFile(in, url, mh);
return uce;
}
private void writeDiskCache(UrlCacheEntry uce)
throws IOException {
String path = docRoot + uce.url;
String dir = path.substring(0, path.lastIndexOf("/"));
dir.replace('/', File.separatorChar);
new File(dir).mkdirs();
FileOutputStream out = new FileOutputStream(path);
out.write(uce.data, 0, uce.length);
out.close();
}
// A client asks us for a url that looks like this:
// http://the.internet.site/the/url
// we go get it from the site and return it...
private void handleProxy(OutputStream out, String url,
MimeHeader inmh) {
try {
int start = url.indexOf("://") + 3;
int path = url.indexOf('/', start);
String site = url.substring(start, path).toLowerCase();
int port = 80;
String server_url = url.substring(path);
int colon = site.indexOf(':');
if (colon > 0) {
port = Integer.parseInt(site.substring(colon + 1));
site = site.substring(0, colon);
}
url = "/cache/" + site + ((port != 80) ? (":" + port) : "") +
server_url;
if (url.endsWith("/"))
url += indexfile;
if (!serveFromCache(out, url)) {
if (readFile(new File(docRoot + url), url) != null) {
serveFromCache(out, url);
return;
}
// If we haven't already cached this page, open a socket
// to the site's port and send a GET command to it.
// We modify the user-agent to add ourselves... "via".
Socket server = new Socket(site, port);
InputStream server_in = server.getInputStream();
OutputStream server_out = server.getOutputStream();
inmh.put("User-Agent", inmh.get("User-Agent") +
" via JavaCompleteReferenceProxy/" + version);
String req = "GET " + server_url + " HTTP/1.0" + CRLF +
inmh + CRLF;
writeString(server_out, req);
String raw_request = getRawRequest(server_in);
HttpResponse server_response =
new HttpResponse(raw_request);
writeString(out, server_response.toString());
if (server_response.statusCode == 200) {
UrlCacheEntry uce = loadFile(server_in, url,
server_response.mh);
out.write(uce.data, 0, uce.length);
writeDiskCache(uce);
logEntry("GET", site + server_url, 200, uce.length);
}
server_out.close();
server.close();
}
} catch (IOException e) {
log.log("Exception: " + e);
}
}
private void handleGet(OutputStream out, String url,
MimeHeader inmh) {
byte file_buf[] = new byte[buffer_size];
String filename = docRoot + url +
(url.endsWith("/") ? indexfile : "");
try {
if (!serveFromCache(out, url)) {
File f = new File(filename);
if (! f.exists()) {
writeString(out, error(404, "Not Found", filename));
return;
}
if (! f.canRead()) {
writeString(out, error(404, "Permission Denied", filename));
return;
}
UrlCacheEntry uce = readFile(f, url);
writeUCE(out, uce);
}
} catch (IOException e) {
log.log("Exception: " + e);
}
}
private void doRequest(Socket s) throws IOException {
if(stopFlag)
return;
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
String request = getRawRequest(in);
int fsp = request.indexOf(' ');
int nsp = request.indexOf(' ', fsp+1);
int eol = request.indexOf('\n');
String method = request.substring(0, fsp);
String url = request.substring(fsp+1, nsp);
String raw_mime_header = request.substring(eol + 1);
MimeHeader inmh = new MimeHeader(raw_mime_header);
request = request.substring(0, eol);
if (method.equalsIgnoreCase("get")) {
if (url.indexOf("://") >= 0) {
handleProxy(out, url, inmh);
} else {
handleGet(out, url, inmh);
}
} else {
writeString(out, error(405, "Method Not Allowed", method));
}
in.close();
out.close();
}
public void run() {
try {
ServerSocket acceptSocket;
acceptSocket = new ServerSocket(port);
while (true) {
Socket s = acceptSocket.accept();
host = s.getInetAddress().getHostName();
doRequest(s);
s.close();
}
} catch (IOException e) {
log.log("accept loop IOException: " + e + "\n");
} catch (Exception e) {
log.log("Exception: " + e);
}
}
private Thread t;
public synchronized void start() {
stopFlag = false;
if (t == null) {
t = new Thread(this);
t.start();
}
}
public synchronized void stop() {
stopFlag = true;
log.log("Stopped at " + new Date() + "\n");
}
public httpd(int p, String dr, LogMessage lm) {
port = p;
docRoot = dr;
log = lm;
}
// This main and log method allow httpd to be run from the console.
public static void main(String args[]) {
httpd h = new httpd(80, "c:\\www", null);
h.log = h;
h.start();
try {
Thread.currentThread().join();
} catch (InterruptedException e) {};
}
public void log(String m) {
System.out.print(m);
}
}
查看本文来源
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章