手写WebServer



前言

java高淇300集的251 - 269 知识整理

反射

反射:把java类中的各种结构(方法,属性,构造器,类名)映射成一个个的java对象
反射是框架设计的灵魂

获取class对象 三种方式
//三种方式获取class对象

	//1 getClass
	Iphone iphone = new Iphone();
	Class clz = iphone.getClass();
	//2类.class()
	clz = Iphone.class;
	//3.Class.forName("包名.类名");
	clz = Class.forName("basic.Iphone");
	推荐第三种 因为第三个传字符串即可,不需要建立一个实类然后才建立Class

可以动态创建对象

获取到Class对象后可以创建:
Iphone iphone2 = (Iphone)clz.getConstructor().newInstance();

XML解析

推荐SAX解析,流解析

解析步骤:

public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
		//SAX解析
			//1、获取解析工厂
			SAXParserFactory factory=SAXParserFactory.newInstance();
			//2、从解析工厂获取解析器
			SAXParser parse =factory.newSAXParser();
			//3、编写处理器   对PersonHandler进行对接DefalutHandler接口
			//4、加载文档 Document 注册处理器
			PersonHandler handler=new PersonHandler();
			//5、解析
			parse.parse(Thread.currentThread().getContextClassLoader()
			.getResourceAsStream("basic/p.xml")
			,handler);
		}
	}
    class PersonHandler extends DefaultHandler{
    	@Override
    	public void startDocument() throws SAXException {
    		System.out.println("解析文档开始");
    	}
    	@Override
    	public void startElement(String uri, String localName, String qName, Attributes attributes)
    			throws SAXException {
    		System.out.println(qName+"解析标签开始");
    	}
    	public void characters(char[] ch, int start, int length) throws SAXException {
    	   String contens = new String (ch,start,length).trim();
    	   if(contens.length()>0)
    		System.out.println("内容为:"+contens);
    	   else
    		   System.out.println("空");
    	}
    	@Override
    	public void endElement(String uri, String localName, String qName) throws SAXException {
    		System.out.println(qName+"解析结束");
    	}
    	@Override
    	public void endDocument() throws SAXException {
    		System.out.println("解析文档结束");
    		super.endDocument();
    	}
}

得到解析出来的数据后:

数据处理

在处理器类中加入容器,对象,和标识
private List<Person> persons;
    	private Person person;
    	private String tag; 存储操作标签

在每一次解析开始时建立容器对象
public void startDocument() throws SAXException {
    		persons = new ArrayList<Person>();
    	}
    	
每一次读取标签时判断是不是person对象,是则创建一个新的对象
public void startElement(String uri, String localName, String qName, Attributes attributes)
    			throws SAXException {
    		tag = qName;  存储标签名
    		if(tag.equals("person"))  如果是对象则建立新的存入容器中
    			person = new Person();
    			}
对内容的标识也要判断,
如果是属性名,则对当前的person对象的属性赋值,如果当前的标识是空的,则不赋值
	内容识别标签储存对应的属性值
    	public void characters(char[] ch, int start, int length) throws SAXException {
    	   String contents = new String (ch,start,length).trim();
    	   if(null!=tag) {  空指针就不进行赋值
    	   if(tag.equals("name")) {
    	   person.setName(contents);
    	   }
    	   else if(tag.equals("age")) {
    		   if(contents.length()>0)
    		   person.setAge(Integer.valueOf(contents));
    	   }
    	   }
    	}
为了防止结束语后的空字符串影响赋值,则每次结束时对当前的tag制空。而且如果是对象名,则把对象存入容器中。
   	public void endElement(String uri, String localName, String qName) throws SAXException {
    		if(qName!=null) {
    		if(qName.equals("person"))
    			persons.add(person);
    		}
    		tag = null;  丢弃掉,防止对空字符串覆盖在属性上
    		}

较为复杂的XML解析

XML源码:

<?xml version="1.0" encoding="UTF-8"?>  
 <web-app>
 <servlet>
  <servlet-name>login</servlet-name>
  <servlet-class>servlet.LoginServlet</servlet-class>
 </servlet>
   <servlet>
  <servlet-name>reg</servlet-name>
  <servlet-class>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>

像这种有两个类对象的,则需要建立两个容器,在解析的时候对标签名进行判断,建立对应的对象存入对应的属性值。

下面是处理器的代码逻辑,可以利用内置的get方法对存好的容器进行调用方便后面解析数据:

 class WebHandler extends DefaultHandler{
    	private List<Entity> entitys;
    	private List<Mapping> mappings;
    	private Entity entity;
    	private Mapping mapping;
    	private String tag; //存储操作标签
    	private boolean isMapping  = true;
    	@Override
    	public void startDocument() throws SAXException {
    		entitys = new ArrayList<Entity>();
    		mappings = new ArrayList<Mapping>();
    	}
    	@Override
    	public void startElement(String uri, String localName, String qName, Attributes attributes)
    			throws SAXException {
    		tag = qName;  //存储标签名
    		
    		if(tag.equals("servlet"))  //如果是对象则建立新的存入容器中
    		{	entity = new Entity();
    			isMapping = false;
    		}
    		else if(tag.equals("servlet-mapping")) {  
    			mapping = new Mapping();
    			isMapping = true;
    		}
    	}
    	//内容识别标签储存对应的属性值
    	public void characters(char[] ch, int start, int length) throws SAXException {
    	   String contents = new String (ch,start,length).trim();
    	   if(null!=tag) {  //空指针就不进行赋值
    		   
    		   if(isMapping) { //操作servlet-mapping
    			   
    			   if(tag.equals("servlet-name")) {
    				   mapping.setName(contents);
    		    	   }
    		    	   else if(tag.equals("url-pattern")) {
    		    			   mapping.addPattern(contents);
    		    	   }
    			   
    		   }else {     //操作serlet
    			    
    			   if(tag.equals("servlet-name")) {
    	   entity.setName(contents);
    	   }
    	   else if(tag.equals("servlet-class")) {
    		   if(contents.length()>0)
    			   entity.setClz(contents);
    	   }
    		   }
    	  
    	   }
    	}
    	@Override
    	public void endElement(String uri, String localName, String qName) throws SAXException {
    		if(qName!=null) {
    		if(qName.equals("servlet"))
    			entitys.add(entity);
    		
    		else if(qName.equals("servlet-mapping"))
    			mappings.add(mapping);
    		}
    		tag = null;  //丢弃掉,防止对空字符串覆盖在属性上
    		
    }
    	
    	
    	
		public List<Entity> getEntitys() {
			return entitys;
		}
	
		public List<Mapping> getMappings() {
			return mappings;
		}
}

反射XML文件

解析完成的XML文件,在servlet中,

<?xml version="1.0" encoding="UTF-8"?>  
 <web-app>
 <servlet>
  <servlet-name>login</servlet-name>
  <servlet-class>servlet.LoginServlet</servlet-class>
 </servlet>
   <servlet>
  <servlet-name>reg</servlet-name>
  <servlet-class>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>

一般在URL后面加入/g或则/reg,这些就是为了服务器找到对应的类对象,比如/g可以找到mapping内对应的login,login可以在上面的servlet标签内对应到servlet-class,也就是loginservelt对象。
根据输入不同的值可以找到不同的类对象,就可以调用getclassname()反射出不同的对象。

这里我们新建一个WebContext 类处理保存好的两个容器。
在类内主要逻辑是:
建立两个Map容器,把servelt和mapping这两个类存入map容器中。
servlet的Map容器的 key是servlet-name value是servlet-class
mapping的Map容器的 key是pattern value是servlet-name

然后调用关键函数就可以达到传入pattern,返回对应的类名的功能。

//通过url的路径找到对应的class
	public String getClz(String pattern) {
		return entityMap.get(mappingMap.get(pattern));
	}

WebContents:

package 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;
	// servlet-name-->class路径
	private Map<String, String> entityMap = new HashMap<>();
	// url路径-->servlet-name
	private Map<String, String> mappingMap = new HashMap<>();
	public WebContext(List<Entity> entities ,List<Mapping> mappings) {
		this.entities = entities;
		this.mappings = mappings;
		
		//将entityList转成对应的map
		for(Entity entity : entities) {
			entityMap.put(entity.getName(), entity.getClz());
		}
		
		for(Mapping mapping : mappings) {
			for(String pattern : mapping.getPattern()) {
				mappingMap.put(pattern, mapping.getName());
			}
		}
		
	}
	
	//通过url的路径找到对应的class
	public String getClz(String pattern) {
		return entityMap.get(mappingMap.get(pattern));
	}
}

使用方法:

WebContext webContext = new WebContext(handler.getEntitys(), handler.getMappings());
			
			String classForName = webContext.getClz("/reg");
			Class clazz =  Class.forName(classForName);
			Servlet servlet = (Servlet)clazz.getConstructor().newInstance();
			servlet.service();

HTML入门:

<html>
	<head>
		<title>登录</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>

HTTP协议

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

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

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

RESTer插件:

测试URL功能的POST和GET:
在这里插入图片描述
在这里插入图片描述

获取请求协议

Socket client = server.accept();
			System.out.println("一个客户端建立了连接");
			
			//获取请求协议
			InputStream is = client.getInputStream();
			byte [] datas  = new byte[1024*1024];
			int len = is.read(datas);
			String request = new String (datas,0,len);
			System.out.println(request);

发送响应协议

要按照特定的格式,注意空格和换行,响应头和正文之间有空行

		StringBuilder content = new StringBuilder();
			content.append("<html>");
			content.append("<head>");
			content.append("<title>");
			content.append("服务器响应成功");
			content.append("<title>");
			content.append("</head>");
			content.append("<body>");
			content.append("server终于回来了呢");
			content.append("</body>");
			content.append("<html>");
			
			必须获取字节长度
			int size = content.toString().getBytes().length;
			
			StringBuilder responselInfo = new  StringBuilder();
			String blank = " ";
			String CRLF = "\r\n";
			
			返回
			1.响应的状态行:HTTP/1.1 200 OK
			responselInfo.append("HTTP/1.1").append(blank);
			responselInfo.append(200).append(blank);
			responselInfo.append("OK").append(CRLF);

			2.响应头:(最后一行存在空行)
			responselInfo.append("Data:").append(new Date()).append(CRLF);
			responselInfo.append("Server:").append("YUAN Server/0.0.1;charset=GBK").append(CRLF);
			responselInfo.append("Content-type:text/html").append(CRLF);
			responselInfo.append("Content-length:").append(size).append(CRLF);
			responselInfo.append(CRLF);


			3.正文
			responselInfo.append(content.toString());


			写出到客户端
			BufferedWriter br = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
			br.write(responselInfo.toString());
			br.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("客户端错误");
		}
	}

响应信息封装

封装成response类,只需要关注正文内容和状态码,其他头信息不用修改。

package server;

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

public class Response {
	BufferedWriter bw;
	//正文
	private StringBuilder content;
	//协议头(状态行,请求,回车)信息
	private StringBuilder head;
	private int len;
	String blank = " ";
	String CRLF = "\r\n";
	
	public Response() {
		 content = new StringBuilder();
		 head = new StringBuilder();
		 len = 0;
	}
	public Response(Socket client)  {
		this();
		try {
			bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
		} catch (IOException e) {
			head = null;
		}
	}

	
	//动态添加内容
	public Response print(String info) {
		content.append(info);
		len+=info.getBytes().length;
		return this;
	}
	
	//构建头信息
	private void creatHead(int code) {
		//1.响应的状态行:HTTP/1.1 200 OK
		head.append("HTTP/1.1").append(blank);
		head.append(code).append(blank);
		switch (code) {
		case 200:head.append("OK").append(CRLF);
		         break;
		case 404:head.append("Not Found").append(CRLF);
                 break;
		case 505:head.append("Server Error").append(CRLF);
                 break;
		}
		//2.响应头:(最后一行存在空行)
		head.append("Data:").append(new Date()).append(CRLF);
		head.append("Server:").append("YUAN Server/0.0.1;charset=GBK").append(CRLF);
		head.append("Content-type:text/html").append(CRLF);
		head.append("Content-length:").append(len).append(CRLF);
		head.append(CRLF);
	}
	
	//推送响应 消息,把head和content两个字符集传入输出流内
	public void push(int code) {
		if(null == head)
			code = 505;
		creatHead(code);        //构建头信息,传入head内
		try {
			bw.append(head);     //导入头信息到输出流
			bw.append(content);  //在server类内已经导入数据流入content内,所以直接导入输出流
			bw.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}	
}

响应协议的封装是比较容易的,同样的对获得的请求协议也需要封装,请求协议文件如下:

请求协议封装

请求协议文件如下:

POST /cacc?uname=acjefe HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Content-Length: 12
sec-ch-ua: “Google Chrome”;v=“89”, “Chromium”;v=“89”, “;Not A Brand”;v=“99”
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: /
Origin: chrome-extension://eejfoncpjfgmeleakejdcanedmefagga
Sec-Fetch-Site: none
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

zevfd=vvbtrb

先把上面的字符存入一个字符数组,然后找指定位置切割获取不同的数据:

获取请求方式 开头 到 第一个/"
获取请求url 第一个/ 到 HTTP/
可能包含请求参数 ?后面
如果包含参数则需要对已经获取的字符串进行分割,前半部分是url后半部分是参数

post的参数不止在?后面,在正文的最后一行也有出现,所以需要判断是否为post来确定要不要切割

System.out.println("获取请求方式:开头 到 第一个/");
		this.method = this.request.substring(0,this.request.indexOf("/")).toLowerCase();
		this.method = this.method.trim();
		System.out.println(method);
		System.out.println("获取请求url:第一个/ 到 HTTP/");
		System.out.println("可能包含请求参数?前面的为url");
		//获取第一个/的位置
		int idx1 = this.request.indexOf("/")+1;
		//获取HTTP位置
		int end1 = this.request.indexOf("HTTP/");
		//分割字符串
		this.uri = this.request.substring(idx1,end1);
		System.out.println(this.uri);
		//获取?的位置
		int query = this.uri.indexOf("?");
		if(query>=0) {//存在请求参数
			String[] urlArray = this.uri.split("\\?");
			this.uri = urlArray[0];
			queryStr = urlArray[1].trim();
		}
		System.out.println(this.uri);
		
		System.out.println("获取请求参数:如果是GET已经获取,如果是POST可能在请求体中");
		
		if(method.equals("post")) {
			
			String  qStr = this.request.substring(this.request.lastIndexOf(CRLF)).trim();
			
			if(null==queryStr) {
				queryStr = qStr;
			}else {
				queryStr+="&"+qStr;
			}
		}
		System.out.println(method+"-->"+uri+"-->"+queryStr);
		

获取完数据后,需要处理的是参数,因为有多个参数,所以需要存入Map内,因为可能一个name有多个属性值,所以Map的建立应该是:

	private Map<String,List<String>> parameterMap;

下面在函数内对参数进行存入Map中:

private void convertMap() {
		//分割字符串 按 &
		String[] keyValues = this.queryStr.split("&");
		for(String query:keyValues) {
			//再次分割字符串 按 = 
			String[] kv = query.split("=");
			kv = Arrays.copyOf(kv,2);
			//获取key和value
			String key = kv[0];
			String value =kv[1]=decode(kv[1]);
			//存储到Map中
			if(!parameterMap.containsKey(key)) {  //判断当前的key是否存在,如果不存在就新建一个容器
				parameterMap.put(key, new ArrayList<String>());
			}
			parameterMap.get(key).add(value);    //直接把value放入容器中
			
		}
	}

最后设置关键get函数以便调用此类来直接获取需要的数据:

//通过name获取对应的多个属性值  一对多,需要输出多个值
	public String [] getParameterValues(String key){
		List<String> list = this.parameterMap.get(key);
		if(null==list||list.size()<1) {
			return null;
		}
		return list.toArray(new String[0]);
	}
	
	public String  getParameter(String key){  //处理一对一的值
		String[] values = getParameterValues(key);
		return values == null?null:values[0];
		
	}

请求协议封装源码:

package server;

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

/**
 * 封装请求协议:封装请求参数为Map
 * @author Little Black
 *
 */
public class Request2 {
	//协议信息
	private String request;
	//请求方式
	private String method;
	//请求URI
	private String uri;
	//请求参数
	private String queryStr;
	String CRLF = "\r\n";

	//储存参数
	private Map<String,List<String>> parameterMap;
	
	
	public Request2(Socket client) throws IOException{
		this(client.getInputStream());
	}
	
	public Request2(InputStream is) {
		parameterMap = new HashMap<String, List<String>>();
		byte [] datas  = new byte[1024*1024];
		int len;
		try {
			len = is.read(datas);
			request = new String (datas,0,len);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
		//分解字符串
		parseRequestInfo();
	}
	
	
	
	private void parseRequestInfo() {
		System.out.println("获取请求方式:开头 到 第一个/");
		this.method = this.request.substring(0,this.request.indexOf("/")).toLowerCase();
		this.method = this.method.trim();
		System.out.println(method);
		System.out.println("获取请求url:第一个/ 到 HTTP/");
		System.out.println("可能包含请求参数?前面的为url");
		//获取第一个/的位置
		int idx1 = this.request.indexOf("/")+1;
		//获取HTTP位置
		int end1 = this.request.indexOf("HTTP/");
		//分割字符串
		this.uri = this.request.substring(idx1,end1);
		System.out.println(this.uri);
		//获取?的位置
		int query = this.uri.indexOf("?");
		if(query>=0) {//存在请求参数
			String[] urlArray = this.uri.split("\\?");
			this.uri = urlArray[0];
			queryStr = urlArray[1].trim();
		}
		System.out.println(this.uri);
		
		System.out.println("获取请求参数:如果是GET已经获取,如果是POST可能在请求体中");
		
		if(method.equals("post")) {
			
			String  qStr = this.request.substring(this.request.lastIndexOf(CRLF)).trim();
			
			if(null==queryStr) {
				queryStr = qStr;
			}else {
				queryStr+="&"+qStr;
			}
		}
		System.out.println(method+"-->"+uri+"-->"+queryStr);
		
		//转成Map fav=1&fav=2&uname=shx&age=18&others=
		convertMap();
	}

	private void convertMap() {
		//分割字符串 按 &
		String[] keyValues = this.queryStr.split("&");
		for(String query:keyValues) {
			//再次分割字符串 按 = 
			String[] kv = query.split("=");
			kv = Arrays.copyOf(kv,2);
			//获取key和value
			String key = kv[0];
			String value =kv[1]=decode(kv[1]);
			//存储到Map中
			if(!parameterMap.containsKey(key)) {  //判断当前的key是否存在,如果不存在就新建一个容器
				parameterMap.put(key, new ArrayList<String>());
			}
			parameterMap.get(key).add(value);    //直接把value放入容器中
			
		}
	}
	
	private String decode(String value) { //处理中文
		return java.net.URLDecoder.decode(value);
		
	}
	
	//通过name获取对应的多个属性值  一对多,需要输出多个值
	public String [] getParameterValues(String key){
		List<String> list = this.parameterMap.get(key);
		if(null==list||list.size()<1) {
			return null;
		}
		return list.toArray(new String[0]);
	}
	
	public String  getParameter(String key){  //处理一对一的值
		String[] values = getParameterValues(key);
		return values == null?null:values[0];
		
	}
	
}

结合Servlet

既然可以直接获取请求协议的参数的值,我们就可以通过判断得到的值来执行不同的代码:
先设置Login,Register两个类连接Servlet接口,内部设置不同的html画面:
然后在serlvet6中执行:

if(request.getUri().equals("login")) {
			Servlet servlet = new LoginServlet();
			servlet.service(request, response);
			}
			else if(request.getUri().equals("over")) {
				Servlet servlet = new RegisterServlet();
				servlet.service(request, response);
			}

至此可以通过传入不同的url来跳转到不同的html中
上面对每一个URL选取不同的Servlet是非常麻烦的,在复杂的应用中,就需要利用到servlet脚本代码和反射来起到以不变应万变
先要引入之前解析xml的处理器解析器等类
Entity,Mapping,WebContext
然后建立一个WebApp类,封装调用上面的方法然后getServlet(String Url)来返回需要的Servlet

import java.lang.reflect.InvocationTargetException;

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


public class WebApp {
	static WebContext webContext;
	static {
		try {
			
				//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("web.xml")
					,handler);
					webContext = new WebContext(handler.getEntitys(), handler.getMappings());
		}catch(Exception e) {
			System.out.println("解析错误");
		}
	}
	
	 通过URL获取配置文件对应的class
	public static Servlet getServlet(String url) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
	String classForName = webContext.getClz("/"+url);
	Class clazz;
	try {
		clazz = Class.forName(classForName);
		Servlet servlet = (Servlet)clazz.getConstructor().newInstance();
		return servlet;
	} catch (ClassNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return null;
	}
}

客户端封装成多线程

public class Dispatcher implements Runnable{
	Socket client;
	Request request;
	Response response;
	public Dispatcher(Socket client) {
		this.client = client;
		
				try {
					//获取请求协议
					 request = new Request(client);
					//响应协议    响应状态行 响应头 空格 正文
					 response = new Response(client);		
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					release();
				}
		
	}
	@Override
	public void run() {
		
		//关注内容
		try {
			String s = request.getUri();
			if(s.equals("") || s == null) {
				s = "yuan";
			}
			Servlet servlet = WebApp.getServlet(s);
			if(null!=servlet) {
				servlet.service(request, response);
				//关注状态码
				response.push(200);
			}
			else {
				
				response.print("访问的页面不存在");
				response.push(404);
				
			}
		} catch (Exception e) {
			response.print("no no no");
			response.push(500);
		}
		release();
	}

	//释放资源
	private void release() {
		try {
			client.close();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
}

在Server内就创建多线程

	//接收连接
	public void receive() {
		while(isRunning) {
		try {
			Socket client = server.accept();
			System.out.println("一个客户端建立了连接");
			
			//多线程
			new Thread(new Dispatcher(client)).start();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("客户端错误");
		}
	}

至此底层的框架已经构建好了。
以后主要的开发流程就是:
新建一个html文件,在里面的

		<form method="get" action="http://localhost:8888/reg">
						用户名:<input type="text" name="uname"  id="uname"/>
						你的兴趣爱好:
						<input type = "checkbox" name="like" id="fa" value="0"/>打篮球
						<input type = "checkbox" name="like" id="fa" value="1"/>王者荣耀
						<input type = "checkbox" name="like" id="fa" value="2"/>打飞机
						<input type="submit" value="登录"/>
		</form>

主要的内部运行机制:

通过发送请求协议,利用request对请求协议的文件进行字符串截取,获得url。
String s = request.getUri();

url与内部WebApp等类解析好的配置文件内的URL进行比对寻找内部的servlet-class,通过反射获取到对应的对象。
Servlet servlet = WebApp.getServlet(s);

实现已经设置好的对象的service行为,service内就是对响应协议的正文的内容的添加,添加完后就会导入到封装好的响应协议response内。
servlet.service(request, response);

响应协议写好内容后就push出去,加上状态码,其他的内容已经在response内封装好,遵守响应协议的格式,传给客户端返回内容。
response.push(200);

public void run() {
		
		//关注内容
		try {
			String s = request.getUri();
			if(s.equals("") || s == null) {
				s = "yuan";
			}
			Servlet servlet = WebApp.getServlet(s);
			if(null!=servlet) {
				servlet.service(request, response);
				//关注状态码
				response.push(200);
			}

以后的开发流程:

写一个用户界面的HTML,设置请求协议的URL值,在Web.xml中设置好URL要映射的servlet类名和对应的类位置,如下图。 然后就写类的内容,继承Servlet接口,重写内部方法,可调用request获取用户输入的属性的值。在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值