手写服务器代码

手写服务器步骤
1.获取请求协议
1、创建ServerSocket
2、建立连接获取Socket
3、通过输入流获取请求协议
注意: GET与POST不一致的地方
2.返回响应协议
1、准备内容
2、获取字节数的长度
3、拼接响应协议
注意: 空格与换行
4、使用输出流输出
3.封装响应信息
Response
1、动态添加内容print
2、累加字节数的长度
3、根据状态码拼接响应头协议
4、根据状态码统一推送出去
调用处: 动态调用print +传入状态码推送
4.封装请求信息
Request
通过分解字符串获取method URL和请求参
数 POST请求参数可能在 请求体中还存在
5.处理请求参数
Request
通过Map封装请求参数 两个方法
考虑一个参数多个值和中文
6.引入Servlet (小脚本)
Sevlet
将业务代码解耦到对应的业务类中(具体的
Serlvet)
7.整合配置文件
整合配置文件
根据配置文件动态的读取类名,再进行反
射获取具体的Servlet来处理业务,真正的
以不变应万变
8.封装分发器
Dispatcher
加入了多线程,可以同时处理多个请求,
使用的是短连接
9.404以及首页处理
读取错误、首页内容即可

具体代码:

package com.pzy.server.core;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**

  • 处理404以及505
  • @author H

*/
public class Server {
private ServerSocket serversocket;
private boolean isRunning;
public static void main(String[] args) {
Server server = new Server();
server.start();
server.receive();
}
public void start(){
try {
serversocket = new ServerSocket(8870);
isRunning = true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(“服务器启动失败?”);
this.stop();
}
}

public void receive(){
	while(isRunning){
		try {
			Socket socket = serversocket.accept();
			Dispatcher dis = new Dispatcher(socket);
			new Thread(dis).start();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("接收失败");
		}
	}


}
public void stop(){
	isRunning = false;
	try {
		this.serversocket.close();
		System.out.println("服务器已停止");
	} catch (IOException e) {
		e.printStackTrace();
	}
}

}

请求协议
package com.pzy.server.core;

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;

/**

  • 请求协议
  • @author H

/
public class Request {
//客户端发送过来的请求信息
private String requestInfo;
//请求方式
private String meathod;
//请求参数
private String queryStr;
//url
private String url;
//一个存放参数的map容器
private Map<String,List> queryStrMap = new HashMap<String,List>();
public Request(Socket client) throws IOException{
this(client.getInputStream());
}
public Request(InputStream is){
byte datas [] = new byte[1024
1024*1024];
int len;
try {
len= is.read(datas);
if(len>0){
requestInfo = new String(datas,0,len);
}else{

		}
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	paresRequestInfo();
}

private void paresRequestInfo(){//分解字符串
	System.out.println("-------分解-----");

// System.out.println(requestInfo);
int index = requestInfo.indexOf("/");
int endIndex = requestInfo.indexOf(“HTTP/”);
//1.获取请求方式
meathod = requestInfo.substring(0,index).toLowerCase().trim();//从0开始到第一个/
//2.获取url
this.url = requestInfo.substring(index+1,endIndex).trim();

	index = url.indexOf("?");
	if(index>=0){//表示存在?需要进行切割
		String urlsp[] = url.split("\\?");
		url = urlsp[0];
		queryStr = urlsp[1];
	}
	
	//如果请求方式是get则请求参数已经全部获取 而如果请求方式是post 则在请求内容的最后一行 最后一行有回车 可以使用lastindex获得最后一个回车的位置
	if(meathod.equals("post")){
		String qStr= requestInfo.substring(requestInfo.lastIndexOf("\r\n")).trim();
		
		if(queryStr == null){//如果请求参数等于空 及index中没有? 所以直接使用qstr为他赋值
			queryStr = qStr;
		}else{//否则就对他进行字符串的连接
			if(!qStr.equals("")){
				queryStr += "&"+qStr;
			}
		}
	}
	queryStr = queryStr==null?"":queryStr;
	 
	System.out.println(url+"-->"+meathod+"-->"+queryStr);
	
	this.getMap();
}

private void getMap(){//将参数值存放到map中
	//存放参数值 如: aaa=14&bbb=oo&sad=&aaa=pp;
	//1.首先通过&将queryStr分割开来
	String queryStrs[] = queryStr.split("&");
	for(String qStr : queryStrs){
		//2.然后通过=分割
		String kv[] = qStr.split("=");
		kv = Arrays.copyOf(kv, 2);
		String key = kv[0];
		String value = kv[1]==null?null:this.decoing(kv[1], "utf-8");
		if(!queryStrMap.containsKey(key)){
			queryStrMap.put(key, new ArrayList<String>());
		}
		queryStrMap.get(key).add(value);
	}
}

public String[] getValues(String key){
	List<String> values = queryStrMap.get(key);
	if(values==null||values.size()<=0){ 
		return null;
	}
	return values.toArray(new String[0]);
	
}
public String getValue(String key){
	String s[] = getValues(key);
	return s == null?null:s[0];
	
}
public String getMeathod() {
	return meathod;
}
public String getQueryStr() {
	return queryStr;
}
public String getUrl() {
	return url;
}

private String decoing(String value,String enc){
	try {
		return java.net.URLDecoder.decode(value,enc);
	} catch (UnsupportedEncodingException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return null;
}

}

package com.pzy.server.core;
/**

  • 响应协议
    */
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.util.Date;

public class Response {
private BufferedWriter bw;
//正文
StringBuilder content;
//头信息
StringBuilder headInfo;
//字节数组长度
private int len;
//空格和回车
private String blank = " ";
private String CRLF = “\r\n”;

public Response(){//无参构造 初始化参数
	content = new StringBuilder();
	headInfo = new StringBuilder();
	len = 0;
}
public Response(Socket client){//有参构造 传入套接字获得输出流
	this();
	try {
		bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
public Response(OutputStream os){//有参构造 传入输出流
	this();
	bw = new BufferedWriter(new OutputStreamWriter(os));
}

public Response println(String msg){//一个直接添加正文的方法
	len += (msg+CRLF).getBytes().length;
	content.append(msg).append(CRLF);
	return this;
}
public Response print(String msg){
	len += msg.getBytes().length;
	content.append(msg);
	return this;
}

public void pushToBrowse(int code) throws IOException{//发送方法 并调用响应头信息 将响应头状态传入
	if(headInfo==null){
		code = 505;
	}
	headInfo(code);
	bw.append(headInfo.toString());
	bw.append(content.toString());
	bw.flush();
}

private void headInfo(int code){
	//拼接响应协议
	//返回响应协议 响应状态栏 HTTP/1.1 200 OK
	headInfo.append("HTTP/1.1").append(blank);
	headInfo.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;
	}
	
	//返回响应头 :
	/*
	 * Date:Mon,31Dec209904:25:57GMT
		Server:shsxt Server/0.0.1;charset=GBK
		Content-type:text/html
		Content-lenqth:39725426
	 */
	headInfo.append("Date:").append(new Date()).append(CRLF);
	headInfo.append("Server:").append("shsxt Server/0.0.1;charset=GBK").append(CRLF);
	headInfo.append("Content-type:").append("text/html").append(CRLF);
	headInfo.append("Content-length:").append(len).append(CRLF);
	headInfo.append(CRLF);
	headInfo.append(content.toString());//拼接正文
}

}

package com.pzy.server.core;

public interface Servlet {
void service(Request request,Response response);
}

package com.pzy.server.core;

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

public class WebApp {
private static WebContext context;
static{
try{
//SAX解析
//1.获取sax工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//2.获取sax解析器
SAXParser parser = factory.newSAXParser();
//3.加载文档document 注册处理器
WebHanlder hanlder = new WebHanlder();
//Thread.currentThread().getContextClassLoader()从当前线程的类加载器中获取
parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream(“web.xml”)
,hanlder);
context = new WebContext(hanlder.getEntitys(),hanlder.getMappings());
}catch(Exception e){
System.out.println(“解析配置文件错误”);
}
}

/**
 * 通过url获取对应文件的Servlet
 * @param url
 * @return
 */
public static Servlet getServlet(String url){
	String name = context.getClz("/"+url);
	if(name==null){
		return null;
	}
	Class clz;
	try {
		clz = Class.forName(name);
		Servlet ser = (Servlet) clz.newInstance();
		return ser;
	} catch (ClassNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (InstantiationException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return null;
}

}

package com.pzy.server.core;

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

public class WebContext {
private List entitys = null;
private List mappings = null;
//key – > servlet-name value – > servlet-class
private Map<String,String> entityMap = new HashMap<String,String>();

//key -- > pattren  value -- > servlet-name
private Map<String,String> mappingMap = new HashMap<String,String>();
public WebContext(List<Entity> entitys, List<Mapping> mappings) {
	this.entitys = entitys;
	this.mappings = mappings;

	for(Entity entity : entitys){
		entityMap.put(entity.getName(), entity.getClz());
	}
	for(Mapping mapping : mappings){

		Set<String> pattrens = mapping.getPatterns();
		for(String pattren : pattrens){
			mappingMap.put(pattren, mapping.getName());
		}
	}
}

public String getClz(String pattern){
	String name = entityMap.get(mappingMap.get(pattern));
	return name==null?null:name;
}

}

package com.pzy.server.core;

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

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

public class WebHanlder extends DefaultHandler{
private List entitys;
private List mappings;
private Entity entity;
private Mapping mapping;
private String tag;
private boolean isMapping = false;

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
	// TODO Auto-generated method stub
	String msg = new String(ch,start,length).trim();
	
	if(isMapping){
		if(msg.length()>0){
			if(tag.equals("servlet-name")){
				mapping.setName(msg);
			}else if(tag.equals("url-pattern")){
				mapping.addPattern(msg);
			}
		}
	}else{
		if(msg.length()>0){
			if(tag.equals("servlet-name")){
				entity.setName(msg);
			}else if(tag.equals("servlet-class")){
				entity.setClz(msg);
			}
		}
	}
	
	
}
@Override
public void startDocument() throws SAXException {
	// TODO Auto-generated method stub
	entitys = new ArrayList<Entity>();
	mappings = new ArrayList<Mapping>();
	
}

@Override
public void endDocument() throws SAXException {
	// TODO Auto-generated method stub
	
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
	// TODO Auto-generated method stub
	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;
		}
	}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
	// TODO Auto-generated method stub
	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;
}

}

package com.pzy.server.core;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/**

  • 加入对404以及505的分发处理
  • @author H

*/
public class Dispatcher implements Runnable{
private Socket socket;
private Request request;
private Response response;
public Dispatcher(Socket socket){
this.socket = socket;
try {
request = new Request(socket);
response = new Response(socket);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
this.releals();
}

}
@Override
public void run() {
	
	//准备正文
	try {
		if(request.getUrl()==null||request.getUrl().equals("")){
			
			response.print(this.getHtml("index.html"));
			response.pushToBrowse(200);
			
			return ;
		}
		
		Servlet servlet = WebApp.getServlet(request.getUrl());//使用servlet将request和response参数传入进去 使得可以获得不同的页面效果 增加了耦合性
		if(servlet!=null){
			servlet.service(request, response);
			response.pushToBrowse(200);
		}else{
			String data = this.getHtml("Error.html");
			response.print(data);
			response.pushToBrowse(404);
		}
	} catch (IOException e) {
		try {
			response.print("服务器正在疯狂修理中..."); 
			response.pushToBrowse(505);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	this.releals();
}

private String getHtml(String path){
	InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
	byte base[] = new byte[1024*2];
	int len;
	try {
		len = is.read(base);
		String data = new String(base,0,len);
		is.close();
		return data;
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return null;
}

private void releals(){
	try {
		socket.close();
	} catch (IOException e1) {
		// TODO Auto-generated catch block
		e1.printStackTrace();
	}
}

}

package com.pzy.server.core;
/**

  • login com.sxt.server.basic.servlet.LoginServlet
  • @author H

*/
public class Entity {
private String name;
private String clz;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClz() {
return clz;
}
public void setClz(String clz) {
this.clz = clz;
}
public Entity(String name, String clz) {
this.name = name;
this.clz = clz;
}
public Entity() {
}

}

package com.pzy.server.core;

import java.util.HashSet;
import java.util.Set;
/**

  • reg /reg
  • @author H

*/
public class Mapping {
private String name;
private Set patterns;
public Mapping(String name, Set patterns) {
this.name = name;
this.patterns = patterns;
}
public Mapping() {
patterns = new HashSet();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getPatterns() {
return patterns;
}
public void setPatterns(Set patterns) {
this.patterns = patterns;
}

public void addPattern(String pattern){
	this.patterns.add(pattern);
}

}

package com.pzy.user;

import com.pzy.server.core.Request;
import com.pzy.server.core.Response;
import com.pzy.server.core.Servlet;

public class LoginServlet implements Servlet {

@Override
public void service(Request request,Response response) {
	response.print("<html>");
	response.print("<head>");
	response.print("<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">");
	response.print("<title>");
	
	response.print("LoginServlet");
	
	response.print("</title>");
	response.print("</head>");
	response.print("<body>");
	//内容
	response.print("我LoginServlet响应啦~~");
	response.print("欢迎你:"+request.getValue("uname")+"~~");
	response.print("</body>");
	response.print("</html>");
}

}

package com.pzy.user;

import com.pzy.server.core.Request;
import com.pzy.server.core.Response;
import com.pzy.server.core.Servlet;

public class OtherServlet implements Servlet {

@Override
public void service(Request request, Response response) {
	response.println("其他页面");
	response.println("heihei");
	response.println("other");
}

}

package com.pzy.user;

import com.pzy.server.core.Request;
import com.pzy.server.core.Response;
import com.pzy.server.core.Servlet;

public class RegisterServlet implements Servlet {

@Override
public void service(Request request,Response response) {
	response.print("<html>");
	response.print("<head>");
	response.print("<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">");
	response.print("<title>");
	
	response.print("第一个网页响应协议");
	
	response.print("</title>");
	response.print("</head>");
	response.print("<body>");
	//内容
	response.print("我响应啦~~");
	response.print("</body>");
	response.print("</html>");
}

}

<html>
	<head>
		<meta http-equiv="content-type" content="text/html;charset=utf-8">
		<title>404页面</title>
	</head>
	<body>
		<h1>你懂的</h1>
	</body>
</html>


<html>
	<head>
		<meta http-equiv="content-type" content="text/html;charset=utf-8">
		<title>首页</title>
	</head>
	<body>
		<h1>欢迎使用我的首页哦</h1>
	</body>
</html>


<html>
	<head>
		<title>登录页面</title>
	</head>
	<body>
		<h1>你好啊</h1>		
		<form method="get" action="http://localhost:8870/login">
						用户名:<input type="text" name="uname"  id="uname"/>
						密码:<input type="password" name="pwd"  id="pwd"/>
						<input type="submit" value="登录"/>
		</form>
	</body>
</html>
<?xml version="1.0" encoding="UTF-8"?> login com.pzy.user.LoginServlet login /login /g
<servlet-mapping>
	<servlet-name>reg</servlet-name>
	<url-pattern>/reg</url-pattern>
	<url-pattern>/hahah</url-pattern>
</servlet-mapping>
<servlet>
	<servlet-name>reg</servlet-name>
	<servlet-class>com.pzy.user.RegisterServlet
	</servlet-class>
</servlet>

<servlet-mapping>
	<servlet-name>999</servlet-name>
	<url-pattern>/o</url-pattern>
	<url-pattern>/other</url-pattern>
</servlet-mapping>
<servlet>
	<servlet-name>999</servlet-name>
	<servlet-class>com.pzy.user.OtherServlet
	</servlet-class>
</servlet>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值