深入理解计算机:Web服务器

Web服务器

Web基础
  • HTTP协议——超文本传输协议(Hypertext Transfer Protocol)

    • 定义
      • Web客户端和服务器之间的交互用的一个基于文本的应用级协议。
      • 一个客户端打开一个到服务器的因特网连接,并且请求某些内容。服务器响应所请求的内容,然后关闭连接。浏览器读取这些内容,并把它显示在屏幕上。
  • HTML语言——超文本标记语言(Hypertext Markuo Language)

    • 定义
      • 区别于常规的文件检索服务,一个HTML程序包含指令,它们告诉浏览器如何显示这页中的各种文本和图形对象。
Web内容
  • 定义

    • 内容是与一个MIME(Multipurpose Internet Mail Extension,多用途的网际邮件扩充协议)类型相关字节序列
  • 常用的MIME类型
    在这里插入图片描述

    • text/html
    • text/plain
    • application/json
    • image/gif
    • image/jpeg
  • 两种方式提供内容

    • 静态内容

      • 取出一个磁盘文件,并将它的内容返回给客户端,磁盘文件为静态内容,过程叫做服务静态内容
    • 动态内容

      • 运行一个可执行文件,并将它的输出返回给客户端,执行文件输出的是动态内容,过程称为服务动态内容
  • URL——通用资源定位符(Univwesal Resource Locator)

HTTP事务
  • 静态内容的事务图
    在这里插入图片描述

  • HTTP请求

    • 组成:一个请求行(第5行)+零个或多个请求报头(第6行)+空的文本行(第7行标识终止报头)
    • 请求行的格式:method URI version
    • HTTP请求支持的方法:GET、POST、OPTIONS、HEAD、PUT、DELETE和TRACE
    • URI:统一资源标识符(Uniform Resource Identifier)是相应URL的后缀,包括文件名和可选的参数
  • HTTP响应

    • 组成:一个响应行(第8行)+零个或多个响应报头(第9~13行)+空行(第14行标识终止报头)+响应主体(第15~17行)
    • HTTP状态码图
      在这里插入图片描述
服务动态内容
  • 客户端如何将程序参数给服务器?

    • GET请求通过URI中传递,用?分隔文件名和参数,用&分隔参数
    • POSt请求的参数放在主体中
  • 服务器如何将参数传递给子进程?

    • it calls fork to create a child process and calls execve to run the /cgi-bin/adder program in the context of the child. Programs like the adder program are often referred to as CGI programs because they obey the rules of the CGI standard. Before the call to execve, the child process sets the CGI environment variable QUERY_STRING to 15000&213, which the adder program can reference at run time using the Linux getenv function.
  • 服务器如何将其他信息传递给子进程?

    • CGI defines a number of other environment variables that a CGI program can expect to be set when it runs. Figure 11.26 shows a subset.
  • 子进程将它的输出发送到哪里?

    • A CGI program sends its dynamic content to the standard output. Before the child process loads and runs the CGI program, it uses the Linux dup2 function to redirect standard output to the connected descriptor that is associated with the client. Thus, anything that the CGI program writes to standard output goes directly to the client.
    • Notice that since the parent does not know the type or size of the content that the child generates, the child is responsible for generating the Content-type and Content-length response headers, as well as the empty line that terminates the headers.
CGI程序示例
  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    /**
    * @author chenjianrong-lhq 2019年12月08日 21:22:16
    * @Description: 网络编程客户端
    * @ClassName: ClientApplication
    */
    public class ClientApplication {

    public static String IP = "www.baidu.com";

    public static Integer PORT = 80;

    public static void main(String[] args) {

    String param = "提供动态内容,需要派生一个子进程,并在子进程的上下文中运行一个CGI程序,来提供各种类型的动态内容。子进程通过请求URI获取的CGI参数初始化QUERY——STRING环境变量。然后子进程重定向它的标准输出到已连接文件描述符,然后加载并运行CGI程序。CGI运行的结果会直接显示在服务器端。";

    /**step1 定义 Socket 对象、OutputStream 对象和一个 InputStream 对象并完成初始化**/
    Socket socket = null;
    OutputStream os = null;
    InputStream is = null;

    try {

    socket = new Socket(IP, PORT);

    /**step2.建立与服务器端的连接并将数据发送到服务器端**/
    socket.setSoTimeout(60 * 1000);

    os = socket.getOutputStream();
    os.write(param.getBytes("utf-8"));
    Thread.sleep(1000);

    // ByteArrayOutputStream bos = new ByteArrayOutputStream();

    /**step3.从输入流中读出服务器的反馈信息并输出到控制台**/
    byte[] buffer = new byte[1024];
    is = socket.getInputStream();
    int count = 0;

    // do {
    // count = is.read(buffer);
    // bos.write(buffer, 0, count);
    // } while (is.available() != 0);

    count = is.read(buffer);
    System.out.println("服务端反馈的数据是:" + new String(buffer, 0, count));
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    /**step4 关闭流及 Socket 对象**/
    try {
    if (os != null) {
    os.close();
    }
    if (is != null) {
    is.close();
    }
    if (socket != null) {
    socket.close();
    }
    } catch (Exception ex) {
    ex.printStackTrace();
    }
    }
    }
    }
  • 服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    /**
    * @author chenjianrong-lhq 2019年12月08日 21:56:09
    * @Description: 网络编程服务端
    * @ClassName: ServerApplication
    */
    public class ServerApplication {

    public static String IP = "www.baidu.com";

    public static Integer PORT = 80;

    public static void main(String[] args) {

    /**step5 创建ServerSocket、Socket、OutputStream、InputStream以及端口号并初始化**/
    ServerSocket serverSocket = null;
    Socket socket = null;
    OutputStream os = null;
    InputStream is = null;
    try {
    /**step6 开启服务器并接收客户端发送的数据**/
    serverSocket = new ServerSocket(PORT);
    System.out.println("服务器开启,等待连接...");

    socket = serverSocket.accept();

    is = socket.getInputStream();
    byte[] buffer = new byte[1024];
    int len = is.read(buffer);
    System.out.println("客户端发送的内容为:" + new String(buffer, 0, len));

    /**step7 使用输出流对象将信息返回给客户端**/
    os = socket.getOutputStream();
    os.write("我是服务器端".getBytes());
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    /**step8 关闭流对象、ServerSocket对象以及Socket对象**/
    try {
    os.close();
    is.close();
    serverSocket.close();
    socket.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
TINY Web服务器

参考博客:网络编程