前言:

在看完尚学堂JAVA300中讲解如何实现一个最简单的httpserver部分的视频之后,

一、前置知识

1.HTTP协议

  当前互联网网页访问主要采用了B/S的模式,既一个浏览器,一个服务器,浏览器向服务器请求资源,服务器回应请求,浏览器再将接收到的回应解析出来展现给用户。这一问一答的过程可以抽象成浏览器向服务器发送一个Request然后服务器返回一个Response的过程

  其中Request和Reponse在HTTP中有有具体的格式要求

  • 一个Request的例子
Method Path-to-resource Http/Version-number
User-agent 浏览器的类型Accept-charset 用户首选的编码字符集……
Accept 浏览器接受的MIME类型
Accept language 用户选择的接受语言
Accept-charset 用户首选的编码字符集
空行
Option Request Body

如表格所示,第一行首先是请求方式,后跟请求资源路径(url)如果请求方式是GET,则请求参数跟在请求路径里,以?分开,然后一个空格,后跟HTTP版本。后几行为固定格式内容。如果请求方式为POST,则隔一个空行后,跟的请求体的内容,里面有请求参数。

  • 一个Response内容
Http/Version-number Statuscode message
Server 服务器的类型信息
Content-type 响应的MIME类型信息
Content-length 被包含在相应类型中的字符数量
空行
Option Response Body

和Request类似,同样包含响应头和响应体两部分。第一行的Statuscode标识了状态参数,404表示请求资源没有找到,500表示服务器错误,200表示成功。响应体里面包含的是响应内容

该部分具体可以参考博文:

2.JAVA网络编程

在Java中提供了两种网络传输方式的实现,面向数据的UDP传输方式和面向连接的TCP传输方式,这里选用TCP方式。

在TCP方式中,服务端的编写主要依靠类Socket和类ServerSocket,通过这两个类可以建立一个TCP连接。

具体方法是:

  1. 首先新建一个ServerSocket对象server,指明端口号信息
  2. 然后使用server.accept()函数监听端口,监听到连接以后返回一个Socket对象
  3. 通过这个Socket对象,以及里面的输入流和输出流,我们就可以获得传过来的信息以及返回信息

二、具体实现

1.服务器类

首先我们需要建立一个服务器类,负责不断监听端口,获得Socket,然后再利用获得Socket新建一个分发器(Dispatcher),该分发器支持多线程,所以一个分发器专门负责处理一个连接,而服务器只负责不断接收连接,建立分发器。

具体代码如下:

  1. package top.dlkkill.httpserver;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.net.ServerSocket;
  5. import java.net.Socket;
  6. public class Server {
  7. private boolean flag=false;
  8. private ServerSocket server;
  9. public static void main(String[] args) throws IOException {
  10. Server myserver=new Server(8888);
  11. myserver.start();
  12. }
  13. public Server(int port) {
  14. try {
  15. server=new ServerSocket(port);
  16. } catch (IOException e) {
  17. this.stop();
  18. }
  19. }
  20. public void start() throws IOException {
  21. this.flag=true;
  22. this.revice(server);
  23. }
  24. public void revice(ServerSocket server) throws IOException {
  25. while(flag) {
  26. Socket client=server.accept();
  27. new Thread(new Dispatcher(client)).start();
  28. }
  29. }
  30. public void stop() {
  31. flag=false;
  32. }
  33. }

2.封装Request和Response

为了方便解析Request和返回Response,我们需要抽象出两个对象(Request类和Response对象)。

首先封装Request

  Request对象的作用是解析请求信息,将请求方式,请求资源路径,请求参数解析分离出来,构建一个Request对象我们需要传入一个参数------>输入流。这样我们就可以从输入流中读入请求信息然后开始解析。

  1. package top.dlkkill.httpserver;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.UnsupportedEncodingException;
  5. import java.net.Socket;
  6. import java.util.ArrayList;
  7. import java.util.Arrays;
  8. import java.util.HashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11. import java.util.StringTokenizer;
  12. public class Request {
  13. private String url;
  14. private String method;
  15. private String info;
  16. private Map<String,List<String>> paramterMapValues;
  17. private InputStream is;
  18. public static final String CRLF="\r\n";
  19. public static final String BANK=" ";
  20. private Request() {
  21. url=null;
  22. method=null;
  23. is=null;
  24. info=null;
  25. paramterMapValues=new HashMap<String,List<String>>();
  26. }
  27. public Request(InputStream is) {
  28. this();
  29. this.is=is;
  30. try {
  31. create();
  32. } catch (IOException e) {
  33. this.is=null;
  34. System.out.println("Request 创建错误");
  35. return;
  36. }
  37. }
  38. public String getMethod() {
  39. return this.method;
  40. }
  41. public String getUrl() {
  42. return url;
  43. }
  44. private void create() throws IOException{
  45. getInfo();
  46. getUrlAndParamter();
  47. }
  48. /**
  49. * 根据页面name获取对应所有值
  50. * @return String[]
  51. */
  52. public String[] getparamterValues(String name) {
  53. List<String> paramterValues=null;
  54. if((paramterValues=paramterMapValues.get(name))==null) {
  55. return null;
  56. }else {
  57. return paramterValues.toArray(new String[0]);
  58. }
  59. }
  60. /**
  61. * 根据页面name获取单个值
  62. * @return String[]
  63. */
  64. public String getparamterValue(String name) {
  65. String values[]=getparamterValues(name);
  66. if(values==null)
  67. return null;
  68. else
  69. return values[0];
  70. }
  71. /**
  72. * 得到请求信息
  73. * @throws IOException
  74. */
  75. private void getInfo() throws IOException {
  76. byte bytes[]=new byte[20480];
  77. int len=is.read(bytes);
  78. info=new String(bytes,0,len);
  79. }
  80. /**
  81. * 处理得到url资源请求路径和请求参数值
  82. * @return
  83. */
  84. private void getUrlAndParamter(){
  85. String firstline=info.substring(0,info.indexOf(CRLF));
  86. //System.out.println("FirstLine: "+firstline);
  87. String paramter="";
  88. this.method=firstline.substring(0,firstline.indexOf("/")).trim();
  89. String tempurl=
  90. firstline.substring(firstline.indexOf("/"),firstline.indexOf("HTTP/")).trim();
  91. System.out.println("tempurl: "+tempurl);
  92. if(this.method.equalsIgnoreCase("post")) {
  93. this.url=tempurl;
  94. paramter=info.substring(info.lastIndexOf(CRLF)).trim();
  95. }else {
  96. if(tempurl.contains("?")) {
  97. //split函数里面的参数实际上需要的是正则表达式,普通字符串还好,?号是特殊字符
  98. String[] urlarry=tempurl.split("\\?");
  99. this.url=urlarry[0];
  100. paramter=urlarry[1];
  101. }else {
  102. this.url=tempurl;
  103. }
  104. }
  105. //解析参数
  106. parseParmter(paramter);
  107. return;
  108. //System.out.println(this.url);
  109. //System.out.println(paramter);
  110. }
  111. /**
  112. * 解析请求参数,转换成键值对形式
  113. * @param str
  114. */
  115. private void parseParmter(String str) {
  116. if(str==null||str.equals("")||str.trim().equals(""))
  117. return;
  118. StringTokenizer st=new StringTokenizer(str,"&");
  119. while(st.hasMoreTokens()) {
  120. String temp=st.nextToken();
  121. String[] KeyAndValues=temp.split("=");
  122. if(KeyAndValues.length==1) {
  123. KeyAndValues=Arrays.copyOf(KeyAndValues,2);
  124. KeyAndValues[1]=null;
  125. }
  126. String key=KeyAndValues[0].trim();
  127. String value=KeyAndValues[1]==null?null:KeyAndValues[1].trim();
  128. if(!paramterMapValues.containsKey(KeyAndValues[0])){
  129. paramterMapValues.put(key,new ArrayList<String>());
  130. }
  131. paramterMapValues.get(key).add(decode(value, "gbk"));
  132. }
  133. }
  134. /**
  135. * 解决中文编码问题
  136. * @param value
  137. * @param code
  138. * @return
  139. */
  140. private String decode(String value,String code) {
  141. try {
  142. return java.net.URLDecoder.decode(value, code);
  143. } catch (UnsupportedEncodingException e) {
  144. }
  145. return null;
  146. }
  147. }

然后我们进行封装Response.

  Response主要分为两部分(响应头和响应体)

  构建Response需要传入通过Socket获得的输出流,利用这个输出流我们才可以写回信息

响应头格式较为固定,所有我们在Response中应该有一个私有方法根据响应码自动构建响应头信息。

  然后我们还需要一个共有方法void println(String msg),其他类利用该方法写入对应的响应体部分。

最后需要有一个send()方法,自动整合响应体和响应体内容,并将所有内容发送出去。

  1. package top.dlkkill.httpserver;
  2. import java.io.BufferedOutputStream;
  3. import java.io.BufferedReader;
  4. import java.io.BufferedWriter;
  5. import java.io.IOException;
  6. import java.io.OutputStream;
  7. import java.io.OutputStreamWriter;
  8. import java.net.Socket;
  9. import java.util.Date;
  10. public class Response {
  11. public static final String CRLF="\r\n";
  12. public static final String BANK=" ";
  13. private StringBuilder headerinfo;
  14. private StringBuilder content;
  15. private BufferedWriter wr;
  16. private int len;
  17. public Response() {
  18. // TODO Auto-generated constructor stub
  19. headerinfo=new StringBuilder();
  20. content=new StringBuilder();
  21. if(content==null)
  22. System.out.println("error");
  23. len=0;
  24. }
  25. public Response(OutputStream os){
  26. this();
  27. wr=new BufferedWriter(new OutputStreamWriter(os));
  28. }
  29. public void createHeaderinfo(int code) {
  30. System.out.println("code is "+code);
  31. headerinfo.append("HTTP/1.1").append(BANK);
  32. switch (code) {
  33. case 200:
  34. headerinfo.append(code).append(BANK).append("OK");
  35. break;
  36. case 404:
  37. headerinfo.append(code).append(BANK).append("404 not found");
  38. break;
  39. case 500:
  40. headerinfo.append(code).append(BANK).append("error");
  41. break;
  42. default:
  43. headerinfo.append(code).append(BANK).append("error");
  44. break;
  45. }
  46. headerinfo.append(CRLF);
  47. headerinfo.append("Server:dlkkill server/0.1").append(CRLF);
  48. headerinfo.append("Date:").append(new Date()).append(CRLF);
  49. headerinfo.append("Content-type:text/html;charset=GBK").append(CRLF);
  50. //正文长度,字节长度
  51. headerinfo.append("Content-Length:").append(len).append(CRLF);
  52. //空行分隔符
  53. headerinfo.append(CRLF);
  54. //System.out.println(headerinfo.toString());
  55. }
  56. public Response println(String msg) {
  57. //System.out.println(msg);
  58. if(content==null)
  59. System.out.println(msg);
  60. content.append(msg);
  61. len+=msg.getBytes().length;
  62. return this;
  63. }
  64. public void pushToClient(int code) throws IOException {
  65. if(wr==null) {
  66. code=500;
  67. }
  68. createHeaderinfo(code);
  69. wr.write(headerinfo.toString());
  70. wr.write(content.toString());
  71. wr.flush();
  72. }
  73. }

3.创建分发器

  我们需要有一个类专门一对一处理一个连接,并且该类要支持多线程。所以我们抽象出来一个分发器类。该类负责专门一对一处理一个连接

  该类拥有一个私有属性Socket client。利用该属性,该类可以创建一个Request和一个Response,然后该类再根据请求的url,利用Webapp类(该类用于生成处理不同请求的不同的类)获得对应的类,启动该类进行处理。

最后该类再调用Response提供的pushToClient方法将所有信息推送给浏览器,然后关闭连接。

具体代码如下:

  1. package top.dlkkill.httpserver;
  2. import java.io.IOException;
  3. import java.net.Socket;
  4. public class Dispatcher implements Runnable {
  5. private Socket client;
  6. private Request req;
  7. private Response rep;
  8. private int code=200;
  9. public Dispatcher(Socket client) {
  10. this.client=client;
  11. try {
  12. req=new Request(client.getInputStream());
  13. rep=new Response(client.getOutputStream());
  14. } catch (IOException e) {
  15. code=500;
  16. }
  17. }
  18. @Override
  19. public void run() {
  20. System.out.println(req.getUrl()+" ***");
  21. Servlet servlet=Webapp.getServlet(req.getUrl());
  22. if(servlet!=null)
  23. servlet.service(req, rep);
  24. else
  25. code=404;
  26. try {
  27. rep.pushToClient(code);
  28. } catch (IOException e) {
  29. code=500;
  30. }
  31. try {
  32. rep.pushToClient(code);
  33. } catch (IOException e) {
  34. }
  35. CloseUtil.closeAll(client);
  36. }
  37. }

4.抽象处理类Servlet

首先我们将该处理类抽象成一个abstract Servlet类,该类负责根据不同的请求进行处理

该抽象类提供多个抽象方法doGet、doPost方法等分别处理不同的请求,传入参数为(Request,Response)这两个参数,在该方法内进行处理。

提供一个service方法根据不同的请求调用不同的方法

具体代码:

  1. package top.dlkkill.httpserver;
  2. import java.net.Socket;
  3. public abstract class Servlet {
  4. public Servlet() {
  5. }
  6. public void service(Request req,Response rep) {
  7. if(req.getMethod().equalsIgnoreCase("get")) {
  8. this.doGet(req, rep);
  9. }else {
  10. this.doPost(req, rep);
  11. }
  12. }
  13. public abstract void doGet(Request req,Response rep);
  14. public abstract void doPost(Request req,Response rep);
  15. }

一个实例:

  1. package top.dlkkill.httpserver;
  2. public class loginServlet extends Servlet {
  3. @Override
  4. public void doGet(Request req, Response rep) {
  5. rep.println("<head>" +
  6. " <title>test</title>" +
  7. "</head>" +
  8. "<body>" +
  9. "<p>hellow</p>"+
  10. "<form action=\"http://localhost:8888/index\" method=\"POST\">" +
  11. "name: <input type=\"text\" name=\"name\">" +
  12. "password: <input type=\"password\" name=\"pwd\">" +
  13. "<input type=\"submit\" value=\"submit\">" +
  14. "</form>" +
  15. "</body>");
  16. }
  17. @Override
  18. public void doPost(Request req, Response rep) {
  19. this.doGet(req, rep);
  20. }
  21. }

5.处理类生成工厂

为了编程的灵活性,我们将该httpserver写出可以根据一个xml配置文件知道有多少种分别处理什么url请求的类,该xml就负责记录这种映射关系

首先需要一个ServletContext类,该类有两个属性private Map<String,String> servlet和private Map<String,String> map,分别用来记录名称到类存储地址之间的映射和url到名称之间的映射

  1. package top.dlkkill.httpserver;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class ServletContext {
  5. //名称到类存储地址之间的映射
  6. private Map<String,String> servlet;
  7. //url到名称之间的映射
  8. private Map<String,String> map;
  9. public void setServlet(Map<String, String> servlet) {
  10. this.servlet = servlet;
  11. }
  12. public void setMap(Map<String, String> map) {
  13. this.map = map;
  14. }
  15. public ServletContext() {
  16. servlet=new HashMap<String, String>();
  17. map=new HashMap<String, String>();
  18. }
  19. public Map<String, String> getServlet() {
  20. return servlet;
  21. }
  22. public Map<String, String> getMap() {
  23. return map;
  24. }
  25. }

然后需要一个Webapp类

该类负责读入xml文件并且进行解析,根据xml文件配置的内容,为分发器生成不同的servlet处理类。

生成不同的类利用的Java的类加载机制,可以在代码中获取class信息然后new一个类出来

解析xml文件我们使用的是SAXParser解析器,为了利用该解析器,我们还需要实现一个继承于DefaultHandler的类

实现代码:

  1. package top.dlkkill.httpserver;
  2. import java.io.IOException;
  3. import java.util.List;
  4. import java.util.Map;
  5. import javax.xml.parsers.ParserConfigurationException;
  6. import javax.xml.parsers.SAXParser;
  7. import javax.xml.parsers.SAXParserFactory;
  8. import org.xml.sax.SAXException;
  9. public class Webapp {
  10. private static ServletContext servletcontext;
  11. static{
  12. servletcontext=new ServletContext();
  13. Map<String,String> servlet=servletcontext.getServlet();
  14. Map<String,String> map=servletcontext.getMap();
  15. // servlet.put("index", "top.dlkkill.httpserver.indexServlet");
  16. // servlet.put("login", "top.dlkkill.httpserver.loginServlet");
  17. // map.put("/login", "login");
  18. // map.put("/index", "index");
  19. SAXParserFactory parserfactor=SAXParserFactory.newInstance();
  20. WebHandler hd=new WebHandler();
  21. SAXParser parser;
  22. try {
  23. parser=parserfactor.newSAXParser();
  24. if(null==Thread.currentThread().getContextClassLoader().getResourceAsStream("top/dlkkill/httpserver/web.xml"))
  25. System.out.println("error");
  26. parser.parse(
  27. Thread.currentThread().getContextClassLoader()
  28. .getResourceAsStream("top/dlkkill/httpserver/web.xml"),
  29. hd);
  30. List<Entity> entityList=hd.getEntityList();
  31. List<Mapping> mappingList=hd.getMappingList();
  32. for (Mapping mapping : mappingList) {
  33. String name=mapping.getName();
  34. List<String> urlList=mapping.getUrl();
  35. for (String url:urlList) {
  36. map.put(url, name);
  37. }
  38. }
  39. for (Entity entity:entityList) {
  40. String servletname=entity.getName();
  41. String clz=entity.getClz();
  42. servlet.put(servletname, clz);
  43. }
  44. } catch (ParserConfigurationException | SAXException |IOException e) {
  45. }
  46. }
  47. public static Servlet getServlet(String url) {
  48. Map<String,String> servlet=servletcontext.getServlet();
  49. Map<String,String> map=servletcontext.getMap();
  50. String className=servlet.get(map.get(url));
  51. Servlet temp=null;
  52. Class<?> clz=null;
  53. try {
  54. System.out.println("classname:"+className);
  55. if(className!=null)
  56. clz=Class.forName(className);
  57. } catch (ClassNotFoundException e) {
  58. return null;
  59. }
  60. try {
  61. if(clz!=null)
  62. temp=(Servlet)clz.newInstance();
  63. } catch (InstantiationException e) {
  64. return null;
  65. } catch (IllegalAccessException e) {
  66. return null;
  67. }
  68. return temp;
  69. }
  70. }
  1. package top.dlkkill.httpserver;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.xml.sax.Attributes;
  5. import org.xml.sax.SAXException;
  6. import org.xml.sax.helpers.DefaultHandler;
  7. public class WebHandler extends DefaultHandler {
  8. private List<Entity> entityList;
  9. private List<Mapping> mappingList;
  10. private Entity entity;
  11. private Mapping mapping;
  12. private String tag;
  13. private boolean isMap;
  14. public WebHandler() {
  15. }
  16. public List<Entity> getEntityList() {
  17. return entityList;
  18. }
  19. public List<Mapping> getMappingList() {
  20. return mappingList;
  21. }
  22. @Override
  23. public void startDocument() throws SAXException {
  24. entityList=new ArrayList<Entity>();
  25. mappingList=new ArrayList<Mapping>();
  26. }
  27. @Override
  28. public void endDocument() throws SAXException {
  29. // for (Mapping mapping : mappingList) {
  30. // if(mapping==null)
  31. // continue;
  32. // String name;
  33. // if(mapping.getName()!=null)
  34. // name=mapping.getName();
  35. // else
  36. // name="null";
  37. // List<String> urlList=mapping.getUrl();
  38. // for (String url:urlList) {
  39. // System.out.println(name+"---->"+url);
  40. // }
  41. // }
  42. // for (Entity entity:entityList) {
  43. // String servletname=entity.getName();
  44. // String clz=entity.getClz();
  45. // System.out.println(servletname+"---->"+clz);
  46. // }
  47. }
  48. @Override
  49. public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
  50. //System.out.println("开始处理"+"--->"+qName);
  51. if(null!=qName) {
  52. if(qName.equals("servlet")) {
  53. isMap=false;
  54. entity=new Entity();
  55. }else if(qName.equals("servlet-mapping")){
  56. isMap=true;
  57. mapping=new Mapping();
  58. }
  59. }
  60. tag=qName;
  61. }
  62. @Override
  63. public void endElement(String uri, String localName, String qName) throws SAXException {
  64. //System.out.println("结束处理"+"--->"+qName);
  65. if(null!=qName) {
  66. if(qName.equals("servlet")) {
  67. entityList.add(entity);
  68. }else if(qName.equals("servlet-mapping")){
  69. mappingList.add(mapping);
  70. }
  71. }
  72. tag=null;
  73. }
  74. @Override
  75. public void characters(char[] ch, int start, int length) throws SAXException {
  76. String str=new String(ch, start, length);
  77. //System.out.println("处理中"+"--->"+str);
  78. if(tag!=null&&str!=null&&!str.trim().equals("")) {
  79. if(!isMap) {
  80. if(tag.equals("servlet-name"))
  81. entity.setName(str);
  82. else if(tag.equals("servlet-class"))
  83. entity.setClz(str);
  84. }else {
  85. if(tag.equals("servlet-name"))
  86. mapping.setName(str);
  87. else if(tag.equals("url"))
  88. mapping.getUrl().add(str);
  89. }
  90. }
  91. }
  92. }

6.一个工具类

该类负责关闭连接,连接关闭了那与该连接有关的流也就关闭了

  1. package top.dlkkill.httpserver;
  2. import java.io.Closeable;
  3. public class CloseUtil {
  4. public static void closeAll(Closeable ...io) {
  5. for (Closeable closeable : io) {
  6. try {
  7. if(closeable!=null)
  8. closeable.close();
  9. }catch (Exception e) {
  10. // TODO: handle exception
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. }

利用Java手写简单的httpserver的更多相关文章

  1. Java手写简单HashMap一(包括增加,查找,toString,泛型)

    @Java 300 学习总结 HashMap底层采用实现采用了哈希表,结合了"数组和链表". 原理如图 一.定义HashMap类. 首先需要定义一个节点来存储数据,构成链表结构. ...

  2. Java手写简单Linkedlist一(包括增加,插入,查找,toString,remove功能)

    @Java300 学习总结 一.自定义节点 LinkList底层为双向链表.特点为查询效率低,但增删效率高,线程不安全. 链表数据储存在节点,且每个节点有指向上个和下个节点的指针. 创建ggLinke ...

  3. 教你如何使用Java手写一个基于链表的队列

    在上一篇博客[教你如何使用Java手写一个基于数组的队列]中已经介绍了队列,以及Java语言中对队列的实现,对队列不是很了解的可以我上一篇文章.那么,现在就直接进入主题吧. 这篇博客主要讲解的是如何使 ...

  4. java 手写 jvm高性能缓存

    java 手写 jvm高性能缓存,键值对存储,队列存储,存储超时设置 缓存接口 package com.ws.commons.cache; import java.util.function.Func ...

  5. java手写多级缓存

    多级缓存实现类,时间有限,该类未抽取接口,目前只支持两级缓存:JVM缓存(实现 请查看上一篇:java 手写JVM高性能缓存).redis缓存(在spring 的 redisTemplate 基础实现 ...

  6. 不使用Tomcat,手写简单的web服务

    背景: 公司使用的YDB提供了http的查询数据库服务,直接通过url传入sql语句查询数据-_-||.ydb的使用参照:https://www.cnblogs.com/hd-zg/p/7115112 ...

  7. 教你如何使用Java手写一个基于数组实现的队列

    一.概述 队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表.在具体应用中通常用链表或者数组来实现.队列只允许在后端(称为rear)进行插入操作,在 ...

  8. 手写简单call,apply,bind

    分析一下call的使用方法:call是显示绑定this指向,然后第一个参数是你所指向的this对象,后面跟着多个参数,以逗号隔开 function sum(num1,num2){ return num ...

  9. 《四 spring源码》利用TransactionManager手写spring的aop

    事务控制分类 编程式事务控制          自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false);  // 设置手动控制事务 Hibern ...

随机推荐

  1. Ionic3多个自定义过滤器--管道(pipe)

    往往我们创建自定义管道一般都不止只会创建一个自定义管道,和自定义组件类似,创建多个方式如下. 一.命令行生成管道 ionic g pipe formateDate ionic g pipemoneyD ...

  2. 多个.txt文件合并到一个.txt文件中

    如果想要将多个.txt文件合并到一个.txt文件中,可以先将所有.txt文件放到一个文件夹中,然后使用.bat文件完成任务. 例如,在一个文件夹下有1.txt, 2.txt, 3.txt三个文件,想把 ...

  3. windy数

    windy数指的是相邻两位差至少为2的数.问区间[a,b]中有多少个windy数 调了半个多小时,不过调出来之后对数位dp理解大大加深 #include<iostream> #includ ...

  4. Hibernate处理事务并发问题

    在Hibernate中设置事务的隔离级别.<property name="hibernate.connection.isolation">2</property& ...

  5. Python3学习之路~8.4 利用socket实现文件传送+MD5校验

    利用socket实现文件传送,大约分为如下几步: 1.读取文件名2.检测文件是否存在3.打开文件(别忘了最后关闭文件)4.检测文件大小5.发送文件大小给客户端6.等客户端确认7.开始边读边发数据8.m ...

  6. tar打包

    进入到目录里,执行下面的命令,可以把目录内的所有内容打包 tar -cvf 111.tar *

  7. 干了这杯Java之transient关键字

    看源码的时候,发现transient这个关键字,不甚理解,查找资料发现:不被序列化 疑问: 静态变量是不是不被序列化? public class User implements Serializabl ...

  8. python操作email

    python操作email 参考链接: python官网imaplib: https://docs.python.org/2/library/imaplib.html Python 用IMAP接收邮件 ...

  9. 使用Apache JMeter对SQL Server、Mysql、Oracle压力测试(一)

    前段时间面试被问到了数据库方面的知识:比如选择什么样的数据库,如何优化,怎么加索引,于是想到了自己动手测试一下常用数据库的性能: 第一步,下载好JMeter之后打开运行.话说这个JMeter打开还真是 ...

  10. 006-CSS引入外部字体

    我们制作页面css,设置字体,可能会需要加入一些比较特殊的字体,这时候就需要引入这些字体. 方法如下: @font-face { font-family: KuTang;/*定义字体的名字*/ src ...