只为理解浏览器与服务器的交互过程,所以编写的一个简易的WebServer程序
该程序可以访问服务器上的网页,txt,gif等资源。
1.首先了解一个标准的web请求响应头格式
GET /Sever/index.html HTTP/1.1
Host: localhost:1234
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2,ja;q=0.2
第一行GET /Sever/index.html HTTP/1.1 说明使用GET方法获得指定URL下的文件 当然还可以有POST 方法 接下来/Sever/index.html 访问的是服务器上的资源 这其实可以通过映射 本程序中使用的 资源请求名 是真实路径 再之后的HTTP/1.1 说明使用的是HTTP协议1.1
第二行 是主机名 和端口号 意思是访问域名 localhost的 1234端口 这里涉及到DNS 协议 不再论述 浏览器默认访问80端口 想访问别的端口需要特别指出
第三行 持续连接 不断开
第四行 表示浏览器支持什么样的文档内容 可以是xml html application等
第五行 表示用户浏览器类型
第六行 表示接受文件类型 可以是 gzip deflate 这是为了减少网络流量所有
第七行 表示接受的编码方式
这里我们只 使用第一行 并实现GET请求获得浏览器资源 /Sever/index.html 指定资源的内容
2.以下为一个测试类 可以获得请求的参数
可以试以下
得到标准的请求格式
package SocketPrograming;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Test {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(1234);
Socket client = null;
while (true) {
client = server.accept();
InputStream stream = new BufferedInputStream(
client.getInputStream());
int b;
while ((b = stream.read()) != -1) {
System.out.write((char) b);
}
stream.close();
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
浏览器访问 http://localhost:1234/server/index.html?username=123&&password=123
然后在JAVA控制台可以看到如下的输出
GET /server/index.html?username=123&&password=123 HTTP/1.1
Host: localhost:1234
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2,ja;q=0.2
我们剩下的就是要解析 红字部分 然后根据解析内容返回资源
public void run() {
//启动一个线程池 当线程池为空时等待
String root = documentRootDirectory.getPath();
while (true) {
Socket connection;
synchronized (pool) {
while (pool.isEmpty()) {
try {
pool.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
//依照FIFO处理请求
connection = (Socket) pool.remove(0);
}
try {
String filename;
String contentType;
OutputStream raw = new BufferedOutputStream(
connection.getOutputStream());
Writer out = new OutputStreamWriter(raw);
//读入请求
Reader in = new InputStreamReader(new BufferedInputStream(
connection.getInputStream()), "ASCII");
StringBuffer requestLine = new StringBuffer();
int c;
while (true) {
c = in.read();
if (c == '\r' || c == '\n') {
break;
}
requestLine.append((char) c);
}
String get = requestLine.toString();
System.out.println(get);
//请求格式为 GET /server/index.html HTTP
//所以依次度出的TokenString 为GET,/server/index.html,HTTP
StringTokenizer st = new StringTokenizer(get);
String method = st.nextToken();
String version = "";
if (method.equals("GET")) {
filename = st.nextToken();
if (filename.endsWith("/")) {
filename += indexFileName;
}
//从名字得到文件类型
contentType = guessContentTypeFromName(filename);
if (st.hasMoreTokens()) {
version = st.nextToken();
}
File theFile = new File(documentRootDirectory,
filename.substring(1, filename.length()));
if (theFile.canRead()
&& theFile.getCanonicalPath().startsWith(root)) {
DataInputStream fis = new DataInputStream(
new BufferedInputStream(new FileInputStream(
theFile)));
byte[] theData = new byte[(int) theFile.length()];
fis.readFully(theData);
fis.close();
//检测如果是用HTTP 1.0
if (version.startsWith("HTTP ")) {
out.write("HTTP/1.0 200 OK\r\n");
Date now = new Date();
out.write("Date:" + now + "\r\n");
out.write("Server:HelloWorld/1.0\r\n");
out.write("Content-length: " + theData.length
+ "\r\n");
out.write("Content-type: " + contentType
+ "\r\n\r\n");
out.flush();
}
raw.write(theData);
raw.flush();
} else {
//资源不存在处理
if (version.startsWith("HTTP ")) {
out.write("HTTP/1.0 404 File Not Found\r\n");
Date now = new Date();
out.write("Date:" + now + "\r\n");
out.write("Server:HelloWorld/1.0\r\n");
out.write("Content-type: text/html\r\n\r\n");
}
out.write("<HTML>\r\n");
out.write("<HEAD><TITLE>FILE NOT FOUNT</TITLE>\r\n");
out.write("</HEAD>\r\n");
out.write("<BODY>");
out.write("<H1>HTTP Error 404:File Not Found</H1>\r\n");
out.write("</BODY></HTML>");
out.flush();
}
} else {
//POST方法处理 这里不写了
if (version.startsWith("HTTP ")) {
out.write("HTTP/1.0 501 Not Implemented\r\n");
Date now = new Date();
out.write("Date:" + now + "\r\n");
out.write("Server:HelloWorld/1.0\r\n");
out.write("Content-type: text/html\r\n\r\n");
}
out.write("<HTML>\r\n");
out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n");
out.write("</HEAD>\r\n");
out.write("<BODY>");
out.write("<H1>HTTP Error 501:Not Implemented</H1>\r\n");
out.write("</BODY></HTML>/r/n");
out.flush();
}
} catch (Exception e) {
// TODO: handle exception
}finally{
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.服务器的启动
这里只有单独的服务器类,以上部分和以下部分是线程处理类
注释附上
package SocketPrograming;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class T03WebServer extends Thread {
// 服务器可以设置的根目录
private File documentRootDirectory;
// 默认首页
private String indexFileName = "index.html";
private ServerSocket server;
// 同时连接最大数
private int numThreads = 50;
// 如果不设置目录则默认目录和端口
private static String DEFAULT_DOCUMENT_ROOT = "/Users/Kratos/Desktop/TestWebServer";
private static int DEFAULT_PORT = 1234;
public T03WebServer(File documentRootDirectory, int port,
String indexFileName) throws IOException {
this.documentRootDirectory = documentRootDirectory;
this.indexFileName = indexFileName;
this.server = new ServerSocket(port);
}
public T03WebServer(File documentRootDirectory, int port)
throws IOException {
this(documentRootDirectory, port, "index.html");
}
//
public void run() {
for (int i = 0; i < numThreads; i++) {
//这里是启动一个线程池来处理请求
Thread t = new Thread(new RequestProcessor(documentRootDirectory,
indexFileName));
t.start();
}
System.out.println("Accepting connection on port "
+ server.getLocalPort());
//这里不断接受请求 然后将请求Socket加入到线程池
while (true) {
try {
Socket request = server.accept();
RequestProcessor.processRequest(request);
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) {
//设置监听端口和文件根目录
File docroot = null;
int port;
if (args.length < 2) {
docroot = new File(DEFAULT_DOCUMENT_ROOT);
port = DEFAULT_PORT;
} else {
docroot = new File(args[0]);
port = Integer.parseInt(args[1]);
}
//服务器启动
try {
T03WebServer webServer = new T03WebServer(docroot, port);
webServer.start();
} catch (Exception e) {
// TODO: handle exception
}
}
}
实际效果如下