实战:手写WebServer:(Web服务器)

实战:手写WebServer:(Web服务器)

项目上传到网盘(包含Firefox 浏览器 RESTer 插件):
链接:https://pan.baidu.com/s/1pzBuTMEQ55e-fKGSirhwdA
提取码:fry3

目的:承上启下,总结所学过得java基础知识+为即将到来的javaweb做准备

简介:
      上网浏览网页,离不开服务器,客户请求页面,服务器响应内容,响应得内容是根据每个Web请求来产生动态内容得,其内部即启动多个线程来产生不同内容。这种请求响应式得交互,都是基于HTTP协议的。

涉及知识

  • OOP 面对对象思想
  • 容器
  • IO流
  • 多线程
  • 网络编程
  • 反射机制
  • XML解析
  • HTML
  • HTTP协议

1.反射:
        Reflection,把java类中的各种结构(方法、属性、构造器、类名)映射成一个个java对象。注意是在运行期,利用反射技术可以对一个类进行解剖,同时反射也是框架设计的灵魂!

package edu.hue.jk;

import java.lang.reflect.InvocationTargetException;

public class ReflectTest {

	public static void main(String[] args) throws Exception{
		
		//获取Class对象的三种方式
		//1.对象.getClass()
		Phone phone = new Phone();
		Class clazz = phone.getClass();
		//2.类.class();
		clazz = Phone.class;
		//3.Class.forName("包名.类名");  这就是反射机制 传的是字符串 就很灵活了
		clazz = Class.forName("edu.hue.jk.Phone");
		
		//创建对象
		
		Phone phone2 = (Phone)clazz.getConstructor().newInstance();
	}

	//手机类
 static class Phone{
		public Phone() {
		}
	}
}

2.XML解析
XML:Extensible Markup Language,可扩展标记语言,作为数据的一种存储格式或用于存储软件的参数,程序解析此配置文件,就可以达到不修改代码就能更改程序的目的。

package edu.hue.jk;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class XmlTest01 {
/**
 * 熟悉SAX解析流程
 * @author 超爱学习的可琛同学
 * @throws IOException 
 * @throws SAXException 
 * @throws ParserConfigurationException 
 */
	public static void main(String[] args) throws SAXException, IOException, ParserConfigurationException {
		//SAX解析
		//1、获取解析工厂
		SAXParserFactory factory=SAXParserFactory.newInstance();
		//2、从解析工厂获取解析器
		SAXParser parse =factory.newSAXParser();
		//3、编写处理器
		
		//4、加载文档 Document 注册处理器
		PersonHandler handler=new PersonHandler();
		//5、解析
		parse.parse(Thread.currentThread().getContextClassLoader()
		.getResourceAsStream("edu/hue/jk/p.xml")
		,handler);
		List<Person> persons = handler.getPersons();
		for (Person person : persons) {
			System.out.println(person.getName()+"---"+person.getAge());
			
		}
	}
	
	static class PersonHandler extends DefaultHandler{
		private List<Person> persons;
		private Person person;
		private String tag;//存储当前操作的标签
		//解析文档开始
		@Override
		public void startDocument() throws SAXException { 
			persons = persons = new ArrayList<Person>();
			  System.out.println("--文档解析开始--");
		}
		public java.util.List<Person> getPersons() {
			return persons;
		}
		public void setPersons(java.util.List<Person> persons) {
			this.persons = persons;
		}
		//解析标签元素开始
		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes)
				throws SAXException {
			tag = qName;
			if (qName.equals("person")) {
				person = new Person();
			}
		}
		
		@Override
		public void characters(char[] ch, int start, int length) throws SAXException {
			String  string = new String(ch,start,length); 
			if (tag != null && tag.equals("name")) {
				person.setName(string);
			}else if ( tag!=null && tag.equals("age")) {
				person.setAge(Integer.valueOf(string));
			}
			
		}
		
		//解析标签元素结束
		@Override
		public void endElement(String uri, String localName, String qName) throws SAXException {
			if(qName.equals("person")) {
				persons.add(person);
			}
			tag = null;
		}
		
		//解析文档结束
		@Override
		public void endDocument() throws SAXException {
			System.out.println("--文档解析结束--");
		}
	}

}

package edu.hue.jk;
//一个Javabean
public class Person {
	private String name;
	private int age ;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public Person() {
		super();
	}
	
}

<?xml version="1.0" encoding="UTF-8" ?>
<persons>
    <person>
      <name>至尊宝</name>
      <age>9000</age>
   </person>
   <person>
      <name>白晶晶</name>
      <age>7000</age>
   </person>
</persons>

在这里插入图片描述

接下来解析一个稍微复杂的XML

<?xml version="1.0" encoding="UTF-8"?>  
 <web-app>
 <servlet>
  <servlet-name>login</servlet-name>
  <servlet-class>edu.hue.jk.servlet.LoginServlet</servlet-class>
 </servlet>
   <servlet>
  <servlet-name>reg</servlet-name>
  <servlet-class>edu.hue.jk.servlet.RegisterServlet</servlet-class>
 </servlet>   
 <servlet-mapping>
  <servlet-name>login</servlet-name>
  <url-pattern>/login</url-pattern> 
  <url-pattern>/g</url-pattern> 
 </servlet-mapping>  
 <servlet-mapping>
  <servlet-name>reg</servlet-name>
  <url-pattern>/reg</url-pattern> 
 </servlet-mapping>
 </web-app>

package edu.hue.jk.servlet;

import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import edu.hue.jk.Person;


public class XmlTest02 {

	public static void main(String[] args) throws Exception {
		//SAX解析
				//1、获取解析工厂
				SAXParserFactory factory=SAXParserFactory.newInstance();
				//2、从解析工厂获取解析器
				SAXParser parse =factory.newSAXParser();
				//3、编写处理器
				
				//4、加载文档 Document 注册处理器
				WebHandler  handler=new WebHandler ();
				//5、解析
				parse.parse(Thread.currentThread().getContextClassLoader()
				.getResourceAsStream("edu/hue/jk/servlet/web.xml")
				,handler);
				WebContext webContext = new WebContext(handler.getEntities(), handler.getMappings());
				
				String classForName = webContext.getClz("/login");
				Class clazz =  Class.forName(classForName);
				Servlet servlet = (Servlet)clazz.getConstructor().newInstance();
				servlet.server();
				
	}
	
	static class WebHandler extends DefaultHandler{
		private List<Entity> entities;
		private List<Mapping> mappings;
		private Entity entity;
		private Mapping mapping;
		public List<Mapping> getMappings() {
			return mappings;
		}
		public List<Entity> getEntities() {
			return entities;
		}
		private String tag;
		private boolean isMapping = true;

		//解析文档开始
				@Override
				public void startDocument() throws SAXException { 
					entities = new ArrayList<>();
					mappings = new ArrayList<>();
					  System.out.println("--文档解析开始--");
				}
				//解析标签元素开始
				@Override
				public void startElement(String uri, String localName, String qName, Attributes attributes)
						throws SAXException {
					tag = qName;
					if (qName.equals("servlet")) {
						entity = new Entity();
						isMapping = false;
					}else if (qName.equals("servlet-mapping")) {
						mapping= new Mapping();
						isMapping = true;	
					}
				}
				
				@Override
				public void characters(char[] ch, int start, int length) throws SAXException {
					String  string = new String(ch,start,length); 
					if(!isMapping) {
					if (tag != null && tag.equals("servlet-name")) {
						entity.setName(string);
					}else if ( tag!=null && tag.equals("servlet-class")) {
						entity.setCls(string);
					}
					}else {
						if (tag != null && tag.equals("servlet-name")) {
							mapping.setName(string);
						}else if ( tag!=null && tag.equals("url-pattern")) {
							mapping.addPattern(string);
						}
					}
				}
				
				//解析标签元素结束
				@Override
				public void endElement(String uri, String localName, String qName) throws SAXException {
					if(qName.equals("servlet")) {
						entities.add(entity);
					}else if (qName.equals("servlet-mapping")) {
						mappings.add(mapping);
					}
					
					tag = null;
				}
				
				//解析文档结束
				@Override
				public void endDocument() throws SAXException {
					System.out.println("--文档解析结束--");
				}
	}
}

package edu.hue.jk.servlet;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WebContext {
	private List<Entity> entities  =null;
	private List<Mapping> mappings =null;
	private Map<String, String> entityMap = new HashMap<>();
	private Map<String, String> mappingMap = new HashMap<>();
	public WebContext(List<Entity> entities ,List<Mapping> mappings) {
		this.entities = entities;
		this.mappings = mappings;
		
		for(Entity entity : entities) {
			entityMap.put(entity.getName(), entity.getCls());
		}
		
		for(Mapping mapping : mappings) {
			for(String pattern : mapping.getPatterns()) {
				mappingMap.put(pattern, mapping.getName());
			}
		}
		
	}
	
	public String getClz(String pattern) {
		
		return entityMap.get(mappingMap.get(pattern));
	}
}

package edu.hue.jk.servlet;

public class Entity {
	private String name;
	private String cls;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCls() {
		return cls;
	}
	public void setCls(String cls) {
		this.cls = cls;
	}
	public Entity(String name, String cls) {
		super();
		this.name = name;
		this.cls = cls;
	}
	public Entity() {
		super();
	}
	

}

package edu.hue.jk.servlet;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Mapping {
	private String name;
	private Set<String> patterns ;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Set<String> getPatterns() {
		return patterns;
	}
	public void setPatterns(Set<String> patterns) {
		this.patterns = patterns;
	}
	
	public Mapping() {
		patterns = new HashSet<>();
	}
	
	public void  addPattern(String string) {
		patterns.add(string);
	}
	

}

package edu.hue.jk.servlet;

public interface Servlet {
	void server();
}

package edu.hue.jk.servlet;

public class LoginServlet implements Servlet {

	@Override
	public void server() {
		System.out.println("Login Server");
	}

}

package edu.hue.jk.servlet;

public class RegisterServlet implements Servlet{

	@Override
	public void server() {
		System.out.println("Register Server");
	}

}

3.简单的HTML

<html>
	<head>
		<title>第一个html登录</title>
	</head>
	<body>
		<h1>表单的使用</h1>
		<pre>
				post:提交 ,基于http协议不同    量大   请求参数url不可见 安全<br/>
				get:  默认,获取,基于http协议不同  量小  请求参数url可见 不安全<br/>
				action: 请求web服务器的资源   URL<br/>
				name:作为后端使用,区分唯一: 请求服务器,必须存在,数据不能提交<br/>
				id: 作为前端使用,区分唯一<br/>
		</pre>
		<form method="get" action="http://localhost:8888/index.html">
						用户名:<input type="text" name="uname"  id="uname"/>
						密码:<input type="password" name="pwd"  id="pwd"/>
						<input type="submit" value="登录"/>
		</form>
	</body>
</html>

在这里插入图片描述

4.HTTP协议
HTTP:Hyper Text Transfer Protocol 超文本传输协议

  • 请求协议

1.请求行:方法(GET/POST)URI 协议/版本
2.请求头:(Request Header)
3.请求正文

  • 响应协议

1.状态行:协议\版本、状态码、状态描述
2.响应头:(Response Header)
3.响应正文

package edu.hue.jk.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 封装请求协议:获取method uri以及请求参数
 * @author Mr . Xu's  PC
 *
 */
public class Request {
	private List<String> list ;
	private static final String CRLF = "\r\n";
	//请求信息
	private String requstInfo;
	//请求方式
	private String method;
	//请求URL
	private String url="";
	//请求参数
	private String queryStr;
	//存储参数
	private Map<String, List<String>> parameterMaP;
	public Request(InputStream is) {
		parameterMaP = new HashMap<>();
		list = new ArrayList<>();
		byte[] datas = new byte[1024*1024];
		int len;
		try {
			len = is.read(datas);
			requstInfo = new String(datas, 0, len);
			System.out.println(requstInfo);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
			return;
		}
		
		parseRequestInfo();
	}
	
	public Request(Socket client) throws IOException {
		this(client.getInputStream());
		//构造器的相互调用
	}
	
	
	private void parseRequestInfo() {
		System.out.println("--分解--");
		method = requstInfo.substring(0, requstInfo.indexOf("/")).toLowerCase().trim();

		url = requstInfo.substring(requstInfo.indexOf("/")+1, requstInfo.indexOf("HTTP/")).trim();
		int queryIdx = url.indexOf("?");
		if(queryIdx >= 0) {
			String[] urls = url.split("\\?");
			url = urls[0];
			queryStr = urls[1];

		}
	if(method.equals("post")) {
		String qStr = requstInfo.substring(requstInfo.lastIndexOf(CRLF)).trim();
		queryStr = qStr;
	}
	System.out.println("method = "+method); 
	System.out.println("url = "+url );
	System.out.println("queryStr = "+queryStr );
	covertMap();    
}
	
	//将请求参数转Map
	private void covertMap() {
		String[] keyValue = this.queryStr.split("&");
		for(String query : keyValue) {
			String[] kv = query.split("=");  
			kv = Arrays.copyOf(kv, 2);
			String key = kv[0].trim();
			System.out.println(kv[0]);
			String value = kv[1]==null?null:decode(kv[1],"UTF-8");
			if(!parameterMaP.containsKey(key)) {
				parameterMaP.put(key, new ArrayList<String>());
			}
			
				parameterMaP.get(key).add(value);
			
		}
		
	}
	
	private String decode(String value , String enc) {
		try {
			return  java.net.URLDecoder.decode(value, enc);
		} catch (UnsupportedEncodingException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		return null;
	}
	
	
	/**
	 * 通过name  获取对应的多个值 
	 * @param key
	 * @return
	 */
	public List<String> getParameterValues(String key) {
		List<String> list = this.parameterMaP.get(key);
		if(null==list || list.size()<1) {
			return null;
		}
		return list;
	}
	

	public String getMethod() {
		return method;
	}

 
	public String getUrl() {
		return url;
	}


	public String getQueryStr() {
		return queryStr;
	}


	
	
}

package edu.hue.jk.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Date;

import javax.lang.model.element.Element;

public class Response {

	private Socket client;
	private BufferedWriter bw;
	private static final String BLANK = " ";
	private static final String CRLF = "\r\n";
	//正文
	private StringBuilder content = new StringBuilder();
	//协议头信息
	private StringBuilder headInfo;
	private int len ;//正文的字节数

	public Response(Socket client) {
		this.client = client;
		content = new StringBuilder();
		headInfo = new StringBuilder();
		len = 0;
		try {
			bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
		} catch (IOException e) { 
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
	}
	public Response(OutputStream os) {
		bw = new BufferedWriter(new OutputStreamWriter(os));
}
	//动态添加内容
	public Response print(String info) {
		content.append(info);
		len += info.toString().getBytes().length;
		return this;
	}
	public Response println(String info) {
		content.append(info).append(CRLF);
		len += (info+CRLF).toString().getBytes().length;
		return this;
	}
	
	
	//推送响应信息
	public void pushToBrowser(int code) {
		createHeadInfo(code);
		try {
			bw.append(headInfo);
			bw.append(content);
			bw.flush();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	
	//构建头信息
	private void createHeadInfo(int code) {
		headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
		switch (code) {
		case 200:
			headInfo.append("OK").append(CRLF);
			break;
		case 404:
			headInfo.append("NOT FOUND").append(CRLF);
			break;
		case 505:
			headInfo.append("SERVER ERROR").append(CRLF);
			break;
		}
		
		//2.响应头 
		headInfo.append("Date:").append(new Date()).append(CRLF);
		headInfo.append("Server:").append("zkc Server/0.0.1;charset=GBK").append(CRLF);
		headInfo.append("Content-type:text/html").append(CRLF);
		headInfo.append("Content-length:").append(len).append(CRLF);
		headInfo.append(CRLF);
	}
	
	
}

package edu.hue.jk.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;

public class Server {
	private static final String BLANK = " ";
	private static final String CRLF = "\r\n";
	private ServerSocket serverSocket ;
	private static Socket client;
	private static boolean isRunning = true;
	public static void main(String[] args) throws Exception {
		
		Server server01 = new Server();
		server01.start();
		while (isRunning) {
		server01.receiver();
		new Thread(new Dispachar(client)).start();;
		}
	}
	 //启动服务
public void start() {
	try {
		serverSocket = new ServerSocket(8888);
	} catch (IOException e) {
		// TODO 自动生成的 catch 块
		e.printStackTrace();
		System.out.println("启动失败");
	}
}
//接收连接
public void receiver() {
	try {
		client = serverSocket.accept();
		System.out.println("一个client 建立了连接");
		isRunning = true;
	} catch (IOException e) {
		// TODO 自动生成的 catch 块
		e.printStackTrace();
	}
}
//停止服务
public void stop() {
	isRunning = false;
}
}

在这里插入图片描述
在这里插入图片描述
源码已存网盘!
ps:其中还有404页面error未处理!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值