How Tomcat Works 1: A Simple Http Server

A Simple Web Server

    Web Server也称作HTTP server,用于客户端和服务器端HTTP通信。Java-base的webserver 主要使用java.net.Socket and java.net.ServerSocket.

 

1 HTTP协议介绍(Hypertest Transfer Protocol)

    Client request一个文件,服务器对request response. HTTP使用可靠的TCP连接(默认PORT 80),第一版HTTP/1.0, 当前版本HTTP/1.1。定义Request for Comments: http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf.

 

2 HTTP Requests

    一个HTTP Request由三个component构成:

  (1)Method - Uniform Resource Identifier(URI) - Protocol/Version

  (2)Request headers

  (3)Entity body

  例子:

POST /examples/index.jsp HTTP/1.1                   ------method-URI-protocol version

Accept: text/plain; text/html                                  ----- request headers

Accept-Language: en-gb

Connection: Keep-Alive

Host: localhost

User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)

Content-Length: 33

Content-Type: application/x-www-form-urlencoded

Accept-Encoding: gzip, deflate

                                                                               -------Must using blank line 分隔

lastName=Chan&firstName=Jack                         -------entity body

3 HTTP Response

    一个HTTP Response包括三个部分:

    (1)Protocol-Status code -Description

    (2)Response headers

    (3)Entity body

例子:

HTTP/1.1 200 OK                                                 ------Protocol-status code-description

Server: Microsoft-IIS/4.0                                      ----- response headers

Date: Mon, 5 Jan 2004 13:13:33 GMT

Content-Type: text/html

Last-Modified: Mon, 5 Jan 2004 13:13:12 GMT

Content-Length: 112

                                                                               -------Must using blank line 分隔

<html>                                                                   -------entity body

<head>

<title>HTTP response Example</title>

</head>

<body>

Welcome to Brainy software

</body>

</html>

 

 

4 The Socket Class(套接字类)

    如上所属,你需要知道IP地址和另个应用的port, socket让你能在网络的不同application间传送数据。

    Socket有多个构造函数,其中之一: public Socket(String host, int port) , host代表远程机器或者IP地址,port代表远程应用端口号。

    (1) 创建Socket: new Socket("127.0.0.1", "8080");

    (2) 发送byte streams: 使用Socket.getOutputStream()方法生成OutputStream对象

    (3) 发送text到远程app:使用OuptStream对象创建PrintWriter对象

    (4) 接受byte streams: 使用 Socket.getInputStream()方法返回InputStream对象

   

5 HTTP Server实例:

   1 模拟客户端:

    需要和本地服务器(127.0.0.1:8080)通信:发送一个HTTP request, 从server接收response. 显示结果运用stringbuffer存储并打印到控制台:

    实现原理: 使用Socket.getOutputStream()获取OutputStream对象->用PrintWriter将request info 通过socket写入远程server.  本地通过Socket.getInputStream()读取server端写入Socket的内容.=->打印in内容到控制台

package com.cisco.tomcat.httpserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientSocket {
	private static final String HOST = "127.0.0.1";
	private static final int PORT = 8080;
	
	public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
		requestLocalServer();
	}
	
	@SuppressWarnings("static-access")
	public static void requestLocalServer() throws UnknownHostException, IOException, InterruptedException {
		Socket socket = new Socket(HOST, PORT);
		OutputStream os = socket.getOutputStream();
		boolean autoflush = true;
		PrintWriter out = new PrintWriter(os, autoflush);  // 用于通过socket写request内容给remote app -》模拟request message
		BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));  // 用于读取socket从远端读取的字节流,模拟response message
		
		// 使用output发送request给server端
		out.println("GET /index.html HTTP/1.1");
		out.println("Host: localhost:8080");
		out.println("Connection: Close");
		
		// 使用input读取response
		boolean loop = true;
		StringBuffer sb = new StringBuffer(8096);
		while(loop) {
			if(in.ready()) {
				int i=0;
				while(i!=-1) {
					i = in.read();
					sb.append((char)i);
				}
				loop = false;
			}
			Thread.currentThread().sleep(50);
		}
		
		// 显示相应值到 控制台
		System.out.println(sb.toString());
		socket.close();
	}
}

 

 

     2)模拟服务端:

    监听本地8080端口. ServerSocket有多个构造函数,其一:public ServerSocket(int port, int backLog, InetAddress bindingAddress)  --- backLog 队列同时最大处理request数:new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));

    (1)实验需要三个模块,HTTP Server, Request, Response三个类

   (2)HTTP Server: 主要通过ServerSocket.accept去建立socket通信,使用Request对象解析请求信息,使用Response获取并发送静态资源

package com.cisco.tomcat.httpserver;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class HttpServer{
	/**
	 *  WEB_ROOT代表当前工作的根目录
	 *  HTML 和其他文件都在这个目录下
	 */
	public static final String WEB_ROOT = System.getProperty("user.dir")+ File.separator + "webroot";
	
	// ShutDown命令
	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
	
	private static final String HOST = "127.0.0.1";
	private static final int PORT = 8080;
	
	// 是否接收到 Shutdown命令
	private boolean shutdown = false;
		
	public static void main(String[] args) {
		HttpServer server = new HttpServer();
		server.await(); // 等待client端的请求
	}
	
	public void await() {
		ServerSocket serverSocket = null;
		try {
			serverSocket = new ServerSocket(PORT, 1, InetAddress.getByName(HOST));
		}catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
		
		// 循环等待请求
		while(!shutdown) {
			Socket socket = null;
			InputStream input = null;
			OutputStream output = null;
			
			try {
				socket = serverSocket.accept();
				input = socket.getInputStream();
				output = socket.getOutputStream();
				
				// 创建Request对象,并解析
				Request request = new Request(input);
				request.parse();
				
				// 创建Response对象, 发送request的静态资源给client
				Response response = new Response(output);
				response.setRequest(request);
				response.sendStaticResource();				
				
				// 关闭socket
				socket.close();
				
				// 查看是否有远程 关闭命令
				shutdown = SHUTDOWN_COMMAND.equals(request.getUri());
				
			} catch (IOException e) {					
				e.printStackTrace();
				continue;
			}
		
		}
	}
	
}

   (3)Request对象:解析请求信息,获取URI

package com.cisco.tomcat.httpserver;

import java.io.IOException;
import java.io.InputStream;

public class Request {
	private String uri;
	private InputStream input;
	private static final int BUFFER_SIZE = 2048;
	
	public Request(InputStream input) {
		this.input = input;
	}
	
	public void parse() {
		StringBuffer request = new StringBuffer(BUFFER_SIZE);
		int i;
		byte[] buffer = new byte[BUFFER_SIZE];
		try {
			i = input.read(buffer);
		}catch (IOException e) {
			e.printStackTrace();
			i = -1;
		}		
		for(int j=0; j<i; j++) {
			request.append((char)buffer[j]);
		}
		System.out.print(request.toString());
		uri = parseUri(request.toString());		
	}
	
	private String parseUri(String requestString) {
		int index1, index2;
		index1 = requestString.indexOf(" ");
		if(index1 != -1) {
			index2 = requestString.indexOf(" ", index1+1);
			if(index2>index1) {
				return requestString.substring(index1+1, index2);
			}
		}
		return null;
	}
	
	public String getUri() {
		return this.uri;
	}
	
}

 

 

   (4)Response对象:使用URI去搜寻Server资源,同时组装Response信息

package com.cisco.tomcat.httpserver;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * HTTP response = 
 *  Staus-Line(HTTP-Version  SP Status-Code SP Reason-Phrase)
 *  *((general-header | response-header | entity-header))
 *  [message - body]
 *
 */
public class Response {
	
	private static final int BUFFER_SIZE = 1024;
	Request request;
	OutputStream output;
	
	public Response(OutputStream output) {
		this.output = output;
	}
	
	public void sendStaticResource() throws IOException{
		byte[] bytes = new byte[BUFFER_SIZE];
		FileInputStream fis = null;
		try {
			File file =  new File(HttpServer.WEB_ROOT, request.getUri());
			if(file.exists()) {
				fis = new FileInputStream(file);
				int ch = fis.read(bytes, 0, BUFFER_SIZE);
				while(ch!=-1) {
					output.write(bytes, 0, ch);
					ch = fis.read(bytes,0, BUFFER_SIZE);
				}
			}else {
				String errorMessage = "HTTP/1.1 404 File Not Found\n"
											+ "Content-Type: text/html\n"
						                   + "Content-Length: 23\n"
											+"\n"
						                   +"<h1>File Not Found</h1>";
				output.write(errorMessage.getBytes());
			}
		}catch (Exception e) {
			System.out.println(e.toString());
		}finally {
			if(fis != null) {
				fis.close();
			}
		}
	}
	
	public void setRequest(Request request) {
		this.request = request;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值