// 1.-----------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
import java.io.IOException;
import java.net.ServerSocket;
/**
-
创建服务器并启动
-
1.请求
-
2.响应 :在接收客户端
*/
public class Server {//Server 服务器
private ServerSocket server;
//定义一个字符串常量 便于后面使用
public static final String CRLF = “\r\n”;//final最终的;CRLF换行符
//定义空格便于后面使用
public static final String BLANK = " ";//BLANK空格private boolean isShutDown = false;//isShutDown判断线程池是否已关闭
public static void main(String[] args) {
Server server = new Server();
server.start();//调用
}
/**-
创建一个方法 便于使用:启动
/
public void start(){//start启动
start(8888);//调用
}
/* -
指定端口方法
/
public void start(int port){//start启动;port端口
try {
server = new ServerSocket(port);
this.receive();//调用;启动时就准备接收
} catch (IOException e) {
//e.printStackTrace();异常没必要报出
stop();
}
}
/* -
接收客户端
*/
private void receive(){//receive接收
try {//对代码在Servlet类进行了封装
while(!isShutDown){//只要出错就证明服务器有问题
new Thread(new Dispatcher(server.accept())).start();//Thread线程;start启动
}} catch (IOException e) {
//e.printStackTrace();
stop();//调用
}
}
/** -
停止服务器
*/
public void stop(){//stop停止
isShutDown = true;//如果服务器有问题;就变成true
CloseUtil.closeSocket(server); //关闭服务器
}
}
-
//-2. —封装响应信息------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Date;
/**
-
封装响应信息
-
Response响应
*/
public class Response {
//定义一个字符串常量 便于后面使用
public static final String CRLF = “\r\n”;//final最终的;CRLF换行符
//定义空格便于后面使用
public static final String BLANK = " ";//BLANK空格
//推送流
private BufferedWriter bw;
//正文
private StringBuilder content;//StringBuilder可变的字符序列;content内容
//存储头信息
private StringBuilder headInfo;//StringBuilder可变的字符序列;headInfo头信息
//存储正文长度
private int len = 0;
//构造器
public Response() {
headInfo = new StringBuilder();
content = new StringBuilder();
//重新构建 正文长度有可能是变化的
len = 0;
}public Response(OutputStream os) {
this();//构造器的相互调用
bw = new BufferedWriter(
new OutputStreamWriter(os));
}public Response(Socket client) {
this();//构造器的相互调用
try {
bw = new BufferedWriter(
new OutputStreamWriter(client.getOutputStream()));//处理异常
} catch (IOException e) {
headInfo = null;//如果报错 就为空 在下面’推送到客户端’增加一个判断
}
}
/**- 正文是外部构建
- @return
/
public Response println(String info){//Response响应
//不断构建
content.append(info).append(CRLF);//getBytes
//长度不断变化
len+=(info+CRLF).getBytes().length;//getBytes中文字符串;因为CRLF也占位置所以加上
return this;
}
/* - 构建响应头;内部构建
/
private void createHeadInfo(int code){//createHeadInfo创建的头信息;code代码
//1.HTTP协议版本,状态代码,描述
//把可变的与不变的区分开; 不变的
headInfo.append(“HTTP/1.1”).append(BLANK).append(code).append(BLANK);
//可变的
switch(code){
case 200:
headInfo.append(“OK”);
break;
case 404:
headInfo.append(“NOT FOUND”);//NOT FOUND无法找到
break;
case 500:
headInfo.append(“SEVER ERROR”);//SEVER ERROR服务器错误
break;
}
headInfo.append(CRLF);//加入空格BLANK
//2.响应头(Response Head)
headInfo.append(“Server:bjsxt Server/0.0.1”).append(CRLF);//0.0.1版本号
//日期
headInfo.append(“Date:”).append(new Date()).append(CRLF);//获取当前时间
//正文的类型及编码格式
headInfo.append(“Content-Type:Text/html;charset=gbk”).append(CRLF);//GBK汉字内码扩展规范简体中文
//处理正文的长度
headInfo.append(“Content-Length:”).append(len).append(CRLF);
//分隔符
headInfo.append(CRLF);//与正文分开的空格区域
}
/* - 推送到客户端方法
- @throws IOException
/
void pushToClient(int code) throws IOException{//pushToClient推动客户
if(null==headInfo){
code = 200;
}
//构建头
createHeadInfo(code);
//头信息+分隔符
bw.append(headInfo.toString());//处理异常
//正文
bw.append(content.toString());
bw.flush();
}
/* - 关闭
*/
public void close(){
CloseUtil.closeIO(bw);
}
}
//-----3. --请求Request--------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
-
请求Request 封装
/
public class Request {
//请求方式
private String method;//method方法
private String url;//请求资源
//请求参数:Map映射;List目录,可能是多个;parameterMapValues参数映射值
private Map<String,List> parameterMapValues;
//内部常量
//定义一个字符串常量 便于后面使用
public static final String CRLF = “\r\n”;//final最终的;CRLF换行符
//定义空格便于后面使用
public static final String BLANK = " “;//BLANK空格
private InputStream is;//输入流
//请求信息
private String requestInfo;//requestInfo请求信息
public Request() {//空参
//初始化 避免空指针异常
method = “”;
url = “”;
//在界面获取的都是字符串 ,以后需要什么类型在转
parameterMapValues = new HashMap<String,List>();
requestInfo = “”;
}
public Request(InputStream is) {
this();
this.is = is;
try {
byte[] data = new byte[20480];//字节数组
int len = is.read(data);//内部使用
requestInfo = new String(data,0,len); //构建
} catch (IOException e) {
return ;//如果报错 就停止
}
parseRequestInfo();//分析头信息
}
//创建方法;分析请求信息 即分解字符串
private void parseRequestInfo(){//parseRequestInfo解析请求信息
//如果为空或者 遇到空格就停止
if(null==requestInfo||(requestInfo=requestInfo.trim()).equals(”")){
return;
}
/*
* ================================================
*1.从信息的首行分解出:请求方式,请求路径,请求参数 (get可能存在)
* 如:GET /index.html?uname=aaa&pwd=1234567 HTTP/1.1
*2.如果为post方式,请求参数可能在正文最后中
* 思路:1.请求方法:找出第一个/截取即可 2.请求资源:找出第一个/HTTP
*/
String paramString = “”;//接收请求参数;paramString参数的字符串
//获取请求方式 firstLine第一行;substring子字符串;indexOf 查找字符或者子串第一次出现的地方
String firstLine = requestInfo.substring(0,requestInfo.indexOf(CRLF));//获取第一行0至换行
//获取第一行首位字符串
int idx = requestInfo.indexOf("/");//记录’/‘的位置
this.method = firstLine.substring(0,idx).trim();//trim修剪,整理
String urlstr = firstLine.substring(idx,firstLine.indexOf(“HTTP/”)).trim();//indexOf 查找字符或者子串第一次出现的地方
//这里只判断2种方式
if(this.method.equalsIgnoreCase(“post”)){//IgnoreCase忽略大小写
this.url = urlstr;
paramString = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();//lastIndexOf 查找字符或者子串是最后一次出现的地方
}else if(this.method.equalsIgnoreCase(“get”)){
if(urlstr.contains("?")){//是否存在参数;contains包含
//如果存在 就要分割数组
String[] urlArray = urlstr.split("\?");//如果使用split’;’?'是特殊符号需要加\
this.url = urlArray[0];//开始位置
paramString = urlArray[1];//接收请求参数}else{ this.url = urlstr; } } //判断 if(paramString.equals("")){//不存在请求参数 return; } //如果从存在就封装到Map里面 parseParams(paramString);//这个方法不是任何时候都调用,只有paramString = urlArray[1];存在时才调用
}
//将 请求参数封装到Map中 好处是快速获取private void parseParams(String paramString){//parseParams解析参数;paramString参数的字符串
//分割;将字符串转成数组;StringTokenizer字符分解器
StringTokenizer token = new StringTokenizer(paramString,"&");
while(token.hasMoreTokens()){//hasMoreTokens得到更多的标记字符串
String keyValue = token.nextToken();//nextToken下一个标记字符串
//继续分割
String[] keyValues = keyValue.split("=");//split分割
//判断
if(keyValues.length1){
keyValues = Arrays.copyOf(keyValues, 2);//数组的拷贝
keyValues[1] = null;
}
//设置键与值
String key = keyValues[0].trim();
//这里增加中文解码方法decode();gbk简体中文(GBK)
String value = nullkeyValues[1]?null:decode(keyValues[1].trim(),“gbk”);//先判断如果等于null就是1即本身;否则就去下空
//转成Map 如果不存在就放进去
if(!parameterMapValues.containsKey(key)){//containsKey判断是否包含查找的是键
parameterMapValues.put(key,new ArrayList());//ArrayList数组列表;put放
}List<String> values = parameterMapValues.get(key);//拿到容器 values.add(value);//加个多个 }
}
/**-
增加 中文解码方法
-
decode解码器
/
private String decode(String value, String code){//value值;code代码
try {
return java.net.URLDecoder.decode(value,code);//java.net.URLDecoderjavanet URL解码器;(value,code)解码value及编码格式code
} catch (UnsupportedEncodingException e) {
//e.printStackTrace();
}
return null;
}
/* -
根据页面name 获取对应的多个值
-
@param name
-
@return
*/
public String[] getParameterValues(String name){List values = null;
if((values=parameterMapValues.get(name))==null){
return null;
}else{
return values.toArray(new String[0]);
}
}
/** -
根据页面获取对应的值;这是一个值
-
@param args
*/
public String getParameter(String name){
String[] values = getParameterValues (name);
if(null==values){
return null;
}
return values[0];
}
//为了外部获取 url 增加set与get方法
public String getUrl() {
return url;
}
}
-
//----4. -------添加服务程序-----------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**
- 添加服务程序
*/
public class RegisterServlet extends Servlet{
@Override
public void doGet(Request req,Response rep) throws Exception {
}
/**
* 下面的方法同上面一致
*/
@Override
public void doPost(Request req,Response rep) throws Exception {
rep.println("<html><head><title>返回注册</title>");
rep.println("</head><body>");//获取请求
rep.println("你的用户名:"+req.getParameter("uname"));
rep.println("</body></html>");
}
}
//------5. -----Dispatcher调度程序------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
import java.io.IOException;
import java.net.Socket;
/**
-
线程类;Dispatcher调度程序
*一个请求与相应,就一个此对象
*/
public class Dispatcher implements Runnable{
private Socket client;//初始化
private Request req;
private Response rep;
private int code = 200;
Dispatcher(Socket client) {//构造器
this.client = client;
try {
req = new Request(client.getInputStream());//包装 Server6里的接收客户端req
rep = new Response(client.getOutputStream());
} catch (IOException e) {
//e.printStackTrace();
code = 500;//如果报错 就显示500
return;
}
}
//重写
@Override
public void run() {//发送命令 try { Servlet serv = WebApp.getServlet(req.getUrl());//多态 if(null==serv){ this.code = 404;//如果找不到对应处理 就显示404 }else { serv.service(req, rep); } rep.pushToClient(code);//pushToClient推动客户 } catch (Exception e) {//两个异常合并一个 e.printStackTrace(); this.code = 500;//系统内部错误 } try { rep.pushToClient(500);//如果报错 就显示500 } catch (IOException e) { e.printStackTrace(); } //关闭流 CloseUtil.closeSocket(client);
}
}
//-----6. ------登录-服务----------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**
- LoginServlet登录服务
*/
public class LoginServlet extends Servlet{
@Override
public void doGet(Request req,Response rep) throws Exception {
String name = req.getParameter("uname");
String pwd = req.getParameter("pwd");
if(login(name, pwd)){
rep.println("登录成功");
}else{
rep.println("登录失败");
}
}
public boolean login(String name,String pwd){
return name.equals("王伟") && pwd.equals("12345");
}
/**
* 下面的方法同上面一致
*/
@Override
public void doPost(Request req,Response rep) throws Exception {
}
}
//----7.----------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**
-
Servlet控制器
-
抽象为一个父类
*/
public abstract class Servlet {//进行抽象
//封装 ; service服务
public void service(Request req,Response rep) throws Exception{
this.doGet(req,rep);//抛出异常
this.doPost(req,rep);
}
public abstract void doGet(Request req,Response rep) throws Exception;//手动抛出异常
public abstract void doPost(Request req,Response rep) throws Exception;
}// 8.-----上下文----------------------------------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
import java.util.HashMap;
import java.util.Map;
/**
-
上下文
*/
public class ServletContext {
//为每一个servlet取个名字
private Map<String, Servlet> servlet ;
//一个名字可以多个路径访问;一个资源可以映射多个路径的访问
private Map<String,String> mapping ;//mapping映射ServletContext(){//初始化
servlet = new HashMap<String, Servlet>();
mapping = new HashMap<String, String>();
}public Map<String, Servlet> getServlet(){
return servlet;
}
public void setServlet(Map<String, Servlet> servlet) {
this.servlet = servlet;
}
public Map<String, String> getMapping() {
return mapping;
}
public void setMapping( ) {
this.mapping = mapping;
}
}
// 9.---------WebApp-网页应用-------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
/**
- 网页应用
*/
import java.util.Map;
public class WebApp {
private static ServletContext contxt;
static {
contxt = new ServletContext(); //模拟
Map<String, String> mapping = contxt.getMapping();
mapping.put("/login", "login"); //模拟数据
mapping.put("/log", "login");
mapping.put("/reg", "register");
Map<String, Servlet> servlet = contxt.getServlet();
servlet.put("login", new LoginServlet());//new个子类
servlet.put("register", new RegisterServlet());
}
public static Servlet getServlet(String url){//通过url获取不同的数据
if((null==url)||(url.trim()).equals("")){//=空就不用处理
return null;
}
return contxt.getServlet().get(contxt.getMapping().get(url));//否则就从contxt里面获取url
}
}
// 10.----------关闭流的方法 ------------------------------------
package 手写服务器httpserver_203_多请求处理_多态_练习;
import java.io.Closeable;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
/**
-
关闭流的方法
/
public class CloseUtil {//CloseUtil关闭所有的
/*-
关闭IO流
-
@param io
/
/public static void closeIO(Closeable…io){//closeAll全部关闭;Closeable可关闭的参数
//判断
for(Closeable temp:io){
try {
if(null!=temp){
temp.close();//处理异常
}
} catch (IOException e) {
e.printStackTrace();
}
}
}/
/* -
使用泛型实现关闭IO流
*/
public static void closeIO(T…io){
for(Closeable temp:io){
try {
if(null!=temp){
temp.close();//处理异常
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void closeSocket(ServerSocket socket){
try {
if(null!=socket){
socket.close();//处理异常
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void closeSocket(Socket socket){
try {
if(null!=socket){
socket.close();//处理异常
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void closeSocket(DatagramSocket socket){if(null!=socket){ socket.close();//处理异常 }
}
} -
//结果测试-----------------------------------
1.启动服务器
2. 复制
3.打开并修改,保存
4.打开浏览器 输入
换个名字 或密码测试
测试reg