基本理念:1.获取请求协议。2.返回响应协议。3.封装响应信息。
4.封装请求信息。5.处理请求参数。6.建立Servlet。7.整合配置文件。8.封装多线程。9.处理错误内容和首页。
1.获取请求信息,并且封装
public class Request {
private final String CRLF="\r\n";
//协议信息
private String s;
//协议方式
private String method;
//请求URL
private String url;
//请求参数
private String queryStr;
//存储参数
private Map<String ,List<String>> p;
private Request(InputStream is) {
p=new HashMap<>();
byte[] data=new byte[1024*1024];
int len;
try {
len = is.read(data);
this.s=new String(data,0,len);
} catch (IOException e) {
e.printStackTrace();
}
parseRequestInfo();
}
//获取服务端传入的输入流
public Request(Socket client) throws IOException {
this(client.getInputStream());
}
//分析请求信息
private void parseRequestInfo() {
System.out.println("---------");
method=s.substring(0,s.indexOf("/")).toLowerCase();
method.trim();
int startIdx=s.indexOf("/")+1;
int endIdx=s.indexOf("HTTP/");
url=s.substring(startIdx,endIdx).trim();
System.out.println(url);
int queryidx=url.indexOf("?");
if(queryidx>=0) {
String[] urlarray=url.split("//?");
url=urlarray[0];
queryStr=urlarray[1];
}
if(method.equals("post")) {
String sqtr=s.substring(s.lastIndexOf(CRLF)).trim();
if(queryStr==null) {
queryStr=sqtr;
}
else {
queryStr+="&"+sqtr;
}
}
queryStr=null==queryStr?"":queryStr;
convertMap();
}
//将不同的请求信息分割,存入map中
private void convertMap() {
//按&分割
String[] keyValues=queryStr.split("&");
//按=分割
for(String bat:keyValues) {
String[] kv=bat.split("=");
kv=Arrays.copyOf(kv, 2);
//获取字符串
String key=kv[0];
String value=kv[1]==null?null:decode(kv[1]);
//存储到MAP中
if(p.containsKey(key)) {
//第一次
p.put(key,new ArrayList<String>());
}
p.get(key).add(value);
}
}
//处理请求参数中中文问题
private String decode(String value) {
try {
return java.net.URLDecoder.decode(value,"utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
//通过Name获取多个值
public String[] getPValues(String key) {
List<String> list=p.get(key);
if(list==null || list.size()<1)
{
return null;
}
return list.toArray(new String[0]);
}
public String getPValue(String key) {
String[] value=getPValues(key);
return value==null?null:value[0];
}
public String geturl() {
return url;
}
public String getmethod() {
return method;
}
public String queryStr() {
return queryStr;
}
}
封装返回的响应信息
public class Response {
private final String BLANK=" ";
private final String CRLF="\r\n";
private BufferedWriter bw;
//正文
private StringBuilder content;
//协议头信息(状态行与请求头 回车)
private StringBuilder head;
//长度
private int len=0;
private Response() {
content=new StringBuilder();
head=new StringBuilder();
}
//根据服务端创建输出流
public Response(Socket client) {
this();
try {
bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
} catch (IOException e) {
e.printStackTrace();
};
}
public Response(OutputStream os) {
this();
bw=new BufferedWriter(new OutputStreamWriter(os));
}
//构建响应头信息
private void createHeadInfo(int code) {
head.append("HTTP/1.1").append(BLANK);
head.append(code).append(BLANK);
switch(code) {
case 200:
head.append("OK").append(CRLF);
break;
case 404:
head.append("NOT FOUND").append(CRLF);
break;
case 505:
head.append("SERVER ERROR").append(CRLF);
break;
}
head.append("Date:").append(new Date()).append(CRLF);
head.append("Server:").append("shsxt Server/0.0.1;charset=GBK").append(CRLF);
head.append("Content-type:text/html").append(CRLF);
head.append("Content-length:").append(len).append(CRLF);
head.append(CRLF);
}
//动态添加内容
public Response print(String info) {
content.append(info);
len+=info.getBytes().length;
return this;
}
public Response println(String info) {
content.append(info).append(CRLF);
len+=(CRLF+info).getBytes().length;
return this;
}
//将回应信息推回浏览器
public void pushToBrowser(int code) {
if(null==head)
{
return ;
}
createHeadInfo(code);
try {
bw.write(head.toString());
bw.write(content.toString());
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
根据传入的请求信息,整合xml文件反射出要加载的类(Web.xml文件根据自身需求编写)
public class WebApp {
// //根据对应的请求参数封装web.xml里的资源信息
private static WebContext context;
static {
//获取SAX解析
//1.获取解析工厂
SAXParserFactory factory=SAXParserFactory.newInstance();
//2.从解析工厂获取解析器
SAXParser parse;
try {
parse = factory.newSAXParser();
//3.编写处理器(自己的)
//4.加载文档Document注册处理器
WebHandler handler=new WebHandler();
//获取当前线程的类加载器
parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("Servers/web.xml"), handler);
context=new WebContext(handler.getEntitys(),handler.getMappings());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Servlet getServletFromUrl(String url) throws Exception, {
String name=context.getClz("/"+url);
Class clz=Class.forName(name);
//Servlet是小程序(接口),用它扩展其他的类,这由自己编写不再给出
Servlet servlet=(Servlet)clz.getConstructor().newInstance();
System.out.println(servlet);
return servlet;
}
}
自己的处理器
public class WebHandler extends DefaultHandler {
private List<Entity> entitys;
private List<Mapping> mappings;
private Entity entity;
private Mapping mapping;
private boolean isMapping=false;
String tag;
//加载文档 只需要加载一次
public void startDocument() {
entitys =new ArrayList<>();
mappings =new ArrayList<>();
}
//Element需要多次的加载
@Override
public void startElement(String uri,String localName,String qName,Attributes attributes) {
if(qName!=null) {
tag=qName;
if(tag.equals("servlet")) {
entity=new Entity();
isMapping=false;
}
else if(tag.equals("servlet-mapping")){
mapping=new Mapping();
isMapping=true;
}
}
}
public void characters(char[] ch, int start, int length) {
String contents=new String(ch,start,length).trim();
if(tag!=null) {
if(isMapping) {
if(tag.equals("servlet-name")) {
mapping.setName(contents);
}
else if(tag.equals("url-pattern")) {
mapping.addPatterns(contents);
}
}
else {
if(tag.equals("servlet-name")) {
entity.setName(contents);
}
else if(tag.equals("servlet-class")) {
entity.setClz(contents);
}
}
}
}
@Override
public void endElement(String uri,String localName,String qName) {
if(qName!=null) {
if(qName.equals("servlet")) {
entitys.add(entity);
}
else if(qName.equals("servlet-mapping")) {
mappings.add(mapping);
}
}
tag=null;
}
public List<Entity> getEntitys() {
return entitys;
}
public void setEntitys(List<Entity> entitys) {
this.entitys = entitys;
}
public List<Mapping> getMappings() {
return mappings;
}
public void setMappings(List<Mapping> mappings) {
this.mappings = mappings;
}
}
//根据对应的请求参数封装web.xml里的资源信息
public class WebContext {
//Entity\Mapping是为web.xml里文件写的类
private List<Entity> entitys;
private List<Mapping> mappings;
private Map<String,String>entityMap=new HashMap<>();
private Map<String,String>mappingMap=new HashMap<>();
public List<Entity> getEntitys() {
return entitys;
}
public void setEntitys(List<Entity> entitys) {
this.entitys = entitys;
}
public List<Mapping> getMappings() {
return mappings;
}
public void setMappings(List<Mapping> mappings) {
this.mappings = mappings;
}
public WebContext(List<Entity> entitys,List<Mapping> mappings) {
this.entitys=entitys;
this.mappings=mappings;
for(Entity entity:this.entitys) {
entityMap.put(entity.getName(), entity.getClz());
}
for(Mapping mapping:this.mappings) {
for(String pattern:mapping.getPatterns()) {
mappingMap.put(pattern, mapping.getName());
}
}
}
public String getClz(String pattern) {
String name=mappingMap.get(pattern);
return entityMap.get(name);
}
}
连接用多线程封装,可以同时处理多个请求
public class Dispatcher implements Runnable {
private Socket client;
Request rq = null;
Response rs = null;
public Dispatcher(Socket client) {
this.client=client;
try {
rs =new Response(client);
rq = new Request(client);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
this.release();
}
}
@Override
public void run() {
Servlet servlet = null;
try {
//首页
if(rq.geturl()==null || rq.geturl().equals("")) {
rs.pushToBrowser(200);
InputStream is=new FileInputStream(new File("index.txt"));
rs.print((new String(is.readAllBytes())));
rs.pushToBrowser(404);
return ;
}
servlet = WebApp.getServletFromUrl(rq.geturl());
if(servlet==null) {
InputStream is=new FileInputStream(new File("404.txt"));
rs.print((new String(is.readAllBytes())));
rs.pushToBrowser(404);
}
else {
servlet.service(rq, rs);
rs.pushToBrowser(200);
}
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
rs.pushToBrowser(500);
}
release();
}
private void release() {
try {
client.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
服务器
public class Server01 {
ServerSocket ss;
private boolean isRunning;
public static void main(String[] args) {
Server01 ia=new Server01();
ia.start();
}
public void start() {
try {
ss=new ServerSocket(8888);
receive();
isRunning=true;
} catch (IOException e) {
e.printStackTrace();
System.out.println("启动失败");
}
}
public void receive() {
try {
while(isRunning) {
Socket client=ss.accept();
System.out.println("一个客户端建立了连接");
new Thread(new Dispatcher(client)).start();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("服务器启动异常");
stop();
}
}
public void stop() {
isRunning=false;
try {
ss.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
本文章中Servlet,web.xml,html文件等等没给出,由大佬们自己编写,写的不够完整,还有很多细节处理,如:空等问题需要改正,讲的思想大致没错。
学习web底层可以更好应用服务器,框架。谢谢大家