Java—手写服务器
1.预备知识_Socket知识回顾
1.1编写服务器用到的知识点
1)Socket编程
2)HTML
3)HTTP协议
4)反射
5)XML解析
6)服务器编写
1.2复习Socket编程
1)C/S结构:客户端与服务器端一次双向通信
2)B/S结构:浏览器与服务器
httpClient_1
Client.java
package com.bjsxt.test;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;public class Client {public static void main(String[] args) {//(1)创建Socket对象Socket client=null;//(2)获取输出流-->请求DataOutputStream dos=null;DataInputStream dis=null;try {client = new Socket("localhost", 8888);dos = new DataOutputStream(client.getOutputStream());dos.writeUTF("我是客户端:服务器你好!");//(3)获取输入流-->响应dis = new DataInputStream(client.getInputStream());System.out.println(dis.readUTF());} catch (UnknownHostException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(4)关闭流IOClose.closeAll(dis,dos,client);}}
}
IOClose.java
package com.bjsxt.test;import java.io.Closeable;
import java.io.IOException;public class IOClose {//关闭全部的工具类public static void closeAll(Closeable...c){for (Closeable closeable : c) {if (closeable!=null) {try {closeable.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
}
httpServer_1
Server.java
package com.bjsxt.server;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) {//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;//(3)获取Scoket对象//(4)获取输入流 -->得到客户端的请求DataInputStream dis=null;DataOutputStream dos=null;try {server = new ServerSocket(8888);client = server.accept();dis = new DataInputStream(client.getInputStream());System.out.println(dis.readUTF());//(5)获取输出流-->给客户端响应结果dos = new DataOutputStream(client.getOutputStream());dos.writeUTF("客户端您好:我是服务器,我收到了你的信息");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(dos,dis,client,server);}}
}
IOClose.java
package com.bjsxt.server;import java.io.Closeable;
import java.io.IOException;public class IOClose {//关闭全部的工具类public static void closeAll(Closeable...c){for (Closeable closeable : c) {if (closeable!=null) {try {closeable.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
}
Server2.java
package com.bjsxt.server;import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class Server2 {public static void main(String[] args) {//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;BufferedReader br=null;try {server = new ServerSocket(8888);client = server.accept();//获取来自浏览器的请求信息br=new BufferedReader(new InputStreamReader(client.getInputStream(), "utf-8"));String str=null;while((str=br.readLine()).length()>0){System.out.println(str);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(br,client,server);}}
}
2. 预备知识_HTML 简单入门
2.1HTML
HTML: HyperText Markup Language 超文本标记语言用于描述网页文档的一种标记语言
2.2 第一个 HTML 文档
<html><head><title>第一个 HTML</title></head><body><h1>hello world</h1></body>
</html>
2.3 表单 form
作用:与用户之间进行交互
method:请求方式 get/post
get 数据量小,安全性低,默认方式
post 数据量大,安全性高
action:请求的服务器路径
<form action="http://localhost:8888/index.html" method="post"><p>用户名:<input type="text" name="uname" id="name"/></p><p>密码 :<input type="password" name="pwd" id="pass"/></p><p><input type="submit" value="登录" /> </p>
</form>
id : (用户的的浏览器在文档里区分唯一性)前端区分唯一性,js 中
name:名称,后端(服务器)区分唯一性,获取值,只要提交数据
给后台(服务器)必须存在 name
httpServer_2
Server.java
package com.bjsxt.server;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) {//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;//(3)获取Scoket对象//(4)获取输入流 -->得到客户端的请求DataInputStream dis=null;DataOutputStream dos=null;try {server = new ServerSocket(8888);client = server.accept();dis = new DataInputStream(client.getInputStream());System.out.println(dis.readUTF());//(5)获取输出流-->给客户端响应结果dos = new DataOutputStream(client.getOutputStream());dos.writeUTF("客户端您好:我是服务器,我收到了你的信息");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(dos,dis,client,server);}}
}
Server2.java
package com.bjsxt.server;import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class Server2 {public static void main(String[] args) {//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;BufferedReader br=null;try {server = new ServerSocket(8888);client = server.accept();//获取来自浏览器的请求信息br=new BufferedReader(new InputStreamReader(client.getInputStream(), "utf-8"));String str=null;while((str=br.readLine()).length()>0){System.out.println(str);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(br,client,server);}}
}
Server3.java
package com.bjsxt.server;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server3 {public static void main(String[] args) {//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;InputStream is=null;try {server = new ServerSocket(8888);client = server.accept();//获取来自浏览器的请求信息is=client.getInputStream();byte [] buf=new byte[20480];int len=is.read(buf);System.out.println(new String(buf,0,len));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(is,client,server);}}
}
IOClose.java
package com.bjsxt.server;import java.io.Closeable;
import java.io.IOException;public class IOClose {//关闭全部的工具类public static void closeAll(Closeable...c){for (Closeable closeable : c) {if (closeable!=null) {try {closeable.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
}
index.html
<html><head><title>我的第一个html</title></head><body><h1>hello world</h1><form action="http://localhost:8888/log" method="get"><p>用户名:<input type="text" id="uname" name="username" /></p><p>密码:<input type="password" id="pwd" name="password"/></p><p>兴趣爱好<input type="checkbox" name="hobby" value="ball"/>球 <input type="checkbox" name="hobby" value="read"/>读书<input type="checkbox" name="hobby" value="paint"/>画画</p><p><input type="submit" value="登录"/></p></form></body></html>
3.预备知识_HTTP协议入门
3.1协议
1)应用层:HTTP、FTP、TELNET、SNMP、DNS
2)传输层:TCP、UDP
3) 网络层:IP
3.2HTTP协议简介
HTTP:Hypertext Transfer Protocol超文本传输协议,是网络应用层的协议,建立在TCP/IP协议基础上,HTTP使用可靠的TCP连接,默认端口为80.
用户打开web浏览器(常见的HTTP客户端),输入URL地址,就能接收到远程HTTP服务器端发送过来的网页,即HTTP遵循请求(Request)/应答(Response)模型。web浏览器向web服务器发送请求,web服务器处理请求并返回适当的应答,所有HTTP连接都被构造成一套请求与应答。
HTTP客户端和服务器分别由不同的软件开发商提供,它们都可以用任意的编程语言编写,如用.NET 编写的客户程序与用Java编写的服务器程序顺利通信,就必须遵守HTTP协议,这样才能彼此都懂对方发送的消息,HTTP协议严格规定了HTTP请求和HTTP响应的数据格式。
3.3HTTP请求格式
1)请求方式、URL(统一资源定位符)、HTTP协议/版本
2)请求头 Request Header
a)请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器所有的语言,请求正文的长度等。
3)请求正文 Requet Content (只有在 post 方式才有)
请求头和请求正文之间必须有符号行(回车符或行结束符),将请求头与请求正文分开,这个行非常重要,它表示 请求头已结束,接下来的是请求正文。通常post方式的数据存放于此,请求正文中可以包含客户提交的查询字符串等信息。在实际应用中,HTTP请求正文可以包含更多的内容。
3.4HTTP 响应格式
- HTTP 协议版本、状态代码、描述
- 响应头(Response Head)
- 响应正文(Respose Content)
httpServer_2
Server.java
package com.bjsxt.server;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) {//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;//(3)获取Scoket对象//(4)获取输入流 -->得到客户端的请求DataInputStream dis=null;DataOutputStream dos=null;try {server = new ServerSocket(8888);client = server.accept();dis = new DataInputStream(client.getInputStream());System.out.println(dis.readUTF());//(5)获取输出流-->给客户端响应结果dos = new DataOutputStream(client.getOutputStream());dos.writeUTF("客户端您好:我是服务器,我收到了你的信息");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(dos,dis,client,server);}}
}
Server2.java
package com.bjsxt.server;import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class Server2 {public static void main(String[] args) {//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;BufferedReader br=null;try {server = new ServerSocket(8888);client = server.accept();//获取来自浏览器的请求信息br=new BufferedReader(new InputStreamReader(client.getInputStream(), "utf-8"));String str=null;while((str=br.readLine()).length()>0){System.out.println(str);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(br,client,server);}}
}
Server3.java
package com.bjsxt.server;import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;public class Server3 {public static void main(String[] args) {String CRLF="\r\n";//换行String BLANK=" ";//空格//(1)创建ServerSocket对象ServerSocket server=null;//(2)监听是否有客户端发送请求Socket client=null;InputStream is=null;try {server = new ServerSocket(8888);client = server.accept();//获取来自浏览器的请求信息is=client.getInputStream();byte [] buf=new byte[20480];int len=is.read(buf);System.out.println(new String(buf,0,len));/**对Web浏览器的请求作出响应*/StringBuilder sb=new StringBuilder();StringBuilder sbContent=new StringBuilder();//响应的文本sbContent.append("<html><head><title>响应结果</title></head>");sbContent.append("<body>登录成功</body></html>");//(1)拼接响应头sb.append("HTTP/1.1").append(BLANK).append(200).append(BLANK).append("OK");sb.append(CRLF);//换行sb.append("Content-Type: text/html;charset=utf-8");sb.append(CRLF);//换行sb.append("Content-Length:").append(sbContent.toString().getBytes().length).append(CRLF);sb.append(CRLF);//换行,代表响应头与响应的正文部门之间的空行sb.append(sbContent);//通过流输出 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream(),"utf-8"));bw.write(sb.toString());bw.flush();bw.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{//(6)关闭流IOClose.closeAll(is,client,server);}}
}
IOClose.java
package com.bjsxt.server;import java.io.Closeable;
import java.io.IOException;public class IOClose {//关闭全部的工具类public static void closeAll(Closeable...c){for (Closeable closeable : c) {if (closeable!=null) {try {closeable.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
}
4.Tomcat服务器的快速使用
4.1Tomcat简介
Tomcat是在SUN公司的JSWDK( JavaServer Web
DevelopmentKit ,是 SUN 公司推出的小型 Servlet/JSP 调试工
具)的基础上发展起来的一个优秀的Servlet容器,Tomcat本身完全用Java语言编写。
4.2Tomcat使用
1)配置Tomcat
a) JAVA_HOME Java JDK 的根目录
b) CATALINA_HOME Tomcat 根目录
bin:存放Tomcat脚本目录
- 启动和关闭 Tomcat
启动 Tomcat 服务器:startup.bat
关闭 Tomcat 服务器:shutdown.bat
conf:存放配置文件
conf—>server.xml:存放配置信息
存放配置端口信息
lib:存放Tomcat运行所需要的jar包
logs:存放日志文件
temp:存放Tomcat临时文件
webapps:部署项目到服务器
在 webapps 目录下新建目录存放.html 面页
访问页面
**work:**工作目录,将JSP文件的Java文件转换为class文件的工作目录。
部署项目到Tomcat
在webapps目录下创建自己的文件夹
在该文件夹下新建一个HTML文档
index.html
<html><head><title>发布到Tomcat中的项目的主页</title></head><body><h1>hello tomcat</h1></body>
</html>
bin目录下双击
启动Tomcat服务
在地址栏输入http://locathost:8080/myfirst/index.html
5.Tomcat服务器运行原理
5.1Tomcat的运行原理
客户浏览器发出要求访问特定德Servlet的请求。
1)Tomcat服务器接收到客户请求并解析
2)Tomcat服务器创建一个ServletRequest对象,在ServletRequest对象中包含了客户请求信息及其他关于客户的信息,如请求头,请求正文,以及客户机的IP地址等。
3)Tomcat服务器创建一个ServletResponse 对象
4)Tomcat服务器调用客户所请求的Servlet的service服务方法,并且把ServletRequest对象和ServletResponse对象做为参数传给该服务方法。
5) Servlet 从 ServletRequest 对象中可获取客户的请求信息。
6) Servlet 利用 ServletResponse 对象来生成响应结果。
7) Tomcat 服务器把 Servlet 生成的响应结果发送给客户。
8)
6.预备_http工具查看网络交互过程
6.1下载并安装
httpwatch安装包:https://pan.baidu.com/s/1ogcMmZSutx6WvIvh2VNpGg 密码:ggv4
6.2HttpWatch 的使用
IE 浏览器查看浏览器栏HttpWatch Professional
最新版已经可以支持Google、Microsoft Edge等浏览器了下面是官方使用文档
输入网址
分析数据
来源:https://blog.csdn.net/cldsj
HttpWatch学习笔记一
HttpWatch学习笔记二
HttpWatch学习笔记三
HttpWatch功能详细介绍
来源:https://www.cnblogs.com/Chilam007/p/6947235.html
7. 手写服务器_ 整体架构和接口_ 编写 XML配置文件
7.1 搭建项目框架
7.2 编写 XML 文档
<?xml version="1.0" encoding="UTF-8"?>
<web-app><servlet><servlet-name>login</servlet-name><serlvet-class>com.bjsxt.servlet.LoginServlet</serlvet-class></servlet><servlet-mapping><serlvet-name>login</serlvet-name><url-pattern>/login</url-pattern></servlet-mapping><servlet-mapping><serlvet-name>login</serlvet-name><url-pattern>/log</url-pattern></servlet-mapping>
</web-app>
7.3 编写 IOCloseUtil 类
package com.bjsxt.util;import java.io.Closeable;
import java.io.IOException;public class IOCloseUtil {//用于关系流public static void closeAll(Closeable...close){for (Closeable closeable : close) {if (closeable!=null) {try {closeable.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
}
8.DOM4J 解析 XML 配置文件
8.1Entity 实体类的编写
package com.bjsxt.server;
/*** <servlet><servlet-name>login</servlet-name><serlvet-class>com.bjsxt.servlet.LoginServlet</serlvet-class></servlet>* @author Administrator**/
public class Entity { /**servlet-name和一个servlet-name所对应的一个实体类*/private String name;//servlet-nameprivate String clazz;//servlet-classpublic String getName() {return name;}public void setName(String name) {this.name = name;}public String getClazz() {return clazz;}public void setClazz(String clazz) {this.clazz = clazz;}public Entity(String name, String clazz) {super();this.name = name;this.clazz = clazz;}public Entity() {super();}}
8.2Mapping 实体类的编写
package com.bjsxt.server;import java.util.ArrayList;
import java.util.List;/*** <servlet-mapping><serlvet-name>login</serlvet-name><url-pattern>/login</url-pattern><url-pattern>/log</url-pattern></servlet-mapping>* @author Administrator**/
public class Mapping {//映射关系,多个路径访问共享资源private String name;//servlet-nameprivate List<String> urlPattern;//url-patternpublic String getName() {return name;}public void setName(String name) {this.name = name;}public List<String> getUrlPattern() {return urlPattern;}public void setUrlPattern(List<String> urlPattern) {this.urlPattern = urlPattern;}public Mapping(){urlPattern=new ArrayList<String>();}public Mapping(String name, List<String> urlPattern) {super();this.name = name;this.urlPattern = urlPattern;}}
8.3 解析 XML 文件
package com.bjsxt.server;import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;public class WebDom4j {//用于解析XMLprivate List<Entity> entityList;//用于存储是N多Entity,而每一个Entity都是servlet-name与servlet-classprivate List<Mapping> mappingList;//用于存储N多Mapping,而每一个Mapping都是一个servlet-name与N多个url-patternpublic List<Entity> getEntityList() {return entityList;}public void setEntityList(List<Entity> entityList) {this.entityList = entityList;}public List<Mapping> getMappingList() {return mappingList;}public void setMappingList(List<Mapping> mappingList) {this.mappingList = mappingList;}//构造方法public WebDom4j() {entityList=new ArrayList<Entity>();mappingList=new ArrayList<Mapping>();}//获取Document对象的方法private Document getDocument(){try {//(1)创建SAXReader对象SAXReader reader=new SAXReader();//(2)调用read方法return reader.read(new File("src/WEB_INFO/web.xml"));} catch (DocumentException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}public void parse(Document doc){//(1)获取根元素Element root=doc.getRootElement(); //web-app//(2)获取servlet子元素for(Iterator<Element> ite=root.elementIterator("servlet");ite.hasNext();){Element subElement=ite.next();//得到每一个servlet//创建一个实体类Entity ent=new Entity(); //用于存储servlet-name与servlet-classfor(Iterator<Element> subIte=subElement.elementIterator();subIte.hasNext();){Element ele=subIte.next(); //可能是servlet-name,也可能是servlet-classif("servlet-name".equals(ele.getName())){ent.setName(ele.getText()); //给实体类中的name赋值}else if("serlvet-class".equals(ele.getName())){ent.setClazz(ele.getText());}}//将Entity添加到集合中entityList.add(ent);}//测试/**for (Entity entity : entityList) {System.out.println(entity.getName()+"\t"+entity.getClazz());}*///解析servlet-mappingfor(Iterator<Element> ite=root.elementIterator("servlet-mapping");ite.hasNext();){Element subEle=ite.next();//得到每一个servlet-mapping//创建一个Mapping类的对象Mapping map=new Mapping();//解析servlet-mapping下的子元素for(Iterator<Element> subIte=subEle.elementIterator();subIte.hasNext();){Element ele=subIte.next(); //servlet-name,也有可能是url-patternif("serlvet-name".equals(ele.getName())){map.setName(ele.getText());}else if("url-pattern".equals(ele.getName())){//获取集合对象,调用集合对象的添加方法,添加元素素map.getUrlPattern().add(ele.getText());}}//Mapping添加到集合中mappingList.add(map);}//测试for (Mapping m : mappingList) {System.out.println(m.getName());for(String s:m.getUrlPattern()){System.out.println(s);}}}//用于测试public static void main(String[] args) {WebDom4j web=new WebDom4j();web.parse(web.getDocument());}
}
9. 反射创建 Servlet 对象
9.1 编写 ServletContext 类
Servlet 上下文,就是一个容器,用于存储映射关系
package com.bjsxt.server;import java.util.HashMap;
import java.util.Map;/*** Servlet上下用,就是一个容器,* @author Administrator**/
public class ServletContext { //Entity与Mapping的映射关系private Map<String,String> servlet;//key是servlet-name (Entity中的name),值serlvet-class Entity中的clazzprivate Map<String,String> mapping;//key是url-pattern (Mapping中的List集合中的每一个元素),value是serlvet-name,是Mapping中的namepublic Map<String, String> getServlet() {return servlet;}public void setServlet(Map<String, String> servlet) {this.servlet = servlet;}public Map<String, String> getMapping() {return mapping;}public void setMapping(Map<String, String> mapping) {this.mapping = mapping;}public ServletContext() {servlet=new HashMap<String,String>();mapping=new HashMap<String,String>();}
}
9.2 编写 WebApp 类
- 初始化程序运行的数据
- 根据不同的 url 创建所请求的 Servlet 对象
package com.bjsxt.server;import java.util.List;
import java.util.Map;import com.bjsxt.servlet.Servlet;public class WebApp {//App的意思是应用程序private static ServletContext contxt;static{contxt=new ServletContext();//分别获取对应关系的Map集合Map<String,String> servlet=contxt.getServlet();Map<String,String> mapping=contxt.getMapping();//创建解析XML文件对象WebDom4j web=new WebDom4j();web.parse(web.getDocument());//解析xml//获取解析XML之后的List集合List<Entity> entityList=web.getEntityList();List<Mapping> mappingList=web.getMappingList();//将List集合中的数据存储到Map集合for (Entity entity : entityList) {servlet.put(entity.getName(), entity.getClazz());}// System.out.println(servlet);for( Mapping map:mappingList){//遍历url-pattern的集合List<String> urlPattern=map.getUrlPattern();for(String s:urlPattern){mapping.put(s, map.getName());}}//System.out.println(mapping);}/*** 根据url创建不同的Servlet对象* @param url* @return*/public static Servlet getServlet(String url){if (url==null||url.trim().equals("")) {return null;}//调用无参构造方法创建Servlet对象try {//如果url正确//根据url的key获取servlet-name的值 /log=login, /reg=registerString servletName=contxt.getMapping().get(url);//根据servletName得到对应的servlet-classString servletClass=contxt.getServlet().get(servletName); //得到的是一个完整个的包名+类的字符串//使用反射创建 Servlet对象Class<?> clazz=Class.forName(servletClass);Servlet servlet = (Servlet) clazz.newInstance();return servlet;} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}//测试public static void main(String[] args) {System.out.println(getServlet("/log"));System.out.println(getServlet("/login"));}
}
10. 封装 Request_method_url
10.1 编写 Server
- 启动服务
- 关闭服务
package com.bjsxt.server;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {//服务器,用于启动和停止服务private ServerSocket server;public static void main(String[] args) {Server server=new Server();//创建服务器对象server.start();}public void start(){this.start(8888);}public void start(int port){try {server=new ServerSocket(port);this.receive(); //调用接收请求信息的方法} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}private void receive() {try {//(1)监听Socket client=server.accept();//获取用户的请求/**InputStream is=client.getInputStream();byte []buf =new byte [20480];int len=is.read(buf);System.out.println(new String(buf,0,len));*///封装请求信息Request req=new Request(client.getInputStream());//req.show();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void stop(){}
}
10.2 编写 HTML
login.html
<html><head><title>登录</title></head><body><form action="http://localhost:8888/log" method="post"><p>用户名:<input type="text" name="username" id="username"/></p><p>密码:<input type="password" name="pwd" id="password"/></p><p>爱好:<input type="checkbox" name="hobby" value="ball"/>足球<input type="checkbox" name="hobby" value="read"/>读书<input type="checkbox" name="hobby" value="paint"/>画画</p><p><input type="submit" value="登录"/></p></form></body>
</html>
10.3 封装 Request_method_url
package com.bjsxt.server;import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class Request {/*请求*/private InputStream is;//输入流private String requestInfo;//请求字符串,请求方式,请求的路径,参数,协议,协议版本,请求的正文。。。private String method;//请求的方式private String url;//请求的url//输入框的name为key,值为value/** key: username value :bjsxt* key:pwd value:123* key:hobby value :read,ball* */private Map<String,List<String>> parametermapValues;//参数private static final String CRLF="\r\n";//换行private static final String BLANK=" ";//空格//构造方法,初始化属性public Request() {parametermapValues=new HashMap<String,List<String>>();method="";url="";requestInfo="";}public Request(InputStream is){this();//调用本类无参的构造方法this.is=is;try {byte [] buf=new byte[20480];int len=this.is.read(buf);requestInfo=new String(buf,0,len);} catch (IOException e) {return;}//调用本类中的分解请求信息的方法this.parseRequestInfo();}//分解请求信息的方法/*** 请求方式* 请求路径* 请求的参数* */private void parseRequestInfo(){String paraString="";//用于存储请求参数//获取请求参数的第一行String firstLine=requestInfo.substring(0, requestInfo.indexOf(CRLF)).trim();//从0开始,到第一个换行的位置//分解出请求方式int index=firstLine.indexOf("/");this.method=firstLine.substring(0, index).trim();//分解url ,get可能包含参数,也可能不包含参数postString urlString= firstLine.substring(index,firstLine.indexOf("HTTP/")).trim();//判断请求方式是GET还 是POSTif("get".equalsIgnoreCase(this.method)){ //包含请求参数if (urlString.contains("?")) {String [] urlArray=urlString.split("\\?");this.url=urlArray[0];paraString=urlArray[1];}else{this.url=urlString;}}else{//post不包含请求参数this.url=urlString;paraString=requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();}if (paraString.equals("")) {return;}//请求参数//System.out.println(paraString);}//用于测试/*public void show(){System.out.println(this.url);System.out.println(this.method);}*/
}
11. 封装 Request_ 存储参数_ 处理中文
11.1 编写分解参数的方法
package com.bjsxt.server;import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class Request {/*请求*/private InputStream is;//输入流private String requestInfo;//请求字符串,请求方式,请求的路径,参数,协议,协议版本,请求的正文。。。private String method;//请求的方式private String url;//请求的url//输入框的name为key,值为value/** key: username value :bjsxt* key:pwd value:123* key:hobby value :read,ball* */private Map<String,List<String>> parametermapValues;//参数private static final String CRLF="\r\n";//换行private static final String BLANK=" ";//空格//构造方法,初始化属性public Request() {parametermapValues=new HashMap<String,List<String>>();method="";url="";requestInfo="";}public Request(InputStream is){this();//调用本类无参的构造方法this.is=is;try {byte [] buf=new byte[20480];int len=this.is.read(buf);requestInfo=new String(buf,0,len);} catch (IOException e) {return;}//调用本类中的分解请求信息的方法this.parseRequestInfo();}//分解请求信息的方法/*** 请求方式* 请求路径* 请求的参数* */private void parseRequestInfo(){String paraString="";//用于存储请求参数//获取请求参数的第一行String firstLine=requestInfo.substring(0, requestInfo.indexOf(CRLF)).trim();//从0开始,到第一个换行的位置//分解出请求方式int index=firstLine.indexOf("/");this.method=firstLine.substring(0, index).trim();//分解url ,get可能包含参数,也可能不包含参数postString urlString= firstLine.substring(index,firstLine.indexOf("HTTP/")).trim();//判断请求方式是GET还 是POSTif("get".equalsIgnoreCase(this.method)){ //包含请求参数if (urlString.contains("?")) {String [] urlArray=urlString.split("\\?");this.url=urlArray[0];paraString=urlArray[1];}else{this.url=urlString;}}else{//post不包含请求参数this.url=urlString;paraString=requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim();}if (paraString.equals("")) {return;}//请求参数//System.out.println(paraString);//调用本类中分解请求参数的方法this.parseParam(paraString);}//用于测试/*public void show(){System.out.println(this.url);System.out.println(this.method);}*///username=bjsxt&pwd=123&hobby=ball&hobby=paint/*** username=bjsxt* pwd=123* hobby=ball* hobby=paint* * username=* @param prarString*/private void parseParam(String prarString){String [] token=prarString.split("&");for(int i=0;i<token.length;i++){String keyValues=token[i];String []keyValue=keyValues.split("="); //username bjsxt pwd 123if (keyValue.length==1) { //username=keyValue=Arrays.copyOf(keyValue, 2);keyValue[1]=null;}//将 表单元素的name与name对应的值存储到Map集合String key=keyValue[0].trim();String value=keyValue[1]==null?null:decode(keyValue[1].trim(), "utf-8");//放到集合中存储if (!parametermapValues.containsKey(key)) {parametermapValues.put(key, new ArrayList<String>());}List<String> values=parametermapValues.get(key);values.add(value);}}//根据表单元素的name获取多个值private String [] getParamterValues(String name){//根据key获取valueList<String> values=parametermapValues.get(name);if (values==null) {return null;}else{return values.toArray(new String [0] );}}private String getParamter(String name){//调用本类中根据name获取多个值的方法String [] values=this.getParamterValues(name);if (values==null) {return null;}else{return values[0];}}//处理中文,因类浏览器对中文进行了编码,进行解码private String decode(String value,String code){try {return URLDecoder.decode(value, code);} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}public static void main(String[] args) {Request req=new Request();//调用分解参数的方法req.parseParam("username=%E5%8C%97%E4%BA%AC%E5%B0%9A%E5%AD%A6%E5%A0%82&pwd=123&hobby=ball&hobby=paint");System.out.println(req.parametermapValues);//调用获取多个值的方法String [] str=req.getParamterValues("hobby");for (String string : str) {System.out.println(string);}//调用获取单个值的方法System.out.println(req.getParamter("pwd"));}}
11.2 编写根据页面上的 name 获取多个值的方法
11.3 编写根据页面上的 name 获取单个值的方法
11.4 编写解码的方法 1
1.5 测试
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=%E5%8C%97%E
4%BA%AC%E5%B0%9A%E5%AD%A6%E5%A0%82&rsv_pq=f65f15f40003a658&rsv_t=d74
7OilFeeFkf73QkinDYpKiB4y%2Ffj3mxWfE865IJ0VxoTk5Obwtc4z41pM&rqlang=cn&rsv_ente
r=1&rsv_sug3=19&rsv_sug1=21&rsv_sug7=101
12. 封装 Response
12.1 封装 Response
- 构造响应头
- 推送到客户端
package com.bjsxt.server;import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;import com.bjsxt.util.IOCloseUtil;public class Response {//响应private StringBuilder headInfo;//响应头private StringBuilder content;//响应内容private int length;//响应内容的长度//流private BufferedWriter bw;//两个常量,换行和空格private static final String CRLF="\r\n";//换行private static final String BLANK=" ";//空格//构造方法public Response() {headInfo=new StringBuilder();content=new StringBuilder();}//带参构造方法public Response(OutputStream os){this();//调用本类的无参构造方法try {bw=new BufferedWriter(new OutputStreamWriter(os, "utf-8"));} catch (UnsupportedEncodingException e) {headInfo=null;}}//构造正文部分public Response print(String info){content.append(info);try {length+=info.getBytes("utf-8").length;} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}return this;}public Response println(String info){content.append(info).append(CRLF);try {length+=(info+CRLF).getBytes("utf-8").length;} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();}return this;}//构造响应头private void createHeadInfo(int code){headInfo.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);switch (code) {case 200:headInfo.append("OK");break;case 500:headInfo.append("SERVER ERROR");break;default:headInfo.append("NOT FOUND");break;}headInfo.append(CRLF);headInfo.append("Content-Type:text/html;charset=utf-8").append(CRLF);headInfo.append("Content-Length:"+length).append(CRLF);headInfo.append(CRLF);}/*** 推送到客户机的浏览器* @param code*/public void pushToClient(int code){if (headInfo==null) {code=500;}try {//调用本类中的构造响应头this.createHeadInfo(code);bw.write(headInfo.toString());bw.write(content.toString());bw.flush();this.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void close(){IOCloseUtil.closeAll(bw);}
}
12.2 编写相应的 Servlet 构造响应内容
package com.bjsxt.servlet;import com.bjsxt.server.Request;
import com.bjsxt.server.Response;public abstract class Servlet { //是所有的请求的Servlet的父类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;
}
LogServlet.java
package com.bjsxt.servlet;import com.bjsxt.server.Request;
import com.bjsxt.server.Response;public class LoginServlet extends Servlet {@Overridepublic void doGet(Request req, Response rep) throws Exception {//获取请求参数String name=req.getParameter("username");String pwd=req.getParameter("pwd");if(this.login(name, pwd)){//调用响应中的构建内容的方rep.println(name+"登录成功");}else{rep.println(name+"登录失败,对不起,账号或密码不正确");}}private boolean login(String name,String pwd){if ("bjsxt".equals(name)&&"123".equals(pwd)) {return true;}return false;}@Overridepublic void doPost(Request req, Response rep) throws Exception {// TODO Auto-generated method stub}
}
12.3 启动服务器进行测试
13. 封装分发器_实现多线程
Dispatcher.java
package com.bjsxt.server;import java.io.IOException;
import java.net.Socket;import com.bjsxt.servlet.Servlet;
import com.bjsxt.util.IOCloseUtil;/*** 一个请求与响应就是一个Dispatcher* @author Administrator**/
public class Dispatcher implements Runnable {private Request req;private Response rep;private Socket client;private int code=200;//状态码//构造方法初始化属性public Dispatcher(Socket client) {//将局部变量的值赋给成员变量this.client=client;try {req=new Request(this.client.getInputStream());rep=new Response(this.client.getOutputStream());} catch (IOException e) {code=500;return ;}}@Overridepublic void run() {//根据不同的url创建指定的Servlet对象System.out.println(req.getUrl());Servlet servlet=WebApp.getServlet(req.getUrl());if (servlet==null) {this.code=404;}else{//调用相应的Servlet中的service方法try {servlet.service(req,rep);} catch (Exception e) {this.code=500;}}//将响应结果推送到客户机的浏览器rep.pushToClient(code);IOCloseUtil.closeAll(client);}}
14. 整合最终版
14.1 查缺补漏完善项目
FaviconServlet.java
package com.bjsxt.servlet;import com.bjsxt.server.Request;
import com.bjsxt.server.Response;public class FaviconServlet extends Servlet {@Overridepublic void doGet(Request req, Response rep) throws Exception {// TODO Auto-generated method stub}@Overridepublic void doPost(Request req, Response rep) throws Exception {// TODO Auto-generated method stub}}
15. 总结和期望
15.1 项目总结
涉及知识点
- 集合
- IO
- 多线程
- 网络编程
- 面向对象
- 反射
- XML
- HTML
15.2 项目思路
- 先分析
- 搭建项目框架
- 一步一步实现,先从会的开始实现
- 整体思路,宏观使用面向对象,微观使用面向过程
- 注重代码的调试
15.3 思想是从键盘中敲出来的
- 多注重实战
- 学知识不是目的,目的是对知识的综合运用,才能将知识
转化为能力 - 不要气馁,每天进步一点点,人最大的敌人是自己,今天
的自己比昨天的自己有进步,就是成功。 - 今天所有的付出,都会在明天以 RMB 的形式还给你。
- 加油!!!
附源码:
链接:https://pan.baidu.com/s/1upUHY0bGXmLfLZKOt5ICxg
提取码:pfz1
Java—手写服务器相关推荐
- java 手写 jvm高性能缓存
java 手写 jvm高性能缓存,键值对存储,队列存储,存储超时设置 缓存接口 1 package com.ws.commons.cache; 2 3 import java.util.functio ...
- Java 手写一个SQL分页
Java手写一个类似PageHelper的分页SQL 目前分页插件众所周知的莫过于和mybatis完美融合的PageHelper了,简单两行代码就实现了sql分页,配合PageInfo类,将数据总数量 ...
- Java手写线程池-第一代(原创)
个人简介 作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门. 文章目录 个人简介 Java手写线程池(第一代) ...
- Java 手写签字去除背景 背景透明
Java 手写签字去除背景 背景透明 /*** 白底照片去除白底 形成透明底图片* @param file 需要去除背景的图片* @param Path 去除背景后保存图片的路径* @return t ...
- 纯jsp实现评论功能_自己实现的java手写tomcat
这是一个java写的模拟tomcat工作原理的demo,是一个极简的tomcat服务器,也是我们培训班(邦邦IT教育)的讲义,是整个j2ee培训的精髓,理解了这个demo其实后面的很多东西都是可以自学 ...
- 用Java手写一个微型下载资源网站
文章目录 手写一个微型下载资源网站[Java实现用户注册.登陆.下载功能] 一.技术栈 二.流程分析图 注册 登陆 下载 三.案例实现效果 首页 注册 登陆 下载网主页 壁纸下载 书籍下载 影视下载 ...
- java手写一个分页的方法_java web手写实现分页功能
现在很多流行的框架,都可以很快的把分页效果做出来,但是作为一名程序员你必须得知道手写分页的流程: 场景效果: 一.分页的思路 首先我们得知道写分页代码的思路,保持思路清晰,才能行云流水的去写代码,其实 ...
- java手写实现BST
难易程度:★★★ 重要性:★★★★★ 今日头条的面试中有过要求:手写实现BST import java.util.*;public class MyBSTImpl {// BST中的节点TreeNod ...
- Java手写HashSet
一:引言 HashSet类继承于 Set接口 其方法均可被直接调用,不用手写,本篇敲的码是为了熟悉底层原理,HashMap的特点:无序,无重复.其底层用的也是map<key,value>容 ...
最新文章
- android 多个绑定事件,Android RxJava 实际应用讲解:联合判断多个事件
- 快速人脸验证--MobileFaceNets: Efficient CNNs for Accurate Real-time Face Verification on Mobile Devices
- Linux+nfs配置开机启动,linux NFS配置:NFS开机自动启用及其原理
- WSDM 2021 | 基于双向推理的多跳知识库问答技术
- (转)静态变量和全局变量的区别
- 克劳斯比的零缺陷——《可以量化的管理学》
- Notepad++ WebEdit插件
- a4如何打印双面小册子_a4如何排版打印双面小册子?
- 2013年锦绣中华民俗村迷情聊斋夜
- 计算日期在当月是第几周-【自然周(每月第一个周一为该月第一周)做法以及1号为第一周做法】
- NetLimiter
- python查询12306余票_python命令行查询12306火车票
- dhcp设置(Padavan dhcp设置)
- Alarm Clock C/C++ Version
- MPB:使用QIIME 2分析微生物组16S rRNA基因扩增子测序数据(视频)
- Pytorch实现Bert模型
- 光敏电阻规格型号的含义解读研究总结
- html css超链接字体颜色,HTML-CSS设置超链接颜色字体
- 2018中国智造金长城奖:创新能力与行业竞争力并重
- XILINX DSP Slice功能特点
热门文章
- phpstrom函数注释模板_PHPSTORM模板变量注释
- 2020科目一考试口诀_2021年科目一考试口诀
- Compose Android 开发终极挑战赛: 天气应用
- 惠普前高管欧明哲任联想台湾区总经理
- Introduce Python to data sience/Python 在数据科学中的应用
- 启动weblogic提示BEA-000386错误,java.lang.NumberFormatException: null
- 哔哩哔哩(B站)的前端之路
- python制作猜数字小游戏
- python爬虫《向往的生活》豆瓣短评,来看看Henry大华的路人评价变化~
- mysql数据库应用与开发姜桂洪 课后答案_数据库应用与开发姜桂洪课后答案