手写一个服务器,提供静态资源访问

1. 接收客户端的请求解析出它请求的文件名及相对路径. 
2. 查找这个文件是否存在 不存在:404页面存在 :1.读取这个资源2. 构建响应协议

 

 1.项目架构

2.kittyServer,创建一个ServerSocket,从配置文件中读取端口号,启动一个线程

package com.yc.http.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class KittyServer {
	public static void main(String[] args) throws Exception {
		KittyServer ks=new KittyServer();
		ks.startServer();
	}
	
	
	boolean flag=false;
	private void startServer() throws Exception{
		ServerSocket ss=null;
		int port=parseServerXml();
		
		try {
			ss=new ServerSocket(port);
			YcConstants.logger.debug("kitty server is starting,and listening to port"+ss.getLocalPort());
			
		} catch (IOException e) {
			YcConstants.logger.debug("kitty server is port "+port+"is aleready in use...");
			return;
		}
		
		while(!flag){
			Socket s=ss.accept();
			YcConstants.logger.debug("a client"+s.getInetAddress()+"is connecting to kittyserver");
			TaskService ts=new TaskService(s);
			Thread t=new Thread(ts);
			t.start();
		}
	}
	
	private int parseServerXml() throws Exception{
		List<Integer> list=new ArrayList<Integer>();
		DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();//通过DocumentBuilderFactory创建xml解析器
		try{
			DocumentBuilder bulider=factory.newDocumentBuilder();//通过解释器创建一个可以加载并生成xml的DocumentBuilder
			Document doc=bulider.parse(YcConstants.SERVERCONFIG);//通过DocumentBuilder加载并生成一颗xml树,Document对象的实例
			NodeList nl=doc.getElementsByTagName("Connector");//通过document可以遍历这棵树,并读取相应的节点的内容
			for(int i=0;i<nl.getLength();i++){
				Element node=(Element)nl.item(i);
				list.add(Integer.parseInt(node.getAttribute("port")));
			}
		}catch(Exception e){
			throw e;
		}
		return list.get(0);
	}

}

3.真正的任务,创建HttpServletRequest  HttpServletResponse,并返回响应内容

package com.yc.http.server;

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

public class TaskService implements Runnable {
	private Socket socket;
	private InputStream iis;
	private OutputStream oos;
	private boolean flag;

	public TaskService(Socket socket){
		this.socket=socket;
		try {
			this.iis=this.socket.getInputStream();
			this.oos=this.socket.getOutputStream();
			flag=true;
		} catch (IOException e) {
			YcConstants.logger.error("failed to get stream ",e);
			
			flag=false;
			throw new RuntimeException(e);
		}
	}

	@Override
	public void run() {
		if(flag){
			//包装一个HttpServletRequest对象,从iis中读取到数据,解析请求信息,保存信息
			HttpServletRequest request=new HttpServletRequest(this.iis);
			//包装一个HttpServletResponse对象 ,从request中取文件资源,构建响应头,会给客户端
			HttpServletResponse response=new HttpServletResponse(request, this.oos);
			response.sendRedirect();
		}
		try {
			this.socket.close();
		} catch (IOException e) {
			YcConstants.logger.error("failed to close connection to client ",e);
		}
		
		
	}
}

4.自定义HttpServletRequest

package com.yc.http.server;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;

public class HttpServletRequest {
	private String method; //请求方法
	private String protocal;//协议版本
	private String serverName;//服务名
	private int serverPort;//端口
	private String requestURI;//资源的地址
	private String requestURL;//绝对路径
	private String contextPath;//项目上下文路径
	private String realPath=System.getProperty("user.dir")+File.separatorChar+"webapps";//user.dir 取到时当前class的路径
	private InputStream iis;
	
	public HttpServletRequest(InputStream iis){
		this.iis=iis;
		parseRequest();
	}
	
	private void parseRequest(){
		String requestInfoString=readFromInputstream();//从输入流读取请求头
		if(requestInfoString==null || "".equals(requestInfoString)){
			return;
		}
		//解析requestinfo字符串  method URI
		parseRequestInfoString(requestInfoString);
	}
	
	private void parseRequestInfoString(String requestInfoString){
		StringTokenizer st=new StringTokenizer(requestInfoString);//分割
		
		// GET /res/aaa/index.html HTTP/1.1
		if(st.hasMoreTokens()){
			this.method=st.nextToken();
			this.requestURI=st.nextToken();///res/aaa/index.html
			this.protocal=st.nextToken();
			this.contextPath="/"+ this.requestURI.split("/")[1];//contextPath上下文路径/res
		}
		
		//TODO: 后面的键值对 实体 暂时不管
	}
	
	//从输入流读取请数据
	public String readFromInputstream(){
		//1.从input 中读出所有的内容(http请求协议--》protocal)
		String protocal=null;
		//从 iis流中取protocal
		StringBuffer sb=new StringBuffer(1024*10);//10k
		int length=-1;
		byte [] bs=new byte[1024 *10];
		
		try {
			length=this.iis.read(bs);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			length=-1;
		}
		for(int j=0;j<length;j++){
			sb.append((char)bs[j]);
		}
		protocal=sb.toString();
		return protocal;
	}

	public String getMethod() {
		return method;
	}

	public String getProtocal() {
		return protocal;
	}

	public String getServerName() {
		return serverName;
	}

	public int getServerPort() {
		return serverPort;
	}

	public String getRequestURI() {
		return requestURI;
	}

	public String getRequestURL() {
		return requestURL;
	}

	public String getContextPath() {
		return contextPath;
	}

	public String getRealPath() {
		return realPath;
	}

	

}

5.自定义HttpServletResponse

package com.yc.http.server;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

public class HttpServletResponse {
	private OutputStream oos;
	private HttpServletRequest request;
	
	public HttpServletResponse(HttpServletRequest request,OutputStream oos){
		this.oos=oos;
		this.request=request;
	}
	
	/*
	 * 从request中取出uri 2.判断是否在本地有个这文件 没有就404
	 * 以输出流将文件写到客户端,要加入响应的协议
	 * 
	 * 	 
	 */
	
	public void sendRedirect(){
		String responseprotocal=null;//响应协议头
		byte[] fileContent=null;//响应的内容
		String uri=request.getRequestURI();//请求的资源的地址     /res/aaa/index.html
		File f=new File(request.getRealPath(), uri);       //请求的文件的绝对路径
		if(!f.exists()){
			fileContent=readFile(new File(request.getRealPath(),"/404.html"));
			responseprotocal=gen404(fileContent.length);
		}else{
			fileContent=readFile(f);
			responseprotocal=gen200(fileContent.length);
		}
		try {
			oos.write(responseprotocal.getBytes());
			oos.flush();
			oos.write(fileContent);
			oos.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		   if(oos!=null){
			   try {
				oos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		   }
		}
		
	}
	
	private byte[] readFile(File f){
		FileInputStream fis=null;
		//字节数组输出流 :读取到字节数组后,存到内存
		ByteArrayOutputStream boas=new ByteArrayOutputStream();
		try{
			//读取这个文件
			fis=new FileInputStream(f);
			byte [] bs=new byte[1024];
			int length=-1;
			while((length=fis.read(bs, 0, bs.length))!=-1){
				boas.write(bs, 0, length);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(fis!=null){
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return boas.toByteArray();//一次性地从内存中读取所有的字节数组返回
	}
	
	
	private String gen200(long bodylength){
		String uri=this.request.getRequestURI();//取出要访问的文件的地址
		int index=uri.lastIndexOf(".");
		if(index>=0){
			index=index+1;
		}
		String fileExtension=uri.substring(index);//文件的后缀名
		String protocal200="";
		if("JPG".equalsIgnoreCase(fileExtension) || "JPEG".equalsIgnoreCase(fileExtension)){
			protocal200="HTTP/1.1 200 OK\r\nContent-Type: image/JPEG\r\nContent-Length: "+bodylength+"\r\n\r\n";
		}else if("PNG".equalsIgnoreCase(fileExtension)){
			protocal200="HTTP/1.1 200 OK\r\nContent-Type: image/PNG\r\nContent-Length: "+bodylength+"\r\n\r\n";
		}else if("json".equalsIgnoreCase(fileExtension)){
			protocal200="HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: "+bodylength+"\r\n\r\n";
		}else if("css".equalsIgnoreCase(fileExtension)){
			protocal200="HTTP/1.1 200 OK\r\nContent-Type: text/css\r\nContent-Length: "+bodylength+"\r\n\r\n";
		}else if("js".equalsIgnoreCase(fileExtension)){
			protocal200="HTTP/1.1 200 OK\r\nContent-Type: text/javascript\r\nContent-Length: "+bodylength+"\r\n\r\n";
		}else{
			protocal200="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: "+bodylength+"\r\n\r\n";
		}
		return protocal200;
	}
	
	
	//404响应
	private String gen404(long bodylength){
		String protocal404="HTTP/1.1 404 File Not Found\r\nContent-Type: text/html\r\nContent-Length: "+bodylength+"\r\n\r\n";
		return protocal404;
	}
	

}

 

5.定义了一些常量

package com.yc.http.server;

import org.apache.log4j.Logger;

public class YcConstants {
	//配置server.xml文件名字
	public final static String SERVERCONFIG="conf/server.xml";
	
	/**
	 * 日志对象
	 */
	public final static Logger logger=Logger.getLogger(YcConstants.class);

}

用到的技术: ServerSocket  ->  Socket  ,线程,dom解析,http协议

注意的问题:
  1. HttpServletRequest类中的  private String readFromInputStream()方法,要一次读取所有的请求头数据. 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值