1.Socket编程
1)创建服务器+指定端口 ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
2)接收客户端连接 阻塞式
3)发送数据+接收数据
package com.yijiuju.learn.server; import com.yijiuju.learn.util.CloseUtil; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /* * 创建服务器并启动 * 请求并相应 * 加入多线程 * */ public class Server { private ServerSocket server; private boolean isShutDown = false; public static void main(String[] args) { Server server = new Server(); server.star(); } /* * 默认启动方法 * */ public void star() { star(8888); } /* * 指定端口的启动方法 * */ public void star(int port) { try { //1.创建服务器端,并指定端口 server = new ServerSocket(port); //2.接收多个客户端连接,启用多线程 this.receive(); } catch (IOException e) { stop(); } } /* * 接收客户端 * */ private void receive() { try { while (!isShutDown) { //接收客户端连接,获取套接字 阻塞式 Socket client =server.accept(); //启用多线程将请求和响应独立,实现发送和接收数据的独立 new Thread(new Dispatcher(client)).start(); } } catch (IOException e) { isShutDown=true; } } /* * 停止服务 * */ public void stop() { isShutDown = true; CloseUtil.closeAll(server); } }
2.html
格式
3.HTTP协议
1)HTTP请求报文
封装请求
package com.yijiuju.learn.server; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.*; /* * 封装请求 * */ public class Request { //请求方式 private String method; //请求资源 private String url; //请求参数 private Map<String, List<String>> parameterMapValues; //内部 private static final String CRLF = "\r\n"; private InputStream is; private String requestInfo; public Request() { method = ""; url = ""; parameterMapValues = new HashMap<>(); } public Request(InputStream is) { this(); this.is = is; byte[] data = new byte[20480]; try { int len = is.read(data); requestInfo = new String(data, 0, len); } catch (IOException e) { return; } //分析请求信息 parseRequestInfo(); } /* * 分析请求信息 * */ private void parseRequestInfo() { if (null == requestInfo || (requestInfo = requestInfo.trim()).equals("")) { return; } /* * ========================== * 从信息的首行分解出:请求方式 请求路径 请求参数(get可能存在) * 如:GET /index.html?uname=123&pwd=456 HTTP/1.1 * 如果是POST方式,请求参数可能在最后正文中 * * ========================== * */ String paramSring = "";//接收请求参数 //1.获取请求方式 String firstLine = requestInfo.substring(0, requestInfo.indexOf(CRLF)); int idx= firstLine.indexOf("/"); this.method = firstLine.substring(0, idx).trim(); String urlStr = firstLine.substring(idx,firstLine.indexOf("HTTP/")); if (this.method.equalsIgnoreCase("POST")) { this.url = urlStr; paramSring = requestInfo.substring(requestInfo.lastIndexOf(CRLF)).trim(); } else if (this.method.equalsIgnoreCase("GET")) { if (urlStr.contains("?")) { //是否参数 String[] urlArray = urlStr.split("\\?"); this.url = urlArray[0]; paramSring = urlArray[1];//接收请求参数 } else { this.url = urlStr; } } //2.将请求参数封装到Map中 //不存在请求参数 if (paramSring.equals("")) { return; } parseParams(paramSring); } private void parseParams(String paramString) { //分割 将字符串转成数组 StringTokenizer token = new StringTokenizer(paramString, "&"); while (token.hasMoreElements()) { String keyValue = token.nextToken(); String[] keyValues = keyValue.split("="); if (keyValues.length == 1) { keyValues = Arrays.copyOf(keyValues, 2); keyValues[1] =null; } String key = keyValues[0].trim(); String value = (null==keyValues[1])?null:decode(keyValues[1].trim(),"utf-8"); //转换成Map if (!parameterMapValues.containsKey(key)) { parameterMapValues.put(key, new ArrayList<>()); } List<String> values = parameterMapValues.get(key); values.add(value); } } /* * 根据页面的name 获取对应的多个值 * * */ public String[] getParameterValues(String name) { List<String> values = null; if ((values = parameterMapValues.get(name)) == null) { return null; } else { return values.toArray(new String[0]); } } /* * 根据页面的name 获取对应的单个值 * * */ public String getParameter(String name) { String[] values = getParameterValues(name); if (null == values) { return null; } return values[0]; } /* * 解决中文问题 * */ private String decode(String value, String code) { try { return URLDecoder.decode(value, code); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
2)HTTP响应报文
封装响应
package com.yijiuju.learn.server; import com.yijiuju.learn.util.CloseUtil; 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 { //两个常量 private static final String CRLF = "\r\n"; private static final String BLANK = " "; //正文 private StringBuilder content; //存储头信息 private StringBuilder headInfo; //存储正文长度 private int len =0; //流 private BufferedWriter bw; 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; } } /* * 构建响应头 * */ private void createHeadInfo(int 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"); break; case 500: headInfo.append("Server Error"); break; } headInfo.append(CRLF); //2)响应头(Response Head) headInfo.append("Server:Apache Tomcat/6.0.12").append(CRLF); headInfo.append("Date:").append(new Date()).append(CRLF); headInfo.append("Content-type:text/html;charset=gbk:").append(CRLF); //正文长度 字节长度 headInfo.append("Content-Length:").append(len).append(CRLF); //正文之前 headInfo.append(CRLF); } /* * 构建正文 * */ public Response println(String info) { content.append(info).append(CRLF); len += (info+CRLF).getBytes().length; return this; } /* * 推送到客户端 * */ void pushToClient(int code) throws IOException { if (null == headInfo) { code = 500; } createHeadInfo(code); //头信息+分隔符 bw.append(headInfo.toString()); //正文 bw.append(content.toString()); bw.flush(); } public void close() { CloseUtil.closeAll(bw); } }
分发器:创建线程,实现请求与响应独立进行
package com.yijiuju.learn.server; import com.yijiuju.learn.servlet.Servlet; import com.yijiuju.learn.util.CloseUtil; import java.io.IOException; import java.net.Socket; /* * 一个请求与响应 就一个此对象 * */ 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()); rep = new Response(client.getOutputStream()); } catch (IOException e) { code = 500; return; } } @Override public void run() { try { Servlet serv = WebApp.getServlet(req.getUrl()); if (null == serv) { this.code = 404; //找不到资源 } else { serv.service(req,rep); } rep.pushToClient(code); //推送到客户端 }catch (Exception e) { this.code = 500; } try { rep.pushToClient(500); } catch (IOException e) { e.printStackTrace(); } CloseUtil.closeAll(client); } }
创建Servlet,用来处理不同的请求对应不同的响应
package com.yijiuju.learn.servlet; import com.yijiuju.learn.server.Request; import com.yijiuju.learn.server.Response; /** * Created by heyuying on 18-6-7 */ /* * 抽象为一个父类 * */ public abstract class Servlet { public void service(Request req, Response rep) throws Exception { this.doGet(req,rep); this.doPost(req,rep); } protected abstract void doGet(Request req, Response rep) throws Exception; protected abstract void doPost(Request req, Response rep) throws Exception; }注册的响应,继承Servlet
package com.yijiuju.learn.servlet; import com.yijiuju.learn.server.Request; import com.yijiuju.learn.server.Response; 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>"); rep.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"); rep.println("<title>返回注册</title>"); rep.println("</head><body>"); rep.println("你的用户名为:" + req.getParameter("uname")); rep.println("</body></html>"); } }
登录的响应,继承Servlet
package com.yijiuju.learn.servlet; import com.yijiuju.learn.server.Request; import com.yijiuju.learn.server.Response; 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("<html><head>"); rep.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"); rep.println("<title>登录</title>"); rep.println("</head><body>"); rep.println("登录成功"); rep.println("</body></html>"); } else { rep.println("<html><head>"); rep.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">"); rep.println("<title>登录</title>"); rep.println("</head><body>"); rep.println("登录失败"); rep.println("</body></html>"); } } @Override public void doPost(Request req, Response rep) throws Exception { } public boolean login(String name, String pwd) { return name.equals("bjsxt") && pwd.equals("123456"); } }
4.反射
创建ServletContext,用来存储映射关系
package com.yijiuju.learn.server; import java.util.HashMap; import java.util.Map; /* * Servlet上下文 * */ public class ServletContext { //为每个servlet取个别名 //login --> com.yijiuju.learn.servlet.LoginServlet private Map<String, String> servlet; //url --> login // /log -->login // login-->login private Map<String, String> mapping; ServletContext() { servlet = new HashMap<>(); mapping = new HashMap<>(); } public 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; } }
5.XML解析
<?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>login</servlet-name> <servlet-class>com.yijiuju.learn.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/log</url-pattern> </servlet-mapping> <servlet> <servlet-name>reg</servlet-name> <servlet-class>com.yijiuju.learn.servlet.RegisterServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>reg</servlet-name> <url-pattern>/reg</url-pattern> </servlet-mapping> </web-app>
webApp用来将解析XML文件,将url-->login-->到loginServlet类的全限定名,做成映射关系
package com.yijiuju.learn.server; import com.yijiuju.learn.servlet.Servlet; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.util.List; import java.util.Map; public class WebApp { private static ServletContext context; static { //1.获取解析工厂 SAXParserFactory factory = SAXParserFactory.newInstance(); //2.获取解析器 SAXParser sax = null; try { sax = factory.newSAXParser(); //指定XML+处理器 WebHandler handler = new WebHandler(); sax.parse(Thread.currentThread(). getContextClassLoader(). getResourceAsStream("WEB-INF/web"), handler); //java包下的相对路径 //将list转成Map context = new ServletContext(); // <url-pattern> <servlet-name> Map<String, String> mapping = context.getMapping(); for (Mapping map : handler.getMappingList()) { List<String> urls = map.getUrlPattern(); for (String url : urls) { mapping.put(url, map.getName()); } } context.getServlet(); //<servlet-name> <servlet-class> Map<String, String> servlet = context.getServlet(); for (Entity entity : handler.getEntityList()) { servlet.put(entity.getName(), entity.getClz()); } } catch (Exception e) { e.printStackTrace(); } } //url--->login-->类的全限定名com.yijiuju.learn.servlet.LoginServlet,通过反射-->LoginServlet public static Servlet getServlet(String url) throws ClassNotFoundException, IllegalAccessException, InstantiationException { if (null == url || (url = url.trim()).equals("")) { return null; } //根据字符串(完整路径)创建对象 String name = context.getServlet().get(context.getMapping().get(url)); return (Servlet) Class.forName(name).newInstance(); //确保空构造存在 } }
处理器
package com.yijiuju.learn.server; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; import java.util.List; public class WebHandler extends DefaultHandler { private List<Entity> entityList; private List<Mapping> mappingList; private Entity entity; private Mapping mapping; private String beginTag; private boolean isMap; public 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; } @Override public void startDocument() throws SAXException { //文档解析开始 entityList = new ArrayList<>(); mappingList = new ArrayList<>(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //开始元素 if (null != qName) { beginTag = qName; if (qName.equals("servlet")) { isMap = false; entity = new Entity(); } else if (qName.equals("servlet-mapping")) { isMap = true; mapping = new Mapping(); } } } @Override public void characters(char[] ch, int start, int length) throws SAXException { //处理内容 if (null != beginTag) { String str = new String(ch, start, length); if (isMap) { if (beginTag.equals("servlet-name")) { mapping.setName(str); } else if (beginTag.equals("url-pattern")) { mapping.getUrlPattern().add(str); } } else { if (beginTag.equals("servlet-name")) { entity.setName(str); } else if (beginTag.equals("servlet-class")) { entity.setClz(str); } } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { //结束元素 if (null != qName) { if (qName.equals("servlet")) { entityList.add(entity); } else if (qName.equals("servlet-mapping")) { mappingList.add(mapping); } } beginTag = null; } @Override public void endDocument() throws SAXException { //文档解析结束 } }
package com.yijiuju.learn.server; /* <servlet> <servlet-name>login</servlet-name> <servlet-class>cn.hyy.test.net.httpServer.server4.LoginServlet</servlet-class> </servlet> * */ public class Entity { private String name; private String clz; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getClz() { return clz; } public void setClz(String clz) { this.clz = clz; } }
package com.yijiuju.learn.server; import java.util.ArrayList; import java.util.List; /* * <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>login</servlet-name> <url-pattern>/log</url-pattern> </servlet-mapping> 一对多 * */ public class Mapping { private String name; private List<String> urlPattern; public Mapping() { urlPattern = new ArrayList<>(); } public 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; } }
package com.yijiuju.learn.util; import java.io.Closeable; import java.io.IOException; /* * 关闭流的方法 * */ public class CloseUtil { public static void closeAll(Closeable...io) { for (Closeable temp : io) { if (null != temp) { try { temp.close(); } catch (IOException e) { //e.printStackTrace(); } } } } }