《JavaWeb与Tomcat》——1.Web基本概念

由于这本书的代码大多为实践性,依赖配置好JDBC、Tomcat等IDE,代码内容多为测试API本身,因此为节约时间,代码部分从书中截图并仅给出相关说明。

第 1 章

这一章主要讲Web相关的基本概念

Web是一种分布式应用架构,以互联网为媒介,旨在分享分布在网络上的各个Web服务器中所有的链接的信息(资源/服务);通信协议HTTP,信息的表现形式HTML,用户为客户端/浏览器(CS/BS);

Web的3个特征:

  • 采用HTML表达信息;
  • 使用URL实现资源定位;
  • 使用应用层HTTP协议来规范通信过程(报文结构);
  1. HTML超文本标记语言,XML风格,包括:标签(<html> <head> <body> <...>)、内容(即一般的文本或资源链接,与标签符号冲突可以加转义字符)、样式(内联样式/外联CSS);
  2. URL统一资源定位器,URL=应用层协议(http)+IP/PORT(IP地址或者域名+端口)+资源路径(资源/服务);
  3. HTTP协议超文本传输协议,采用客户端(如浏览器)/服务器(Web服务器)的通信模式,基于TCP/IP协议可靠传输,,默认端口80;
  4. 5层协议:物理层、数据链路层、网络层、传输层、应用层;自上而下-自下而上,减小耦合,规范各自的能力,便于扩展;

HTTP过程:
(1)客户端发起-建立TCP连接(3次握手);
(2)发出HTTP请求(解析:域名、HTTP报文(头+内容));
(3)服务器发出HTTP相应(封装相应的HTTP报文);
(4)客户端/浏览器收到相应并展示(解析HTTP相应);
(5)关闭TCP连接(4次挥手);

报文格式

请求:
(1)第一行,请求方式(如POST、GET、PUT、DELETE)、URI、HTTP协议版本;
(2)中间多行,请求头HTTP HEAD,包括:浏览器信息、语言、主机、请求内容的长度、是否Keep-Alive,是否No-Cache;
(3)最后一行,请求正文,如:username=admin&password=123456;

image.png

响应

(1)第一行,HTTP版本+状态码(10X信息提示 200 OK成功相应 30X重定向跳转 40X浏览器请求有问题 50X服务器故障)
(2)中间多行,相应头RESPONSE HEAD,包括:服务器信息、正文类型编码、长度;
(3)最后一段,相应正文,一般是HTML文档;

写一个Socket模拟HTTP过程

(1) 服务端:

  • 服务器SocketServer绑端口;
  • while(true)监听;accept得到客户端对象;
  • Service()提供服务,包括:解析HTTP请求(如getInputStream按行解析)、相应-发送(如getOutputStream写状态码+HTML内容);
  • 最后关闭TCP连接;

eg:

import java.io.*;

import java.net.*;


public class HTTPServer {

    public static void main(String args[]) {
        int port;
        ServerSocket serverSocket;
        try {
            port = Integer.parseInt(args[0]);
        } catch (Exception e) {
            System.out.println("port = 8080 (默认)");
            port = 8080; //默认端口为8080
        }
        
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务器正在监听端口:" + serverSocket.getLocalPort());
            while (true) { //服务器在一个无限循环中不断接收来自客户的TCP连接请求
                try {
                    //等待客户的TCP连接请求
                    final Socket socket = serverSocket.accept();
                    System.out.println("建立了与客户的一个新的TCP连接,该客户的地址为:" +
                            socket.getInetAddress() + ":" + socket.getPort());
                    service(socket);  //响应客户请求
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } //#while
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 响应客户的HTTP请求
     */
    public static void service(Socket socket) throws Exception {
        /*读取HTTP请求信息*/
        InputStream socketIn = socket.getInputStream(); //获得输入流
        Thread.sleep(500);  //睡眠500毫秒,等待HTTP请求
        int size = socketIn.available();
        byte[] requestBuffer = new byte[size];
        socketIn.read(requestBuffer);
        String request = new String(requestBuffer);
        System.out.println(request); //打印HTTP请求数据

        /*解析HTTP请求*/
        //获得HTTP请求的第一行
        String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
        //解析HTTP请求的第一行
        String[] parts = firstLineOfRequest.split(" ");
        String uri = parts[1]; //获得HTTP请求中的uri

        /*决定HTTP响应正文的类型*/
        String contentType;
        if (uri.indexOf("html") != -1 || uri.indexOf("htm") != -1)
            contentType = "text/html";
        else if (uri.indexOf("jpg") != -1 || uri.indexOf("jpeg") != -1)
            contentType = "image/jpeg";
        else if (uri.indexOf("gif") != -1)
            contentType = "image/gif";
        else
            contentType = "application/octet-stream";

        /*创建HTTP响应结果 */
        //HTTP响应的第一行
        String responseFirstLine = "HTTP/1.1 200 OK\r\n";
        //HTTP响应头
        String responseHeader = "Content-Type:" + contentType + "\r\n\r\n";
        //获得读取响应正文数据的输入流
        InputStream in = HTTPServer1.class.getResourceAsStream("root/" + uri);
        
        /*发送HTTP响应结果 */
        OutputStream socketOut = socket.getOutputStream(); //获得输出流
        //发送HTTP响应的第一行
        socketOut.write(responseFirstLine.getBytes());
        //发送HTTP响应的头
        socketOut.write(responseHeader.getBytes());
        //发送HTTP响应的正文
        int len = 0;
        byte[] buffer = new byte[128];
        while ((len = in.read(buffer)) != -1)
            socketOut.write(buffer, 0, len);
        Thread.sleep(1000);  //睡眠1秒,等待客户接收HTTP响应结果
        socket.close(); //关闭TCP连接
    }
}

(2) 客户端:

  • 绑定目的服务器的IP+PORT,即建立连接;
  • OutputStream发送HTTP请求;
  • getInputStream接收/解析相应;
  • 最后,关闭socket;

(以上过程可以使用IDE两个类间或者浏览器访问Tomcat上的指定IP测试,发现都可以互相通信)

import java.net.*;

import java.io.*;

import java.util.*;


public class HTTPClient {

    public static void main(String args[]) {
        //确定HTTP请求的uri
        String uri = "index.htm";
        if (args.length != 0) uri = args[0];
        doGet("localhost", 8080, uri); //按照GET请求方式访问HTTPServer
    }
    
    /**
     * 按照GET请求方式访问HTTPServer
     */
    public static void doGet(String host, int port, String uri) {
        Socket socket = null;
        try {
            socket = new Socket(host, port); //与HTTPServer建立FTP连接
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        try {
            /*创建HTTP请求 */
            StringBuffer sb = new StringBuffer("GET " + uri + " HTTP/1.1\r\n");
            sb.append("Accept: */*\r\n");
            sb.append("Accept-Language: zh-cn\r\n");
            sb.append("Accept-Encoding: gzip, deflate\r\n");
            sb.append("User-Agent: HTTPClient\r\n");
            sb.append("Host: localhost:8080\r\n");
            sb.append("Connection: Keep-Alive\r\n\r\n");
            
            /*发送HTTP请求*/
            OutputStream socketOut = socket.getOutputStream(); //获得输出流
            socketOut.write(sb.toString().getBytes());
            Thread.sleep(2000); //睡眠2秒,等待响应结果
            
            /*接收响应结果*/
            InputStream socketIn = socket.getInputStream(); //获得输入流
            int size = socketIn.available();
            byte[] buffer = new byte[size];
            socketIn.read(buffer);
            System.out.println(new String(buffer)); //打印响应结果
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    } //#doGet()
}

由上可知,浏览器的作用:
(1)通过URL访问,发送请求(可以传入用户的输入内容);
(2)可以解析相应,并且解析HTML将其显示在网页上,并且可以调用安装的浏览器插件(如AdobeFlashPlayer或者pdf阅读器);

Tomcat的作用:
(1)可以解析HTTP请求的内容;
(2)将请求封装于一个request对象,找对应的Servlet去执行service()方法去相应请求,并且可以将相应封装于一个response对象,写入相应的内容,发送给浏览器;

HTML(动态的,如曲线、表格里的监控信息,服务器与用户交互)

即Web服务器利用特定的程序来实现动态的生成HTML文档,如使用嵌入java代码的html文档(JSP);

过程:

  • 浏览器访问特定servlet;
  • web服务器运行HelloServlet类(编译成HelloServlet.class);
  • 返回动态的html内容;
  • 编写代码模拟这一过程(P26)
(1)
import java.io.*;
import java.net.*;
import java.util.*;

public class HTTPServer1 {

    private static Map servletCache = new HashMap();
    
    public static void main(String args[]) {
        int port;
        ServerSocket serverSocket;

        try {
            port = Integer.parseInt(args[0]);
        } catch (Exception e) {
            System.out.println("port = 8080 (默认)");
            port = 8080; //默认端口为8080
        }
        
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务器正在监听端口:" + serverSocket.getLocalPort());
            
            while (true) { //服务器在一个无限循环中不断接收来自客户的TCP连接请求
                try {
                    //等待客户的TCP连接请求
                    final Socket socket = serverSocket.accept();
                    System.out.println("建立了与客户的一个新的TCP连接,该客户的地址为:" +
                            socket.getInetAddress() + ":" + socket.getPort());
                    service(socket);  //响应客户请求
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } //#while
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 响应客户的HTTP请求
     */
    public static void service(Socket socket) throws Exception {
        /*读取HTTP请求信息*/
        InputStream socketIn = socket.getInputStream(); //获得输入流
        Thread.sleep(500);  //睡眠500毫秒,等待HTTP请求
        int size = socketIn.available();
        byte[] requestBuffer = new byte[size];
        socketIn.read(requestBuffer);
        String request = new String(requestBuffer);
        System.out.println(request); //打印HTTP请求数据

        /*解析HTTP请求*/
        //获得HTTP请求的第一行
        String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
        //解析HTTP请求的第一行
        String[] parts = firstLineOfRequest.split(" ");
        String uri = parts[1]; //获得HTTP请求中的uri

        /*如果请求访问Servlet,则动态调用Servlet对象的service()方法*/
        if (uri.indexOf("servlet") != -1) {
            //获得Servlet的名字
            String servletName = null;
            if (uri.indexOf("?") != -1)
                servletName = uri.substring(uri.indexOf("servlet/") + 8, uri.indexOf("?"));
            else
                servletName = uri.substring(uri.indexOf("servlet/") + 8, uri.length());
            //尝试从Servlet缓存中获取Servlet对象
            Servlet servlet = (Servlet) servletCache.get(servletName);
            //如果Servlet缓存中不存在Servlet对象,就创建它,并把它存放在Servlet缓存中
            if (servlet == null) {
                servlet = (Servlet) Class.forName("server." + servletName).newInstance();
                servlet.init();//先调用Servlet对象的init()方法
                servletCache.put(servletName, servlet);
            }
            
            //调用Servlet的service()方法
            servlet.service(requestBuffer, socket.getOutputStream());
            Thread.sleep(1000);  //睡眠1秒,等待客户接收HTTP响应结果
            socket.close(); //关闭TCP连接
            return;
        }

        /*决定HTTP响应正文的类型*/
        String contentType;
        if (uri.indexOf("html") != -1 || uri.indexOf("htm") != -1)
            contentType = "text/html";
        else if (uri.indexOf("jpg") != -1 || uri.indexOf("jpeg") != -1)
            contentType = "image/jpeg";
        else if (uri.indexOf("gif") != -1)
            contentType = "image/gif";
        else
            contentType = "application/octet-stream";

        /*创建HTTP响应结果 */
        //HTTP响应的第一行
        String responseFirstLine = "HTTP/1.1 200 OK\r\n";
        //HTTP响应头
        String responseHeader = "Content-Type:" + contentType + "\r\n\r\n";
        //获得读取响应正文数据的输入流
        InputStream in = HTTPServer1.class.getResourceAsStream("root/" + uri);

        /*发送HTTP响应结果 */
        OutputStream socketOut = socket.getOutputStream(); //获得输出流
        //发送HTTP响应的第一行
        socketOut.write(responseFirstLine.toString().getBytes());
        //发送HTTP响应的头
        socketOut.write(responseHeader.toString().getBytes());
        //发送HTTP响应的正文
        int len = 0;
        byte[] buffer = new byte[128];
        while ((len = in.read(buffer)) != -1)
            socketOut.write(buffer, 0, len);
        Thread.sleep(1000);  //睡眠1秒,等待客户接收HTTP响应结果
        socket.close(); //关闭TCP连接
    }
}
(2)
import java.io.*;
public class HelloServlet implements Servlet {
    public void init() throws Exception {
        System.out.println("HelloServlet is inited");
    }
    public void service(byte[] requestBuffer, OutputStream out) throws Exception {
        String request = new String(requestBuffer);
        //获得HTTP请求的第一行
        String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
        //解析HTTP请求的第一行
        String[] parts = firstLineOfRequest.split(" ");
        String method = parts[0]; //获得HTTP请求中的请求方式
        String uri = parts[1]; //获得HTTP请求中的uri
        /*获得请求参数username */
        String username = null;
        if (method.equalsIgnoreCase("get") && uri.indexOf("username=") != -1) {
            /*假定uri="servlet/HelloServlet?username=Tom&password=1234"*/
            //parameters="username=Tom&password=1234"
            String parameters = uri.substring(uri.indexOf("?"), uri.length());
            //parts={"username=Tom","password=1234"};
            parts = parameters.split("&");
            //parts={"username","Tom"};
            parts = parts[0].split("=");
            username = parts[1];
        }
        if (method.equalsIgnoreCase("post")) {
            int locate = request.indexOf("\r\n\r\n");
            //获得响应正文
            String content = request.substring(locate + 4, request.length());
            if (content.indexOf("username=") != -1) {
                /*假定content="username=Tom&password=1234"*/
                //parts={"username=Tom","password=1234"};
                parts = content.split("&");
                //parts={"username","Tom"};
                parts = parts[0].split("=");
                username = parts[1];
            }
        }
        /*创建并发送HTTP响应*/
        //发送HTTP响应第一行
        out.write("HTTP/1.1 200 OK\r\n".getBytes());
        //发送HTTP响应头
        out.write("Content-Type:text/html\r\n\r\n".getBytes());
        //发送HTTP响应正文
        out.write("<html><head><title>HelloWorld</title></head><body>".getBytes());
        out.write(new String("<h1>Hello:" + username + "</h1></body><head>").getBytes());
    }
}

Web服务器

(1)从输入内容中解析出要访问的servlet的名称+请求的内容;
(2)先从servlet缓存中找,找不到则通过ClassName反射出一个对应的servlet对象,调用servlet对象的init、service(传入request/response对象)、destroy方法;
(3)close()方法关闭;

Web服务

一个Web服务器上面有多个Web服务(靠Web服务器发布),一个网络中可能有多个Web服务器,Web、服务可以看成可以被客户端远程访问的各种方法(业务逻辑、复杂运算、资源数据等);

(图P31 1-19)

image.png

image.png

HTTP的请求参数(如输入的username=admin)

对于GET请求方式,发送的HTTP报文中,放在HTTP的第一行URI里面;

(P35图)

对应的HTML中的输入框(method=”GET” action=”servlet/HelloServlet”)

(P34图)

image.png

对于POST方法,请求参数放在HTTP正文部分(相对安全);

(P36图)

image.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值