一个简易的WebServer实现

只为理解浏览器与服务器的交互过程,所以编写的一个简易的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
		}
	}
}



实际效果如下



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值