Web服务器底层解析(浅谈)

基本理念:1.获取请求协议。2.返回响应协议。3.封装响应信息。
4.封装请求信息。5.处理请求参数。6.建立Servlet。7.整合配置文件。8.封装多线程。9.处理错误内容和首页。

在这里插入图片描述

1.获取请求信息,并且封装
public class Request {
private final String CRLF="\r\n";
//协议信息
private String s;
//协议方式
private String method;
//请求URL
private String url;
//请求参数
private String queryStr;
//存储参数
private Map<String ,List<String>> p;

private  Request(InputStream is) {
	p=new HashMap<>();
	
	byte[] data=new byte[1024*1024];
	int len;
	try {
		len = is.read(data);
		this.s=new String(data,0,len);
		} catch (IOException e) {
		e.printStackTrace();
	}	
	parseRequestInfo();
}
//获取服务端传入的输入流
public Request(Socket client) throws IOException {
	this(client.getInputStream());
}
//分析请求信息
private void parseRequestInfo() {
	System.out.println("---------");
	method=s.substring(0,s.indexOf("/")).toLowerCase();
	method.trim();
	int startIdx=s.indexOf("/")+1;
	int endIdx=s.indexOf("HTTP/");
	url=s.substring(startIdx,endIdx).trim();
	System.out.println(url);
	int queryidx=url.indexOf("?");
	if(queryidx>=0) {
		String[] urlarray=url.split("//?");
		url=urlarray[0];
		queryStr=urlarray[1];
	}
	if(method.equals("post")) {
		String sqtr=s.substring(s.lastIndexOf(CRLF)).trim();
		if(queryStr==null) {
			queryStr=sqtr;
		}
		else {
			queryStr+="&"+sqtr;
		}
	}
	queryStr=null==queryStr?"":queryStr;
	convertMap();
}
//将不同的请求信息分割,存入map中
private void convertMap() {
	//按&分割
	String[] keyValues=queryStr.split("&");
	//按=分割
	for(String bat:keyValues) {
		String[] kv=bat.split("=");
		kv=Arrays.copyOf(kv, 2);
		//获取字符串
		String key=kv[0];
		String value=kv[1]==null?null:decode(kv[1]);
		//存储到MAP中
		if(p.containsKey(key)) {
			//第一次
			p.put(key,new ArrayList<String>());
		}
		p.get(key).add(value);
	}
}
//处理请求参数中中文问题
private String decode(String value) {
	try {
		return java.net.URLDecoder.decode(value,"utf-8");
	} catch (UnsupportedEncodingException e) {
		e.printStackTrace();
	}
	return null;
}
//通过Name获取多个值
public String[] getPValues(String key) {
	List<String> list=p.get(key);
	if(list==null || list.size()<1)
	{
		return null;
	}
	return list.toArray(new String[0]);
}
public String getPValue(String key) {
	String[] value=getPValues(key);
	return value==null?null:value[0];
}
public String geturl() {
	return url;
}
public String getmethod() {
	return method;
}
public String queryStr() {
	return queryStr;
}
}
封装返回的响应信息
public class Response {
private final String BLANK=" ";
private final String CRLF="\r\n";
private BufferedWriter bw;
//正文
private StringBuilder content;
//协议头信息(状态行与请求头 回车)
private StringBuilder head;
//长度
private int len=0;
private Response() {
	content=new StringBuilder();
	head=new StringBuilder();
}
//根据服务端创建输出流
public Response(Socket client) {
	this();
	try {
		bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
	} catch (IOException e) {
		e.printStackTrace();
	};
}
public Response(OutputStream os) {
	this();
	bw=new BufferedWriter(new OutputStreamWriter(os));
}
//构建响应头信息
private void createHeadInfo(int code) {
	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;
	}
	head.append("Date:").append(new Date()).append(CRLF);
	head.append("Server:").append("shsxt 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);
}
//动态添加内容
public Response print(String info) {
	content.append(info);
	len+=info.getBytes().length;
	return this;
}
public Response println(String info) {
	content.append(info).append(CRLF);
	len+=(CRLF+info).getBytes().length;
	return this;
}
//将回应信息推回浏览器

public void pushToBrowser(int code) {
	if(null==head)
	{
		return ;
	}
	createHeadInfo(code);
	try {
		bw.write(head.toString());
		bw.write(content.toString());
		bw.flush();
	} catch (IOException e) {
		e.printStackTrace();
	}
}
}
根据传入的请求信息,整合xml文件反射出要加载的类(Web.xml文件根据自身需求编写)
public class WebApp {
	//	//根据对应的请求参数封装web.xml里的资源信息
	private static WebContext context;
	static {
	//获取SAX解析
	//1.获取解析工厂
	SAXParserFactory factory=SAXParserFactory.newInstance();
	//2.从解析工厂获取解析器
	SAXParser parse;
	try {
		parse = factory.newSAXParser();
	//3.编写处理器(自己的)
	//4.加载文档Document注册处理器
	WebHandler handler=new WebHandler();
	//获取当前线程的类加载器
	parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("Servers/web.xml"), handler);
	context=new WebContext(handler.getEntitys(),handler.getMappings());
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
public static Servlet getServletFromUrl(String url) throws Exception, {
	String name=context.getClz("/"+url);
	Class clz=Class.forName(name);
	//Servlet是小程序(接口),用它扩展其他的类,这由自己编写不再给出
	Servlet servlet=(Servlet)clz.getConstructor().newInstance();
	System.out.println(servlet);
	return servlet;
	}
}
自己的处理器
public class WebHandler extends DefaultHandler {
private List<Entity> entitys;
private List<Mapping> mappings;
private Entity entity;
private Mapping mapping;
private boolean isMapping=false;
String tag;

	//加载文档 只需要加载一次
public void startDocument() {
	entitys =new ArrayList<>();
	mappings =new ArrayList<>();
	}
	//Element需要多次的加载
	@Override
	public void startElement(String uri,String localName,String qName,Attributes attributes) {
		if(qName!=null) {
			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) {
		String contents=new String(ch,start,length).trim();
		if(tag!=null) {
			if(isMapping) {
				if(tag.equals("servlet-name")) {
					mapping.setName(contents);
				}
				else if(tag.equals("url-pattern")) {
					mapping.addPatterns(contents);
				}
			}
			else {
				if(tag.equals("servlet-name")) {
					entity.setName(contents);
				}
				else if(tag.equals("servlet-class")) {
					entity.setClz(contents);
				}
			}
		}
	}
	@Override
	public void endElement(String uri,String localName,String qName) {
	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 void setEntitys(List<Entity> entitys) {
		this.entitys = entitys;
	}
	public List<Mapping> getMappings() {
		return mappings;
	}
	public void setMappings(List<Mapping> mappings) {
		this.mappings = mappings;
	}
	}
//根据对应的请求参数封装web.xml里的资源信息
public class WebContext {
//Entity\Mapping是为web.xml里文件写的类
private List<Entity> entitys;
private List<Mapping> mappings;

private Map<String,String>entityMap=new HashMap<>();
private Map<String,String>mappingMap=new HashMap<>();

public List<Entity> getEntitys() {
	return entitys;
}
public void setEntitys(List<Entity> entitys) {
	this.entitys = entitys;
}
public List<Mapping> getMappings() {
	return mappings;
}
public void setMappings(List<Mapping> mappings) {
	this.mappings = mappings;
}
public WebContext(List<Entity> entitys,List<Mapping> mappings) {
	this.entitys=entitys;
	this.mappings=mappings;
	for(Entity entity:this.entitys) {
		entityMap.put(entity.getName(), entity.getClz());
	}
	
	for(Mapping mapping:this.mappings) {
		for(String pattern:mapping.getPatterns()) {
			mappingMap.put(pattern, mapping.getName());
		}
	}
}
public String getClz(String pattern) {
	String name=mappingMap.get(pattern);
	return entityMap.get(name);
}
}
连接用多线程封装,可以同时处理多个请求
public class Dispatcher implements Runnable {
private Socket client;
Request rq = null;
Response rs = null;
public Dispatcher(Socket client) {
	this.client=client;
	
	try {
		rs =new Response(client);
		rq = new Request(client);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
		this.release();
	}
}
@Override
public void run() {
	Servlet servlet = null;
	try {
		//首页
		if(rq.geturl()==null || rq.geturl().equals("")) {
			rs.pushToBrowser(200);
			InputStream is=new FileInputStream(new File("index.txt"));
			rs.print((new String(is.readAllBytes())));
			rs.pushToBrowser(404);
			return ;
		}
		servlet = WebApp.getServletFromUrl(rq.geturl());
		if(servlet==null) {
			InputStream is=new FileInputStream(new File("404.txt"));
			rs.print((new String(is.readAllBytes())));
			rs.pushToBrowser(404);
		}
		else {
			servlet.service(rq, rs);
			rs.pushToBrowser(200);
		}
	}
	catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
		rs.pushToBrowser(500);
	}
	release();
}
private void release() {
	try {
		client.close();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}
服务器
public class Server01 {
ServerSocket ss;
private boolean isRunning;

public static void main(String[] args) {
	Server01 ia=new Server01();
	ia.start();
}
public void start() {
	try {
		ss=new ServerSocket(8888);
		receive();
		isRunning=true;
	} catch (IOException e) {
		e.printStackTrace();
		System.out.println("启动失败");
	}
}
public  void receive()  {
	try {
		while(isRunning) {
			Socket client=ss.accept();
			System.out.println("一个客户端建立了连接");
			new Thread(new Dispatcher(client)).start();
		}
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
		System.out.println("服务器启动异常");
		stop();
	}
}
public void stop() {
	isRunning=false;
	try {
		ss.close();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}

本文章中Servlet,web.xml,html文件等等没给出,由大佬们自己编写,写的不够完整,还有很多细节处理,如:空等问题需要改正,讲的思想大致没错。
学习web底层可以更好应用服务器,框架。谢谢大家

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值