Servlet原理解析:手写一个web简易服务器(思路及代码实现)(一)

思路

1、创建一个服务器

用ServerSocket绑定一个端口创建服务器

/**
*在本地创建一个服务器
*main函数入口
*/
public class servSocket {
	private ServerSocket serSocket;
	private boolean isRunniang = true;
	public static void main(String[] args) {
		servSocket s = new servSocket();
		s.start();
		s.receive();
	}
	//启动服务器
	public void start(){
		try {
			serSocket = new ServerSocket(8888);
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("服务器启动失败");
		}
		
	}
	
	
    public void receive(){
    	while(isRunniang){
    		try {
    			Socket client = serSocket.accept();
    			System.out.println("客户端建立了链接");
    		} catch (IOException e) {
    			e.printStackTrace();
    			System.out.println("客户端出错");	
    			end();
    		}
    	}	
	}
	//关闭服务器
	public void end(){
		isRunniang = false;
		try {
			serSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("服务器关闭出错");	
		}
	}


}

2、思考如何通信

得到了一个服务器之后,怎么得到浏览器发来的消息呢,又如何给浏览器发送消息呢?
根据网络编程的知识,我们知道,服务器的accept函数会返回一个Socket对象,封装了客户端的所有信息,这样的话我们根据得到的client引用,获得输入流和输出流就可以和浏览器进行消息的传递。

得到浏览器的请求信息,给浏览器发送信息
public void receive(){
    	while(isRunniang){
    		try {
    			Socket client = serSocket.accept();
    			System.out.println("客户端建立了链接");

				//根据client得到输入输出流
				InputStream is = client.getInputStream();//输入流
				OutputStream os = client.getOutStream();//输出流


				//获得浏览器的请求信息
				byte[] datas = new bytes[1024*1024];//为方便起见,直接创建一个足够大的容器来接收请求信息
				int len = is.read(datas);//请求信息的字节长度
				String requestInfo = new String(datas,0,len);//转换成字符串,requestInfo即为浏览器发送的请求信息
				System.out.println(requestInfo);  //打印查看浏览器发送的请求信息


				//给浏览器发送信息,-------->比较复杂,要写响应头和一个html页面
				StringBuffer header = new StringBuffer(); ///构建响应头
				StringBuffer content= new StringBuffer(); ///构建响应体(静态HTML页面)
				
				//响应体
				content.append("<html>");
				content.append("<head>");
				content.append("<title>");
				content.append("构建的响应信息");
				content.append("</title>");
				content.append("</head>");
				content.append("<body>");
				content.append("你好,浏览器");
				content.append("</body>");
				content.append("</html>");
				int size = content.toString().getBytes().length;  //得到字节的长度(content是字符型的,字符长度和字节是不同的,所以不能直接用content.length())

				//响应头
				String BLANK = " ";  //空格
				String CRLF = "\r\n"; //换行
				header.append("HTTP/1.1").append(BLANK);
				header.append(200).append(BLANK);
				header.append("OK").append(CRLF);
				header.append("Date:").append(new Date()).append(CRLF);
				header.append("Server:").append("shsxt Server/0.01;charset=GBK").append(CRLF);
				header.append("Content-type:").append("text/html").append(CRLF);
				header.append("Content-length:").append(size).append(CRLF);
				header.append(CRLF);
				//构成完整的响应协议
				header.append(content);
				//发送给浏览器
				os.write(header.toString().getBytes());
				os.flush();


    		} catch (Exception e) {  //捕捉所有异常
    			e.printStackTrace();
    			System.out.println("客户端出错");	
    			end();
    		}
    	}	
	}

3、封装请求信息和响应信息

从上面的代码我们知道了服务器和浏览器如何进行通信,但是我们可以发现,请求信息和响应信息可以被封装在对象里面,我们通过管理对象就可以管理请求信息和响应信息。

封装Request对象
//封装的request对象

package HttpSever.core;

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

/**
 * 用于封装HttpRrquest请求
 * @author Administrator
 *
 */
public class Request {
    private Socket client;
    private String METHOD;
    private String URL;
    private String query;
    private String requestInfo;
    private InputStream is;
	public Request(){		
	}
	public Request(Socket client){
		this.client = client;
		getRequestInfo();
		parseInfo();
	}
	
	
	
	//分解参数
	private void parseInfo(){
		//获取请求方法
		this.METHOD = this.requestInfo.substring(0,this.requestInfo.indexOf("/")).trim().toLowerCase();
		
		//获取虚拟URL
		//1、url不带参数
		int startIndex = this.requestInfo.indexOf("/")+1;
		int endIndex = this.requestInfo.indexOf("HTTP/");
		this.URL = this.requestInfo.substring(startIndex,endIndex).trim();
		
		//2、url带参数
		if(this.URL.contains("?")){
			String[] urlAndPar = this.URL.split("\\?");
			this.URL = urlAndPar[0].length()==0?null:urlAndPar[0];  //null或有值
			if(urlAndPar.length==1)
			{
				this.query=null;
			}else
			    this.query = urlAndPar[1].length()==0?null:urlAndPar[1]; //null或有值
		}
		
		//post方法中请求体的参数
		if(this.METHOD.equals("post")){
			int idx = this.requestInfo.lastIndexOf("\r\n");
			String postQuery = this.requestInfo.substring(idx).trim().length()==0?null:this.requestInfo.substring(idx).trim();   //null或有值
			if(postQuery!=null){
				if(this.query==null)
					this.query = postQuery;
				else
					this.query+="&"+postQuery;
			}
		
		}
	}
	
	private void getRequestInfo(){
		try {
			is = this.client.getInputStream();
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("reques输入流获取失败。。。。。。");
		}
		byte[] datas = new byte[1024*1024];//为方便起见,直接创建一个够大的数组
		int len =-1;
		try {
			 len = is.read(datas);
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("request请求数据接收异常。。。。。。");
		}
		
		requestInfo = new String(datas,0,len);
	}
	
	public String getMETHOD() {
		return METHOD;
	}
	public String getURL() {
		return URL;
	}
	public String getQuery() {
		return query;
	}
}

封装Response对象
//封装Response



package HttpSever.core;

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

/**
 * 用于封装HttpResponse请求
 * 通过该对象,我们不用再关心响应头,只用关心想要输出的响应体(HTML)和状态码即可
 * @author Administrator
 *
 */
public class Response {
	private int size;
	private StringBuilder header;
	private StringBuilder content;
	private Socket client;
	private OutputStream os;
	private final String BLANK = " ";
	private final String CRLF  = "\r\n";
	public Response(){
		size = 0;
		header = new StringBuilder();
		content = new StringBuilder();
	}
	public Response(Socket client){
		this();
		this.client = client;
		try {
			os = this.client.getOutputStream();
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("response输出了获取失败。。。。。。");
		}
	}
	
	/**
	*该方法用于向响应体中动态添加内容
	*/
	public Response print(String info){
		content.append(info);
		this.size+=info.getBytes().length; 
		return this;
	}
	
	/**
	*该方法用于向浏览器输出封装好的response
	*/
	public void pushToBro(int status){
		createHeader(status);
		header.append(content.toString());
		try {
			os.write(header.toString().getBytes());
			os.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("response输出出错。。。。。。。");
		}
		
	}
	/**
	*私有方法,用来构建头信息
	*/
	private void createHeader(int status){
		header.append("HTTP/1.1").append(BLANK);
		header.append(status).append(BLANK);
		switch(status){
		case 200:header.append("OK").append(CRLF);break;
		case 404:header.append("NOT FOUND").append(CRLF);break;
		case 505:header.append("SERVER ERROR").append(CRLF);break;
		}
		header.append("Date:").append(new Date()).append(CRLF);
		header.append("Server:").append("shsxt Server/0.01;charset=GBK").append(CRLF);
		header.append("Content-type:").append("text/html").append(CRLF);
		header.append("Content-length:").append(size).append(CRLF);
		header.append(CRLF);
	}
	
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值