Java筆記-13 http服務器

反射入門

  • 把java類中的各種結構(方法、屬性、構造器、類名)映射成一個個的Java對象,利用反射技術可以對一個類進行剖析。框架設計常用反射
  • 可以通過字符串的方式或得類,創建對象
  • 獲得Class對象Class.forName(“包名.類名”),
  • 使用無參構造器創建對象 (Iphone) classObj.getConstructor().newInstance();
package server.basic;

import java.lang.reflect.InvocationTargetException;

public class TestReflection {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //獲取Class對象 三種方式
        // 1 對象.getClass
        Iphone r = new Iphone();
        Class classObj = r.getClass();
        System.out.println(classObj);

        //2 類.class
        classObj = Iphone.class;

        //3 Class.forName("包名.類名")
        classObj = Class.forName("server.basic.Iphone");

        //解析Class對象
        //1 創建對象,java9不推薦
        Iphone t = (Iphone) classObj.newInstance();
        //2 創建對象的另一種方式,使用構造器,無參數構造器和帶參數構造器
        Iphone iphone7 = (Iphone) classObj.getConstructor().newInstance();
        System.out.println(iphone7);
        iphone7 = (Iphone) classObj.getConstructor(double.class).newInstance(1000);
        System.out.println(iphone7);

    }
}

class Iphone{
    double price;
    public Iphone(){
    }
    public Iphone(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Price is "+price;
    }
}

XML解析

  • 可擴展標記語言
  • 解析方式SAX流解析、DOM文檔解析樹。

解析簡單的xml文件

  • SAX解析示例,一個xml有person的標籤,分別有name和age兩個字段。將person作爲類的對象放到容器中
  • xml示例文件
<?xml version="1.0" encoding="UTF-8" ?>
<persons>
    <person>
      <name>至尊寶</name>
      <age>9000</age>
   </person>
   <person>
      <name>白晶晶</name>
      <age>7000</age>
   </person>
</persons>
  • SAX解析
package server.basic;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class TestXml1 {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        //1 獲取解析工廠
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //2 從解析工廠獲取解析器
        SAXParser parse = factory.newSAXParser();
        //3 編寫處理器

        //4 加載文檔Document註冊處理器
        PersonHandler handler = new PersonHandler();
        //5 解析server/basic/p.xml
        String xmlPath = "src/main/java/server/basic/p.xml";
        FileInputStream fis = new FileInputStream(xmlPath);
        parse.parse(fis,handler);

        List<Person> persons = handler.getPersons();
        for(Person p:persons){
            System.out.println(p.getName()+"--->"+p.getAge());
        }
    }
}

class PersonHandler extends DefaultHandler{
    private List<Person> persons; //多個Person的容器
    private Person person;
    private String tag; //當前操作的標籤
    @Override  //開始解析xml文檔
    public void startDocument() throws SAXException {
        System.out.println("開始解析");
        persons = new ArrayList<Person>(); //初始化容器
    }

    @Override  //開始解析元素,qName是標籤名稱
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        System.out.println(qName+"解析開始");
        if(null != qName){  //保存tag
            tag = qName;
            if(tag.equals("person")){  //如果是person標籤就創建person對象
                person = new Person();
            }
        }
    }

    @Override //讀取內容
    public void characters(char[] ch, int start, int length) throws SAXException {
        String str = new String(ch,start,length).trim();
//        if(str.length()>0) {
//            System.out.println("內容爲----->" + str.trim());
//        }else {
//            System.out.println("內容爲空---");
//        }
        if(tag != null) {
            if (tag.equals("name")) {
                person.setName(str);
            } else if (tag.equals("age")) {
                person.setAge(Integer.valueOf(str));
            }
        }
    }

    @Override //結束解析元素
    public void endElement(String uri, String localName, String qName) throws SAXException {
        System.out.println(qName+"解析結束");
        if(qName != null){
            if(qName.equals("person")){  //如果再次碰到person標籤就添加到容器中
                persons.add(person);
            }
        }
        tag = null; //解析完成爲了防止後一個標籤的空格的影響,將tag賦值爲null
    }

    @Override //結束解析文檔
    public void endDocument() throws SAXException {
        System.out.println("解析結束");;
    }

    //獲取存數據的容器
    public List<Person> getPersons() {
        return persons;
    }
}


class Person{
    private String name;
    private int age;

    public Person(){
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

解析webxml

  • 根據不同的pattern找到對應的類.servlet-mapping的url-pattern爲網址的後綴,根據這個找到servlet-name,再根據servlet-name找到servlet下的servlet-class標籤裏的包.類名,再利用反射根據這個類創建對象,執行相應的操作
  • web.xml文件
<?xml version="1.0" encoding="UTF-8"?>  
 <web-app>
     <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>server.basic2.LoginServlet</servlet-class>
     </servlet>
     <servlet>
        <servlet-name>reg</servlet-name>
        <servlet-class>server.basic2.RegisterServlet</servlet-class>
     </servlet>

    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
        <url-pattern>/g</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>reg</servlet-name>
        <url-pattern>/reg</url-pattern>
    </servlet-mapping>
</web-app>
  • Entity.java。對應servlet
package server.basic2;

/**
 *      <servlet>
 *         <servlet-name>login</servlet-name>
 *         <servlet-class>com.sxt.server.basic.servlet.LoginServlet</servlet-class>
 *      </servlet>
 */
public class Entity {
    private String name;
    private String clz;
    public Entity(){
    }

    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;
    }
}

  • Mapping.java對應xml中的servlet-mapping,pattern可能有多個,因此pattern放到集合中
package server.basic2;

import java.util.HashSet;
import java.util.Set;

public class Mapping {
    private String name;
    private Set<String> patterns;
    public Mapping(){
        patterns = new HashSet<>();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<String> getPatterns() {
        return patterns;
    }

    public void setPatterns(Set<String> patterns) {
        this.patterns = patterns;
    }

    public void addPattern(String pattern){
        this.patterns.add(pattern);
    }
}
  • TestXml2.java負責解析xml並完成響應,主要
package server.basic2;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class TestXml2 {
    public static void main(String[] args) throws Exception {
        //1 獲取解析工廠
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //2 從解析工廠獲取解析器
        SAXParser parse = factory.newSAXParser();
        //3 編寫處理器

        //4 加載文檔Document註冊處理器
        WebHandler handler = new WebHandler();
        //5 解析server/basic/p.xml
        String xmlPath = "src/main/java/server/basic2/web.xml";
        FileInputStream fis = new FileInputStream(xmlPath);
        parse.parse(fis,handler);

        List<Entity> entities  = handler.getEntities();
        List<Mapping> mappings = handler.getMappings();
        //打印內容
        for(Entity p:entities){
            System.out.println(p.getName()+"--->"+p.getClz());
            //login--->server.basic2.LoginServlet
            //reg--->server.basic2.RegisterServlet
        }

        for(Mapping m:mappings){
            System.out.println(m.getName()+"--->"+m.getPatterns());
            //login--->[/login, /g]
            //reg--->[/reg]
        }
        //下面開始操作
        //獲取映射關係
        WebContent web = new WebContent(entities,mappings);
        //模擬輸入,假設輸入了/login /g或者 /reg
        String className = web.getClz("/reg");
        System.out.println(className);
        //利用反射獲取到類
        Class clz = Class.forName(className);
        //實例化,使用接口類型
        Serverlet servlet = (Serverlet)clz.getConstructor().newInstance();
        //提供服務
        servlet.service();
    }
}

class WebHandler extends DefaultHandler{
    private List<Entity> entities = new ArrayList<>();; //多個Person的容器
    private List<Mapping> mappings = new ArrayList<>(); //多個Person的容器

    private Entity entity;
    private Mapping mapping;
    private String tag; //當前操作的標籤
    private boolean isMapping = false; //操作serverlet還是mapping

    @Override  //開始解析元素,qName是標籤名稱
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        System.out.println(qName+"解析開始");
        if(null != qName){  //保存tag
            tag = qName;
            if(tag.equals("servlet")){  //如果是servlet標籤就創建eneity對象
                entity = new Entity();
                isMapping = false;
            }else if(tag.equals("servlet-mapping")){
                mapping = new Mapping();
                isMapping = true;
            }
        }
    }

    @Override //讀取內容
    public void characters(char[] ch, int start, int length) throws SAXException {
        String str = new String(ch,start,length).trim();
//        if(str.length()>0) {
//            System.out.println("內容爲----->" + str.trim());
//        }else {
//            System.out.println("內容爲空---");
//        }
        if(tag != null) {
            if (isMapping) { //操作mapping
                if(tag.equals("servlet-name")){
                    mapping.setName(str);
                }else if(tag.equals("url-pattern")){
                    mapping.addPattern(str);
                }
            } else { //操作servlet
                if(tag.equals("servlet-name")){
                    entity.setName(str);
                }else if(tag.equals("servlet-class")){
                    entity.setClz(str);
                }
            }
        }
    }

    @Override //結束解析元素
    public void endElement(String uri, String localName, String qName) throws SAXException {
        System.out.println(qName+"解析結束");
        if(qName != null){
            if(qName.equals("servlet")){  //如果再次碰到person標籤就添加到容器中
                entities.add(entity);
            }else if(qName.equals("servlet-mapping")){
                mappings.add(mapping);
            }
        }
        tag = null; //解析完成爲了防止後一個標籤的空格的影響,將tag賦值爲null
    }


    //生成getter方法
    public List<Entity> getEntities() {
        return entities;
    }

    public List<Mapping> getMappings() {
        return mappings;
    }
}
  • Serverlet.java接口,xml中的類servlet-class對應的接口
package server.basic2;

public interface Serverlet {
    void service();
}
  • 兩個實現類,servlet-class的兩個類
  • LoginServlet.java
package server.basic2;

public class LoginServlet implements Serverlet {
    @Override
    public void service() {
        System.out.println("登錄");
    }
}
  • RegisterServlet
package server.basic2;

public class RegisterServlet implements Serverlet {
    @Override
    public void service() {
        System.out.println("註冊");
    }
}

html

  • 超文本標記語言,瀏覽器用
<html>
	<head>
		<title>第一個html登錄</title>
	</head>
	<body>
		<h1>表單的使用</h1>
		<pre>
				post:提交 ,基於http協議不同    量大   請求參數url不可見 安全<br/>
				get:  默認,獲取,基於http協議不同  量小  請求參數url可見 不安全<br/>
				action: 請求web服務器的資源   URL<br/>
				name:作爲後端使用,區分唯一: 請求服務器,必須存在,數據不能提交<br/>
				id: 作爲前端使用,區分唯一<br/>
		</pre>
		<form method="get" action="http://localhost:8888/index.html">
						用戶名:<input type="text" name="uname"  id="uname"/>
						密碼:<input type="password" name="pwd"  id="pwd"/>
						<input type="submit" value="登錄"/>
		</form>
	</body>
</html>

http協議

獲取請求協議

  • 獲取get或post的請求協議
package server.myserver;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 瀏覽器輸入http://locolhost:8889
 * 端口號,接收
 */
public class Server01 {
    ServerSocket serverSocket;
    public static void main(String[] args) throws IOException {
        Server01 server = new Server01();
        server.start();

    }
    //啓動服務
    public void start() throws IOException {
        serverSocket = new ServerSocket(8889);
        receive();
    }
    //接受
    public void receive() throws IOException {
        Socket client = serverSocket.accept();
        System.out.println("一個客戶端建立了連接");
        //獲取請求協議,get方式可以通過瀏覽器直接訪問,get/post方式通過運行login.html訪問
        InputStream is = client.getInputStream();
        byte[] data = new byte[1024*1024];
        int len = is.read(data); //一次性讀取存入data
        String requestinfo = new String(data,0,len);
        System.out.println(requestinfo);

    }

    //停止服務
    public void stop(){

    }
}
  • get的請求協議
GET /index.html?uname=123&paswrd=10000 HTTP/1.1
Host: localhost:8889
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _ga=GA1.1.1809456532.1578749380
  • post的請求協議,數據在最後空白行後面
POST /index.html HTTP/1.1
Host: localhost:8889
Connection: keep-alive
Content-Length: 19
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3706.400 SLBrowser/10.0.3974.400
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _ga=GA1.1.2136018396.1577101129

uname=fda&pwd=34567

返回響應協議

  • 主要處理responseInfo,包括響應行,響應頭和正文。正文前面空一行。

BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
bw.write(responseInfo.toString());
bw.flush();

package server.myserver;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/**
 * 返回響應協議
 * 瀏覽器輸入http://locolhost:8889/index.html?uname=werg&passwd=2344
 * 創建ServerSocket,建立連接獲取Socket,通過輸入流獲取請求協議
 */
public class Server02 {
    ServerSocket serverSocket;
    public static void main(String[] args) throws IOException {
        Server02 server = new Server02();
        server.start();

    }
    //啓動服務
    public void start() throws IOException {
        serverSocket = new ServerSocket(8889);
        receive();
    }
    //接受
    public void receive() throws IOException {
        Socket client = serverSocket.accept();
        System.out.println("一個客戶端建立了連接");
        //獲取請求協議,get方式可以通過瀏覽器直接訪問,get/post方式通過運行login.html訪問
        InputStream is = client.getInputStream();
        byte[] data = new byte[1024*1024];
        int len = is.read(data); //一次性讀取存入data
        String requestinfo = new String(data,0,len);
        System.out.println(requestinfo);

        //返回響應信息
        //1 響應行
        //2 響應頭,最後一行是空行
        //3 正文
        StringBuilder content =new StringBuilder();
        content.append("<html>");
        content.append("<head>");
        content.append("<title>");
        content.append("服務器響應成功");
        content.append("</title>");
        content.append("</head>");
        content.append("<body>");
        content.append("中國必勝,武漢必勝。。。。");
        content.append("</body>");
        content.append("</html>");
        int size = content.toString().getBytes().length; //必須獲取字節長度
        StringBuilder responseInfo =new StringBuilder();
        String blank =" ";
        String CRLF = "\r\n";
        //返回
        //1、響應行: HTTP/1.1 200 OK
        responseInfo.append("HTTP/1.1").append(blank);
        responseInfo.append(200).append(blank);
        responseInfo.append("OK").append(CRLF);
        //2、響應頭(最後一行存在空行):
			/*
			 Date:Mon,31Dec209904:25:57GMT
			Server:shsxt Server/0.0.1;charset=GBK
			Content-type:text/html
			Content-length:39725426
			 */
        responseInfo.append("Date:").append(new Date()).append(CRLF);
        responseInfo.append("Server:").append("myserver Server/0.0.1;charset=GBK").append(CRLF);
        responseInfo.append("Content-type:text/html").append(CRLF);
        responseInfo.append("Content-length:").append(size).append(CRLF);
        responseInfo.append(CRLF);
        //3、正文
        responseInfo.append(content.toString());

        //寫出到客戶端
        BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
        bw.write(responseInfo.toString());
        bw.flush();

    }

    //停止服務
    public void stop(){

    }
}

封裝的響應

  • 將響應頭放到一個類中,直接傳遞響應代碼和相應內容,進行響應。使用時只關注內容和狀態碼
  • Response.java
package server.myserver;

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 BufferedWriter bw;
    //正文
    private StringBuilder content;
    //協議頭
    private StringBuilder headInfo;
    private int len; //正文的字節長度
    private final String BLANK = " ";
    private final String CRLF = "\r\n";
    private Response(){  //這個構造器私有,只能自己調用
        content = new StringBuilder();
        headInfo = new StringBuilder();
        len = 0;
    }

    public Response(Socket client) throws IOException {
        this();
        bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
    }

    public Response(OutputStream os){
        this();
        bw = new BufferedWriter(new OutputStreamWriter(os));
    }

    //構建頭信息,包括響應行和響應頭
    private void createHeadInfo(int code){
        //1 響應行
        headInfo.append("HTTP/1.1").append(BLANK);
        headInfo.append(code).append(BLANK);
        switch (code){
            case 200:
                headInfo.append("OK").append(CRLF);
                break;
            case 404:
                headInfo.append("NOT FOUND").append(CRLF);
                break;
            case 505:
                headInfo.append("SERVER ERROR").append(CRLF);
                break;
        }
        //2 響應頭,最後是個空行
        headInfo.append("Date:").append(new Date()).append(CRLF);
        headInfo.append("Server:").append("shsxt Server/0.0.1;charset=GBK").append(CRLF);
        headInfo.append("Content-type:text/html").append(CRLF);
        headInfo.append("Content-length:").append(len).append(CRLF);
        headInfo.append(CRLF);
    }

    //動態添加 3內容,返回對象本身,可以鏈式調用
    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+=(info+CRLF).getBytes().length;
        return this;
    }

    // 推送信息,返回到瀏覽器,
    public void pushToBrowser(int code) throws IOException {
        if(null == headInfo){
            code = 505;
        }
        createHeadInfo(code);
        bw.append(headInfo);
        bw.append(content);
        bw.flush();
    }
}
  • 使用
package server.myserver;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * 封裝響應協議
 * 瀏覽器輸入http://locolhost:8889/index.html?uname=werg&passwd=2344
 * 創建ServerSocket,建立連接獲取Socket,通過輸入流獲取請求協議
 */
public class Server03 {
    ServerSocket serverSocket;
    public static void main(String[] args) throws IOException {
        Server03 server = new Server03();
        server.start();

    }
    //啓動服務
    public void start() throws IOException {
        serverSocket = new ServerSocket(8889);
        receive();
    }
    //接受
    public void receive() throws IOException {
        Socket client = serverSocket.accept();
        System.out.println("一個客戶端建立了連接");

        //返回響應信息
        Response response = new Response(client);

        response.print("<html>");
        response.print("<head>");
        response.print("<title>");
        response.print("服務器響應成功");
        response.print("</title>");
        response.print("</head>");
        response.print("<body>");
        response.print("中國必勝,武漢必勝。。。。");
        response.print("</body>");
        response.print("</html>");

        response.pushToBrowser(200);

    }

    //停止服務
    public void stop(){

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章