2017.09.08:webServer服务器练习
要求:在浏览器上输入URL,实现读取本机html文件
主要目的:学习掌握HTTP超文本协议原理及浏览器与服务器数据交换过程实现
主要用的到的知识点:
1. HTTP协议:
2. html
3. tcp协议
4. 多线程:线程池
5. IO流
6. 配置文件
7. map集合
8. xml文件读写
一、浏览器与服务器之间的交互
1.1请求报文格式
HTTP请求由三部分组成:请求行,消息头,消息正文
请求行格式:
Method Request-URI HTTP-Version CRLF
例如:
GET /index.html HTTP/1.1CRLF
CR:回车 LF:换行
实体头(消息头)格式:
name:value CRLF
例如:
CRLF(消息头每一项信息后面都会有CRLF,当消息头所
信息发送完毕后,会单独发送一个CRLF。)
响应体(消息)正文:
实际随请求发送过来的数据(POST请求用,GET请求该项为空)
1.2 响应报文格式
HTTP响应的格式:
响应包含三部分:状态行,响应头,响应正文
状态行的格式:
HTTP-Version Status-Code Reason-PhraseCRLF
协议版本 , 状态值 , 状态描述
例如:
HTTP/1.1 200 OKCRLF
响应头格式:
name:value
例如:
Content-Type:text/htmlCRLF
Content-Length:35CRLF
CRLF
所有响应头信息完毕后也会单独发送一个CRLF
响应正文为服务端实际回应客户端的资源数据。
1.3 交互过程详解:
1.3.1浏览器和服务器建立连接:
a. 浏览器通过URL(统一资源定位)解析到服务器地址,若果URL地址是一个域名,不是IP地址,,则通过DNS(Domain Name System,域名系统)将域名解析成IP地址。
URL中不包括端口号,则使用协议的默认端口号。如http协议的默认端口号为80。
b. TCP三次握手建立连接:http应用层协议是建立在TCP传输层协议之上的。在浏览器发送http请求之前,会先通过三次握手建立TCP连接,提供可靠传输。
1.3.2 浏览器发送请求:浏览器对服务器发送的一系列消息,希望可以在服务器端获取到自己想要的数据。而这些消息就是浏览器对服务器发出的请求,一般包括请求的类型,请求的数据,请求的浏览器信息等。
1.3.3 服务器响应请求:服务器在接到浏览器发出的请求后,会对这些请求做出处理,并向浏览器进行反馈。反馈的数据包括浏览器的请求是否成功,状态码,浏览请请求所需要的数据信息等(一个html页面,图片等等)。
二、运行结果图:
三、代码实现:
1 服务端代码
package com.tedu.core;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*
* @author S-miles 2017-09-10
*/
public class webServer {
// 浏览器与服务器建立连接
private ServerSocket server;
// 创建线程池处理客户端请求任务
private ExecutorService threadPool;
/**
* 构造方法初始化,定义服务器端口
*
* @throws IOException
*/
public webServer() throws IOException {
try {
server = new ServerSocket(8088);
threadPool = Executors.newFixedThreadPool(100);
} catch (IOException e) {
throw e;
}
}
/**
* start() 程序执行方法
*/
public void start() {
try {
System.out.println("等在连接...");
Socket socket = server.accept();
System.out.println("已成功连接!");
// 创建一个客户端请求对象
ClientHandle handle = new ClientHandle(socket);
// 把客户端对象任务交给线程池处理
threadPool.execute(handle);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 主方法程序入口,启动程序
*
* @param args
*/
public static void main(String[] args) {
try {
webServer server = new webServer();
server.start();
} catch (Exception e) {
System.out.println("服务器启动失败!");
}
}
}
2 客户端代码
package com.tedu.core;
/**
* @author S-miles 2017-09-10
*
*/
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import com.tedu.common.HttpContext;
import com.tedu.http.HttpRequest;
import com.tedu.http.HttpResponse;
public class ClientHandle implements Runnable {
private Socket socket;
public ClientHandle(Socket socket) {
this.socket = socket;
}
public void run() {
// 要处理的任务
// 1 输入输出流要处理异常
try {
// 获取客户端请求 输入的流
InputStream input = socket.getInputStream();
// 封装一个请求对象
HttpRequest request = new HttpRequest(input);
// 服务端做出响应 ,获取服务端的输出流
OutputStream output = socket.getOutputStream();
// 封装一个响应对象
HttpResponse response = new HttpResponse(output);
// 获取请求的url路径
String url = request.getUrl();
// 创建一个文件对象
File file = new File("webapp" + File.separator + url);
if (file.exists()) {
System.out.println("找到资源" + file.length());
responseServer(HttpContext.STATUS_CODE_OK, file, response);
} else {
System.out.println("您访问的页面不存在!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String responseContentType(File file) {
String name = file.getName();
int index = name.lastIndexOf(".");
String ext = name.substring(index + 1);
String contentType = HttpContext.contentTypeMapping.get(ext);
return contentType;
}
private void responseServer(Integer Statue, File file, HttpResponse response) throws Exception {
response.setStatus(Statue);
response.setContentType(responseContentType(file));
response.setContentLength((int) file.length());
response.setEntity(file);
response.flush();
}
}
2.1 封装的请求对象
package com.tedu.http;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import com.tedu.common.HttpContext;
/**
*
* @author S-miles 2017-09-10
*
*/
public class HttpRequest {
private InputStream in;
private String methon;
private String url;
private String verson;
private String name;
private String value;
private Map<String, String> map;
public HttpRequest(InputStream input) throws Exception {
this.in = input;
try {
// 解析请求行
parseRequestLine();
// 解析消息头
parseRequestHeader();
} catch (Exception e) {
throw e;
}
}
// 解析一行
private String parseReadLine() throws Exception {
StringBuilder builder = new StringBuilder();
int c1 = -1, c2 = -1;
while ((c1 = in.read()) != -1) {
if (c1 == HttpContext.LF && c2 == HttpContext.CR) {
break;
}
c2 = c1;
builder.append((char) c1);
}
String line = builder.toString().trim();
System.out.println(line);
return line;
}
// 解析请求行
private void parseRequestLine() {
try {
String line = parseReadLine();
String[] data = line.split("\\s");
methon = data[0];
url = data[1];
verson = data[2];
System.out.println("解析请求行完毕:" + methon + " " + url + " " + verson);
} catch (Exception e) {
e.printStackTrace();
}
}
// 解析消息头
private void parseRequestHeader() {
String line = null;
try {
map = new HashMap<String, String>();
while (true) {
line = parseReadLine();
if (line.length() == 0) {
break;
}
name = line.substring(0, line.indexOf(":"));
value = line.substring(line.indexOf(":"));
map.put(name, value);
// System.out.println(line);
}
System.out.println("消息头解析完毕:" + line);
} catch (Exception e) {
System.out.println("异常发生了");
e.printStackTrace();
}
}
public String getMethon() {
return methon;
}
public String getUrl() {
return url;
}
public String getVerson() {
return verson;
}
}
2.2 封装的响应对象
package com.tedu.http;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import com.tedu.common.HttpContext;
/**
*
* @author S-miles 2017-09-10
*
*/
public class HttpResponse {
private OutputStream output;
// 响应状态描述集合
private Map<Integer, String> statusMap;
// 响应状态
private int status;
// 响应资源类型
private String contentType;
// 响应资源大小
private int contentLength = -1;;
// 响应实体
private File entity;
public HttpResponse(OutputStream output) {
this.output = output;
statusMap = new HashMap<Integer, String>();
statusMap.put(HttpContext.STATUS_CODE_OK, HttpContext.STATUS_REASON_OK);
}
// 响应请求资源
public void flush() throws Exception {
try {
// 发送响应行(状态 状态描述)
responseStatus();
// 响应头(资源类型,资源大小)
responseHeader();
// 响应实体(资源)
responseEntity();
} catch (Exception e) {
System.out.println("响应客户段失败!");
throw e;
}
}
// 发送一行
private void println(String line) throws Exception {
try {
output.write(line.getBytes("ISO8859-1"));
output.write(HttpContext.CR);
output.write(HttpContext.LF);
} catch (Exception e) {
throw e;
}
}
// 发送响应行(状态 状态描述)
private void responseStatus() throws Exception {
try {
String line = "HTTP/1.1" + " " + status + " " + statusMap.get(status);
System.out.println(line);
println(line);
} catch (Exception e) {
throw e;
}
}
// 响应头(资源类型,资源大小)
private void responseHeader() throws Exception {
try {
if (contentType.length() != 0) {
String line = contentType;
println(line);
}
if (contentLength >= 0) {
String line = contentType;
println(line);
}
println("");
} catch (Exception e) {
throw e;
}
}
// 响应实体(资源)
private void responseEntity() throws Exception {
BufferedInputStream bis = null;
try {
FileInputStream fis = new FileInputStream(entity);
bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(output);
int d = -1;
while ((d = bis.read()) != -1) {
bos.write(d);
}
bos.flush();
} catch (Exception e) {
throw e;
} finally {
if (bis != null) {
try {
bis.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public int getContentLength() {
return contentLength;
}
public void setContentLength(int contentLength) {
this.contentLength = contentLength;
}
public File getEntity() {
return entity;
}
public void setEntity(File entity) {
this.entity = entity;
}
}
3 HTTP相关配置文件
package com.tedu.common;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* HTTP协议相关信息定义
*
* @author adminitartor
*
*/
public class HttpContext {
public static final int CR = 13;
public static final int LF = 10;
/*
* 状态代码定义
*/
// 状态码-接受成功
public static final int STATUS_CODE_OK = 200;
// 状态描述-接受成功
public static final String STATUS_REASON_OK = "OK";
// 状态码-资源未发现
public static final int STATUS_CODE_NOTFOUND = 404;
// 状态描述-资源未发现
public static final String STATUS_REASON_NOTFOUND = "Not Found";
// 状态码-服务器发生未知错误
public static final int STATUS_CODE_ERROR = 500;
// 状态描述-服务器发生未知错误
public static final String STATUS_REASON_ERROR = "Internal Server Error";
/*
* 状态码-状态描述 对应的Map
*/
public static final Map<Integer, String> statusMap = new HashMap<Integer, String>();
/*
* Content-Type映射Map key:资源类型(资源文件的后缀名) value:对应该资源在HTTP协议中规定的ContentType
*
* 例如:index.html 那么这个文件在该map中对应key应当是html value对应的值就是text/html
*/
public static final Map<String, String> contentTypeMapping = new HashMap<String, String>();
static {
/*
* 根据配置文件初始化相关信息 /conf/web.xml
*
*/
// 1初始化ContentType映射
initContentTypeMapping();
// 2初始化状态码-状态描述
initStatus();
}
private static void initStatus() {
statusMap.put(STATUS_CODE_OK, STATUS_REASON_OK);
statusMap.put(STATUS_CODE_NOTFOUND, STATUS_REASON_NOTFOUND);
statusMap.put(STATUS_CODE_ERROR, STATUS_REASON_ERROR);
}
private static void initContentTypeMapping() {
/*
* 将web.xml配置文件中<type-mappings>中 的每一个<type-mapping>进行解析,将
* 其中属性ext的值作为key,将type属性的 值作为value存入到contentTypeMapping 这个Map中
*/
System.out.println("初始化ContentType");
try {
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("conf" + File.separator + "web.xml"));
Element root = doc.getRootElement();
Element mappingsEle = root.element("type-mappings");
List<Element> mappingList = mappingsEle.elements();
for (Element mapping : mappingList) {
String ext = mapping.attribute("ext").getValue();
String type = mapping.attribute("type").getValue();
contentTypeMapping.put(ext, type);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// public static void main(String[] args) {
// initContentTypeMapping();
// System.out.println(contentTypeMapping);
// }
}
人生如棋,不悔!!!