首页 > Web开发 > 详细

WebServer的响应和XML解析

时间:2021-03-27 22:37:58      阅读:32      评论:0      收藏:0      [点我收藏+]

WebServer的响应和XML解析

WebServer(一)

1 阶段目的

解决代码冗余,响应工作重用。

设计:

与请求相似,设计一个类HttpResponse,用它每个实例表示要给客户端回复的响应内容,然后在ClientHandler开始工作时将该对象实例化,并在处理请求的过程中将要响应的内容设置到该对象,并在响应客户端的工作中统一将该对象内容以标准的响应格式发送给客户端。

2 编写HttpResponse类

把处理响应的代码抽取出来,减少代码冗余。

package cn.tedu.vip.webserver.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 响应客户端请求的对象
 * 每一个请求对应一个响应
 * 响应对象包含如下部分
 * 状态行,响应头,响应正文
 * @author Tedu
 *
 */

public class HttpResponse {
	//状态行相关信息
	private int statusCode=200;//状态码
	private String statusReason="OK";//状态结果

	//响应头相关信息
	
	//响应正文文件
	private File entity;

	//连接对象和输出流
	private Socket socket;
	private OutputStream out;

	//构造方法,为socket和out赋值
	public HttpResponse(Socket socket) {
		try {
			this.socket=socket;
			this.out=socket.getOutputStream();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/*
	 * 将当前响应对象的内容以标准的http协议格式响应给客户端
	 * 
	 */
	public void flush() {
		try {
			//发送状态行
			System.out.println("开始发送状态行");
			sendStatusLine();

			//发送响应头
			System.out.println("开始发送消息头");
			sendHerders();

			//发送响应正文
			System.out.println("开始发送消息正文");
			sendContent();

		} catch (Exception e) {	
			e.printStackTrace();
		}		
	}
	/*
	 * 发送状态行
	 */
	public void sendStatusLine() {
		try {
			String line = "HTTP/1.1"+" "+statusCode+" "+statusReason;
			out.write(line.getBytes("ISO8859-1"));
			out.write(13);
			out.write(10);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/*
	 * 发送响应头
	 */
	public void sendHerders() {
		try {
			//遍历headers,发送每个响应头信息
			for(Entry<String,String> e: headers.entrySet()) {
				String key=e.getKey();
				String value=e.getValue();
				String line=key+": "+value;
				System.out.println("响应头:"+line);
				out.write(line.getBytes("ISO8859-1"));
				out.write(13);
				out.write(10);
			}
			//单独发送一个换行表示响应头发送完毕
			out.write(13);
			out.write(10);	
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	// 发送响应正文
	public void sendContent() {
		try (
            FileInputStream fis=new FileInputStream(entity)
        ){
			byte[] data=new byte[1024*10];
			int len=-1;
			while((len=fis.read(data))!=-1) {
				out.write(data,0,len);
            }
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
		public File getEntity() {
		return entity;
	}
	public void setEntity(File entity) {
		this.entity = entity;
	}
	public int getStatusCode() {
		return statusCode;
	}
	public void setStatusCode(int statusCode) {
		this.statusCode = statusCode;
	}
	public String getStatusReason() {
		return statusReason;
	}
	public void setStatusReason(String statusReason) {
		this.statusReason = statusReason;
	}
	//这个方法不是自动生成的要自己写
	public void putHeader(String key,String value) {
		this.headers.put(key, value);
	}
}
3 重构ClientHandler类
package cn.tedu.vip.webserver.core;

import java.io.File;
import java.net.Socket;

import com.webserver.http.HttpRequest;
import com.webserver.http.HttpResponse;
/**
 * 用于处理客户端交互的业务类
 * @author Tedu
 *
 */
public class ClientHandler implements Runnable{
    private Socket socket;
    public ClientHandler(Socket socket) {
        this.socket=socket;
    }
    public void run() {
        /*
		 * Http协议要求一问一答的交互方式
		 * 客户端发送请求,服务器响应结果
		 * ClientHandler处理客户端请求有三个步骤
		 *1:解析请求
		 *1.1解析请求行
		 *1.2解析消息头
		 *1.3解析消息正文
		 *2:处理请求
		 *3:发送响应 
		 */
        try {
            //1:实例化HttpRequest对象,解析请求
            HttpRequest request=new HttpRequest(socket);

            //2:处理请求
            //2.1通过request获取请求的抽象路径
            String path=request.getUri();
            System.out.println("抽象路径:"+path);

            //2.2通过该路径去webapps目录下寻找该资源
            File file=new File("./webapps"+path);

            //2.3判断该资源是否存在
            if(file.exists()){
                System.out.println("该资源已找到!");
                response.setEntity(file);
            }else{
                System.out.println("该资源不存在!");
                File notFoundFile=new File("./webapps/root/404.html");
                response.setStatusCode(404);
                response.setStatusReason("Not Found");
                response.setEntity(notFoundFile);      
            }
            //响应客户端
            response.flush();
        }catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            //处理完毕后与客户端断开连接
            try{
                socket.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

WebServer(二)

1 阶段目的

在indexhtml页面中显示一张图片。

  1. 当一个页面中包含其他资源时,浏览器在解释该页面时会自动再次发起请求向服务器索取这些资源并将内容显示当前浏览器上。将WebServer中原本关闭的接收多次客户端连接的操作打开,导入图片时,请求的页面无法正确显示,原因是Content-Type指明客户端请求的资源实际类型时是固定的text/html,导致无法正确理解因此页面出现了问题。
  2. 重构HttpResponse响应对象代码,将flush发送响应的过程分为三步,并以3个方法顺序调用完成:发送状态行,发送响应头,发送响应正文sendStatusLine(),sendHeaders(),sendContent(),将发送响应头的工作由原本固定发送两个头:Content-Type和Content-Length改为可以根据处理请求的结果而进行配置。
2 WebServer类

一个客户端请求的页面中,如果包含了图片等其他信息,;那么就相当于又发送了一次请求来请求图片等资源。

要求服务器端能连续的处理请求,在WebServer类接收请求的代码上加上循环结构来实现。

package cn.tedu.vip.webserver.core;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * WebServer项目的主类
 * 启动这个类,开启WebServer程序
 * 
 * 这个程序功能是启动之后,可以使用浏览器来发送请求
 * 我们编写代码对浏览器的请求作出响应的效果
 * @author Tedu
 *
 */

public class WebServer {
	//声明服务器对象,浏览器访问服务器的地址和端口
	private ServerSocket server;

	public WebServer() {
		try {
			System.out.println("服务器端正在启动。。。");
			server=new ServerSocket(8088);
			threadPool=Executors.newFixedThreadPool(50);
			System.out.println("服务器端启动完成!");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() {
		try {
			while(true) {
				System.out.println("等待客户端连接");
				Socket socket=server.accept();
				System.out.println("一个客户端连接了");
                
                //启动一个线程处理该客户端交互
				ClientHandler handler=new ClientHandler(socket);
				Thread t=new Thread(handler);
				t.start();
			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
	public static void main(String[] args) {
		WebServer server=new WebServer();
		server.start();
	}
}
3 HttpResponse类

为了更好的返回响应,将本类flush方法中的代码也分为三步:

  1. 输出状态行
  2. 输出响应头
  3. 输出响应正文
package cn.tedu.vip.webserver.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 响应客户端请求的对象
 * 每一个请求对应一个响应
 * 响应对象包含如下部分
 * 状态行,响应头,响应正文
 * @author Tedu
 *
 */

public class HttpResponse {
	//状态行相关信息
	private int statusCode=200;//状态码
	private String statusReason="OK";//状态结果

	//响应头相关信息
    //key:响应头名字  value:响应头的值
	private Map<String,String>headers=new HashMap<>();
	
    //响应正文相关信息
    
	//响应正文文件
	private File entity;

	//连接对象和输出流
	private Socket socket;
	private OutputStream out;

	//构造方法,为socket和out赋值
	public HttpResponse(Socket socket) {
		try {
			this.socket=socket;
			this.out=socket.getOutputStream();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/*
	 * 将当前响应对象的内容以标准的http协议格式响应给客户端
	 * 
	 */
	public void flush() {
		try {
			//发送状态行
			//System.out.println("开始发送状态行");
			sendStatusLine();

			//发送响应头
			//System.out.println("开始发送消息头");
			sendHerders();

			//发送响应正文
			//System.out.println("开始发送消息正文");
			sendContent();

		} catch (Exception e) {	
			e.printStackTrace();
		}		
	}
	/*
	 * 发送状态行
	 */
	public void sendStatusLine() {
		try {
			String line = "HTTP/1.1"+" "+statusCode+" "+statusReason;
			out.write(line.getBytes("ISO8859-1"));
			out.write(13);
			out.write(10);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/*
	 * 发送响应头
	 */
	public void sendHerders() {
		try {
			//遍历headers,发送每个响应头信息
			for(Entry<String,String> e: headers.entrySet()) {
				String key=e.getKey();
				String value=e.getValue();
				String line=key+": "+value;
				System.out.println("响应头:"+line);
				out.write(line.getBytes("ISO8859-1"));
				out.write(13);
				out.write(10);
			}
			//单独发送一个换行表示响应头发送完毕
			out.write(13);
			out.write(10);	
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	 // 发送响应正文
	 
	public void sendContent() {
		try (
            FileInputStream fis=new FileInputStream(entity)
        ){
            System.out.println("HttpResponse:开始发送响应正文...");
            //发送响应正文
            
			byte[] data=new byte[1024*10];
			int len=-1;
			while((len=fis.read(data))!=-1) {
				out.write(data,0,len);
			}
            System.out.println("HttpResponse:发送响应正文完毕!");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

	public File getEntity() {
		return entity;
	}
	public void setEntity(File entity) {
		this.entity = entity;
	}
	public int getStatusCode() {
		return statusCode;
	}
	public void setStatusCode(int statusCode) {
		this.statusCode = statusCode;
	}
	public String getStatusReason() {
		return statusReason;
	}
	public void setStatusReason(String statusReason) {
		this.statusReason = statusReason;
	}
	//这个方法不是自动生成的要自己写
	public void putHeader(String key,String value) {
		this.headers.put(key, value);
	}
}
4 ClientHandler类

本类使用一个Map保存了一些常见的文件后缀名和mime类型的对应关系,以备请求出现时使用

package cn.tedu.vip.webserver.core;

import java.io.File;
import java.net.Socket;

import com.webserver.http.HttpRequest;
import com.webserver.http.HttpResponse;
/**
 * 用于处理客户端交互的业务类
 * @author Tedu
 *
 */
public class ClientHandler implements Runnable{
    private Socket socket;
    public ClientHandler(Socket socket) {
        this.socket=socket;
    }
    public void run() {
        /*
		 * Http协议要求一问一答的交互方式
		 * 客户端发送请求,服务器响应结果
		 * ClientHandler处理客户端请求有三个步骤
		 *1:解析请求
		 *1.1解析请求行
		 *1.2解析消息头
		 *1.3解析消息正文
		 *2:处理请求
		 *3:发送响应 
		 */
        try {
            //1:实例化HttpRequest对象,解析请求
            HttpRequest request=new HttpRequest(socket);
            //实例化响应对象
            HttpResponse response=new HttpResponse(socket);

            //2:处理请求

            //2.1通过request获取请求的抽象路径
            String path=request.getUri();
            System.out.println("抽象路径:"+path);

            //2.2通过该路径去webapps目录下寻找该资源
            File file=new File("./webapps"+path);

            //2.3判断该资源是否存在
            if(file.exists()){
                System.out.println("该资源已找到!");
                response.setEntity(file);

                Map<String,String> map=new HashMap<>();
                map.put("html","text/html");
                map.put("png","image/png");
                map.put("jpg","image/jpeg");
                map.put("gif","image/gif");
                map.put("css","text/css");
                //获取请求资源的文件名 logo.png
                String fileName=file.getName();
                int index=fileName.lastIndexOf(".");//最后一个点出现的位置
				String ext=fileName.substring(index+1);//获得后缀名:png
                String mime=map.get(ext);
                
                response.putHeader("Content-Type", mime);
				response.putHeader("Content-Length", file.length()+"");

            }else{
                System.out.println("该资源不存在!");
                File notFoundFile=new File("./webapps/root/404.html");
                response.setStatusCode(404);
                response.setStatusReason("Not Found");
                response.setEntity(notFoundFile);      
                
                response.putHeader("Content-Type", mime);
				response.putHeader("Content-Length", file.length()+"");

            }
            //响应客户端
            response.flush();
        }catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            //处理完毕后与客户端断开连接
            try{
                socket.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}                                       

XML概述

1 什么是XML

XML指可扩展标记语言(eXtensible Markup Language)

<?xml version="1.0" encoding="UTF-8"?>
<note>
	<to>tom</to>
	<from>jreey</from>
    <title>好久不见</title>
    <content>特别想念你</content>
</note>

2 为什么需要XML

XML被设计用来传输和储存数据。

3 XML使用注意事项

  1. 元素和属性
  2. 大小写敏感
  3. 标签必须关闭
  4. 有且只有一个根元素
  5. 必须正确嵌套

4 XML特殊字符

实体引用 字符 说明

&lt < 小于

&gt > 大于

&amp & 与字符(和字符)

&apos ‘ 单引号

&quot “ 双引号

5 CDATA段

<![CDATA [文本内容]]>

上面格式中[文本内容]中,无论出现了什么特殊字符或符号,全部被认为是普通文本

XML解析

1 什么是XML解析

将XML文件中的内容读取到java中

2 XML解析的方式

SAX:速度快,但是不能修改元素内容

DOM:速度慢,但是可以修改元素容易

3 DOM4解析

1 使用maven导入坐标
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>cn.tedu.vip</groupId>
	<artifactId>Unit009</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<!-- 这里按照固定格式下载需要的jar包 这个固定格式称之为坐标 编写坐标后,maven会自动到远程中央仓库下载指定的jar包 http://maven.aliyun.com/mvn/view搜索下载jar 
			dom4j=dom for java 
			log4j=log for java 
			-->
		<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
		</dependency>

	</dependencies>
</project>
2 XML测试文件
<?xml version="1.0" encoding="UTF-8"?>
<list>
	<emp id="e1">
		<name>王五</name>
		<gender>男</gender>
		<age>30</age>
		<salary>5000</salary>
	</emp>
	<emp id="e2">
		<name>赵四</name>
		<gender>男</gender>
		<age>35</age>
		<salary>4500</salary>
	</emp>
	<emp id="e3">
		<name>刘三</name>
		<gender>男</gender>
		<age>31</age>
		<salary>6000</salary>
	</emp>
</list>
3 java编写对应XML文件的Emp类
package cn.tedu.vip.xml;

public class Emp {
	private String id;
	private String name;
	private String gender;
	private int age;
	private int salary;
	
	public Emp() {		
	}

	public Emp(String id, String name, String gender, int age, int salary) {
		super();
		this.id = id;
		this.name = name;
		this.gender = gender;
		this.age = age;
		this.salary = salary;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getSalary() {
		return salary;
	}

	public void setSalary(int salary) {
		this.salary = salary;
	}
}
4 XML解析类
package cn.tedu.vip.xml;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * 使用dom4j解析XML文档
 * @author Tedu
 *
 */

public class ParseXmlDemo {

	public static void main(String[] args) {
		/*
		 * 解析XML的步骤分为4步
		 * 1:创建一个SAXReader
		 * 2:使用SAXReader读取XML获得一个Document对象
		 * 注意:这个步骤是比较耗时的
		 * 将XML文件全部解析完才能获得Document对象
		 * 3:根据Document对象获得根元素
		 * 4:通过根元素获得所有XML的元素
		 * 也称遍历XML元素
		 */
		try {
			//1
			SAXReader reader=new SAXReader();
			
			//2
			Document doc=reader.read(new File("./emp.xml"));
			
			//3
			/*
			 * 相关API
			 * doc提供了获得根元素的方法
			 * Element getRootElement();
			 * 
			 * 每个Element又包含一些方法
			 * 一个ELement对象对应一组标签
			 * 
			 * String getName()返回标签的名字
			 * String getText()返回标签中包含的文本
			 * Element element(String name)获得当前标签中指定名字的子标签
			 * List elements()返回当前标签中的所有子标签
			 * List elements(String name)返回当前标签中指定名字的所有子标签
			 */
			Element root=doc.getRootElement();
			
			//4
			//准备一个集合接收所有员工
			List<Emp> emps=new ArrayList<>();
			//获取当前根元素的所有子元素
			List<Element> els=root.elements("emp");
			//遍历所有元素els
			for(Element el:els) {
				//获取员工id
				//Attribute att=el.attribute("id");
				//String id=att.getValue();
				//System.out.println(id);
				String id=el.attributeValue("id");
				System.out.println(id);
				
				//获得员工姓名
				Element nameEl=el.element("name");
				String empName=nameEl.getText();
				//System.out.println(empName);
				
				//获得员工性别
				String empGender=el.elementText("gender");
				System.out.println(empGender);
				
				String stringAge=el.elementText("age");
				int empAge=Integer.parseInt(stringAge);
				//System.out.println(empAge);
				
				int empSalary=Integer.parseInt(el.elementText("salary"));
				
				Emp emp=new Emp(id, empName, empGender, empAge, empSalary);
				emps.add(emp);
			}
			//遍历xml的for循环结束了
			
			System.out.println("解析完毕");
			
			for(Emp e:emps) {
				System.out.println("id:"+e.getId()+",姓名:"+e.getName()+",性别:"+e.getGender()+",年龄:"+e.getAge()+",工资:"+e.getSalary());
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

WebServer(三)

1 阶段目的

  1. 在com.webserver,http包中定义类:HttpContext,设计目的是存放所有HTTP协议规定死的内容
  2. 定义一个静态属性Map mimeMapping,用于保存所有的资源后缀与对应的Content-Type
  3. 在静态块中初始化这个Map
  4. 定义一个公有方法,可以根据资源后缀获取到对应的Content-Type的值
  5. 将ClientHandler原本创建Map的操作和从Map中获取Content-Type对应值的操作改为通过HttpContext获取

问题:

在ClientHandler处理请求的环节,无论是找到资源还是未找到资源设置404页面,都要在设置正文文件同时设置,设置对应的响应头Content-Type与Content-Length,因此将这两个头的工作放在setEntity方法中,这样每次设置正文的时候就自动设置这两个头了

解决:

  1. 将ClientHandler中找到资源后设置响应头的工作移动到HttpResponse的setEntity方法中
  2. 将设置404的分支中设置响应头的代码删除即可

2 新建HttpContext类

package cn.tedu.vip.webserver.http;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * HTTP规定的固定内容全在这个类中
 * @author Tedu
 *
 */

public class HttpContext {
	/*
	 * 保存所有后缀名对应mineType类型的map
	 * key:后缀
	 * value:mime
	 */
	private static Map<String,String> mimeMapping=new HashMap<>();

	static {
		//编写一个方法专门处理mimeMapping的静态资源
		initMimeMapping();
	}
	private static void initMimeMapping() {
		/*
		 * 赋值web.xml文件到项目中:conf/web.xml
		 * 使用dom4j,在pom.xml导入坐标
		 * 获得根元素后,将根元素中所有名为:mime-mapping的子元素获取
		 * 子元素中名为extension作为map的key
		 * 子元素中名为mime-type作为map中的value
		 */
		try {
			SAXReader reader=new SAXReader();
			Document doc=reader.read(new File("conf/Web.xml"));
			Element root=doc.getRootElement();

			List<Element> list=root.elements("mime-mapping");
			for (Element el : list) {
				String key=el.elementText("extension");
				String value=el.elementText("mime-type");
				mimeMapping.put(key, value);
			}
			//输出静态map的长度
			System.out.println(mimeMapping.size());


		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	/*
	 * 根据给定的后缀名返回这个后缀对应的mimeType
	 */
	public static String getMimeType(String ext) {
		return mimeMapping.get(ext);
	}

	public static void main(String[] args) {
		//String type=mimeMapping.get("exe");
		String type=HttpContext.getMimeType("exe");
		System.out.println(type);
	}
}

3 HttpResponse类

减少ClientHandler类响应操作的代码冗余,将setEntity方法重构一下,并在其他使用我们创建的HttpContext类来获取文件后缀名对应的mime类型,放入Content-Type中

package cn.tedu.vip.webserver.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 响应客户端请求的对象
 * 每一个请求对应一个响应
 * 响应对象包含如下部分
 * 状态行,响应头,响应正文
 * @author Tedu
 *
 */

public class HttpResponse {
	//状态行相关信息
	private int statusCode=200;//状态码
	private String statusReason="OK";//状态结果

	//响应头相关信息
	//key:响应头名字  value:响应头的值
	private Map<String,String>headers=new HashMap<>();

    //响应正文相关信息
    
	//响应正文文件
	private File entity;

	//连接对象和输出流
	private Socket socket;
	private OutputStream out;

	//构造方法,为socket和out赋值
	public HttpResponse(Socket socket) {
		try {
			this.socket=socket;
			this.out=socket.getOutputStream();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/*
	 * 将当前响应对象的内容以标准的http协议格式响应给客户端
	 * 
	 */
	public void flush() {
		try {
			//发送状态行
			System.out.println("开始发送状态行");
			sendStatusLine();

			//发送响应头
			System.out.println("开始发送消息头");
			sendHerders();

			//发送响应正文
			System.out.println("开始发送消息正文");
			sendContent();

		} catch (Exception e) {	
			e.printStackTrace();
		}		
	}
	/*
	 * 发送状态行
	 */
	private void sendStatusLine() {
		try {
            System.out.println("HttpResponse:开始发送状态行....");
			String line = "HTTP/1.1"+" "+statusCode+" "+statusReason;
			out.write(line.getBytes("ISO8859-1"));
			out.write(13);
			out.write(10);
            System.out.println("HttpResponse:发送状态行完毕!");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/*
	 * 发送响应头
	 */
	private void sendHerders() {
		try {
			//遍历headers,发送每个响应头信息
			for(Entry<String,String> e: headers.entrySet()) {
				String key=e.getKey();
				String value=e.getValue();
				String line=key+": "+value;
				System.out.println("响应头:"+line);
				out.write(line.getBytes("ISO8859-1"));
				out.write(13);
				out.write(10);
			}
			//单独发送一个换行表示响应头发送完毕
			out.write(13);
			out.write(10);	
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	 // 发送响应正文
	 
	private void sendContent() {
		try (FileInputStream fis=new FileInputStream(entity)){
			byte[] data=new byte[1024*10];
			int len=-1;
			while((len=fis.read(data))!=-1) {
				out.write(data,0,len);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

	public File getEntity() {
		return entity;
	}
    /*
     *设置响应正文文件的同时会根据该文件自动
     *添加响应头:Content-Type与Content-Length
     */
    
	public void setEntity(File entity) {
		/*
		 * 在Clinethandler调用这个方法时,直接将后续的操作放到这个方法中
		 * 达到减少Clienthandler类中代码冗余的目的
		 */
		this.entity = entity;
		String fname=entity.getName();
		int index=fname.lastIndexOf(".");//获得后缀
		String ext=fname.substring(index+1);
		
		String mime=HttpContext.getMimeType(ext);//type
		
		//设置文件头信息
		putHeader("Content-Type",mime);//type
		putHeader("Content-Length", entity.length()+"");	
	}
	public int getStatusCode() {
		return statusCode;
	}
	public void setStatusCode(int statusCode) {
		this.statusCode = statusCode;
	}
	public String getStatusReason() {
		return statusReason;
	}
	public void setStatusReason(String statusReason) {
		this.statusReason = statusReason;
	}
	//这个方法不是自动生成的要自己写
	public void putHeader(String key,String value) {
		this.headers.put(key, value);
	}
}

4 ClientHandler类

package cn.tedu.vip.webserver.core;

import java.io.File;
import java.net.Socket;

import com.webserver.http.HttpRequest;
import com.webserver.http.HttpResponse;
/**
 * 用于处理客户端交互的业务类
 * @author Tedu
 *
 */
public class ClientHandler implements Runnable{
    private Socket socket;
    public ClientHandler(Socket socket) {
        this.socket=socket;
    }
    public void run() {
        /*
		 * Http协议要求一问一答的交互方式
		 * 客户端发送请求,服务器响应结果
		 * ClientHandler处理客户端请求有三个步骤
		 *1:解析请求
		 *1.1解析请求行
		 *1.2解析消息头
		 *1.3解析消息正文
		 *2:处理请求
		 *3:发送响应 
		 */
        try {
            //1:实例化HttpRequest对象,解析请求
            HttpRequest request=new HttpRequest(socket);
            //实例化响应对象
            HttpResponse response=new HttpResponse(socket);

            //2:处理请求
            //2.1通过request获取请求的抽象路径
            String path=request.getUri();
            System.out.println("抽象路径:"+path);

            //2.2通过该路径去webapps目录下寻找该资源
            File file=new File("./webapps"+path);

            //2.3判断该资源是否存在
            if(file.exists()){
                System.out.println("该资源已找到!");
                response.setEntity(file);

            }else{
                System.out.println("该资源不存在!");
                File notFoundFile=new File("./webapps/root/404.html");
                response.setStatusCode(404);
                response.setStatusReason("Not Found");
                response.setEntity(notFoundFile);      
            }
            
            //响应客户端
            response.flush();
        }catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            //处理完毕后与客户端断开连接
            try{
                socket.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}                                       

WebServer的响应和XML解析

原文:https://www.cnblogs.com/xiongchenglong/p/14586468.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!