package 手写服务器httpserver_201_封装Request_储存参数_处理中文;
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];
}
public String getUrl() {
return url;
}
-
}
//------------服务器端-------------------------------------------
package 手写服务器httpserver_201_封装Request_储存参数_处理中文;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
/**
-
创建服务器并启动
-
1.请求
-
2.响应 :在接收客户端
*/
public class Server5 {//Server 服务器
private ServerSocket server;
//定义一个字符串常量 便于后面使用
public static final String CRLF = “\r\n”;//final最终的;CRLF换行符
//定义空格便于后面使用
public static final String BLANK = " ";//BLANK空格public static void main(String[] args) {
Server5 server = new Server5();
server.start();//调用
}/**
-
创建一个方法 便于使用:启动
/
public void start(){//start启动
try {
server = new ServerSocket(8888);
this.receive();//调用;启动时就准备接收
} catch (IOException e) {
e.printStackTrace();
}
}
/* -
接收客户端
*/
private void receive(){//receive接收
try {
Socket client = server.accept();//Server 服务器;accept接受
//请求
Request req = new Request(client.getInputStream());//响应 封装 Response rep = new Response(client.getOutputStream()); rep.println("<html><head><title>HTTP响应示例</title>"); //获取请求 rep.println("</head><body>"); rep.println("欢迎:").println(req.getParameter("uname")).println("回来"); rep.println("</body></html>"); //发送命令 rep.pushToClient(200);//pushToClient推动客户
} catch (IOException e) {
}
}
/** -
停止服务器
*/
public void stop(){//stop停止
}
}
//------响应端-----------------------------------------------------------------
package 手写服务器httpserver_201_封装Request_储存参数_处理中文;
-
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 {
//定义一个字符串常量 便于后面使用
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);
}
}
//-----------关闭流方法--------------------------------------
package 手写服务器httpserver_201_封装Request_储存参数_处理中文;
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.启动浏览器 输入内容 密码随意
测试结果成功