第 1 章 Web运作原理探析
目录
1.1 Web的概念
Web的特征:
-
用HTML来表示信息
-
用统一资源定位技术URL来实现网络上信息的精确定位
-
用网络应用层协议HTTP来规范浏览器与Web服务器之间的通信过程
1.2 HTML简介
略
1.3 URL简介
- 应用层协议://主机IP地址或域名/资源所在路径/文件名
1.4 HTTP协议简介
-
应用层----HTTP
-
传输层----TCP
-
网络层----IP
客户端与服务器端之间的一次信息交换:
-
客户端与服务器端建立TCP连接
-
客户端发出HTTP请求
-
服务器端发回相应的HTTP响应
-
客户端接收HTTP响应后,解析HTTP响应
-
客户端与服务器端之间的TCP连接关闭
1.4.1 HTTP请求格式
-
请求方法、URI和HTTP协议的版本
- 请求方法有:GET、POST、HEAD、PUT、DELETE
- URI:/资源所在路径/文件名
-
请求头
-
请求正文
1.4.2 HTTP响应格式
- HTTP协议的版本、状态码和描述
状态码:一个3位整数,以1、2、3、4或5开头
1xx:信息提示,表示临时的响应
2xx :响应成功,表示服务器成功地接收客服端请求
3xx:重定向
4xx:客户端错误,表明客户端可能有问题
5xx:服务器错误,表明服务器由于遇到某种错误而不能响应客户端请求
常见的状态代码:
200:响应成功
400:错误请求。客户发送的HTTP请求不正确
404:文件不存在。在服务器上没有客户要求访问的文档
405:服务器不支持客户的请求方式
500:服务器内部错误
-
响应头
-
响应正文
1.4.3 正文部分的MIME类型
文件扩展名 | MIME类型 |
---|---|
.html、.htm | text/html |
.js | application/x-javascript |
.json | application/json |
.jpg、.jpeg | application/jpeg |
application/pdf |
1.5 用Java套接字创建HTTP客户与服务器程序
HTTPServer
package server;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HTTPServer {
public static void main(String args[]) {
int port;
ServerSocket serverSocker;
try {
port = Integer.parseInt(args[0]);
} catch (Exception e) {
System.out.println("port = 8080(默认)");
port = 8080;
}
try {
serverSocker = new ServerSocket(port);
System.out.println("服务器正在监听端口:" + serverSocker.getLocalPort());
while (true) {
try {
final Socket socket = serverSocker.accept();
System.out.println("建立了与客户的一个新的TCP连接,该客户的地址为:" + socket.getInetAddress() + ":" + socket.getPort());
service(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void service(Socket socket) throws Exception {
InputStream socketIn = socket.getInputStream();
Thread.sleep(500);
int size = socketIn.available();
byte[] buffer = new byte[size];
socketIn.read(buffer);
String request = new String(buffer);
System.out.println(request);
String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
String[] parts = firstLineOfRequest.split(" ");
String uri = parts[1];
String contentType;
if (uri.contains("html") || uri.contains("htm")) {
contentType = "text/html";
} else if (uri.contains("jpg") || uri.contains("jpeg")) {
contentType = "image/jpeg";
} else if (uri.contains("gif")) {
contentType = "image/gif";
} else {
// 字节流类型
contentType = "application/octet-stream";
}
String responseFirstLine = "HTTP/1.1 200 OK\r\n";
String responseHeader = "Content-Type:" + contentType + "\r\n\r\n";
InputStream inputStream = HTTPServer.class.getResourceAsStream("root/" + uri);
OutputStream socketOut = socket.getOutputStream();
socketOut.write(responseFirstLine.getBytes());
socketOut.write(responseHeader.getBytes());
int len = 0;
buffer = new byte[128];
while ((len = inputStream.read(buffer)) != -1) {
socketOut.write(buffer, 0, len);
}
Thread.sleep(1000);
socket.close();
}
}
HTTPClient
package Client;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class HTTPClient {
public static void main(String[] args) {
String uri = "index.htm";
if (args.length != 0) {
uri = args[0];
}
doGet("localhost", 8080, uri);
}
public static void doGet(String host, int port, String uri) {
Socket socket = null;
try {
socket = new Socket(host, port);
} catch (Exception e) {
e.printStackTrace();
}
try {
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: HTTPClint\r\n");
sb.append("Host: localhost:8080\r\n");
sb.append("Connection: Keep-Alive\r\n\r\n");
OutputStream socketOut = socket.getOutputStream();
socketOut.write(sb.toString().getBytes());
Thread.sleep(2000);
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();
}
}
}
}
1.5.1 演示异构系统之间用HTTP协议通信
- 只要HTTP客户程序和服务器程序都遵守HTTP协议,那么即使他们分别用不同的语言编写,或者运行在不同操作系统平台上,彼此也能看的懂对方发送的数据.
1.5.2 演示对网页中的超链接的处理过程
1.5.3 演示对网页中的图片的处理过程
- 两者都是客户端会再次与服务器端创建连接.
1.6 Web发展历程
1.6.1. 发布静态HTML文档
- 信息主要是文本和图片.
1.6.2. 发布静态多媒体信息
1.6.3. 提供浏览器端与用户的动态交互功能
- JavaScript等脚本语言
1.6.4. 提供服务器端与用户的动态交互功能
- Web服务器增加了动态执行程序代码的功能,分两种:
- 完全用编程语言写的程序,如CGI程序和用Java编写的Servlet程序.
- 嵌入程序代码的HTML文档,如ASP,JSP文档.JSP文档是指嵌入了Java程序代码的HTML文档.
HTTPServer改进,动态功能
package server;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class HTTPServer1 {
private static Map<String, Servlet> servletCache = new HashMap<>();
public static void main(String args[]) {....}
/**
* Web服务器动态执行程序代码,在这里指HTTPServer1在运行时动态加载Servlet接口的实现类,创建它的实例,然后调用它的相关方法.
* 规定:如果客户请求的URI位于servlet子目录下,就按照Servlet来处理,否则就按照普通静态文件处理.
*/
public static void service(Socket socket) throws Exception {
InputStream socketIn = socket.getInputStream();
Thread.sleep(500);
int size = socketIn.available();
byte[] buffer = new byte[size];
socketIn.read(buffer);
String request = new String(buffer);
System.out.println(request);
String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
String[] parts = firstLineOfRequest.split(" ");
String uri = parts[1];
// 如果请求访问Servlet,则动态调用Servlet对象的service()方法
if (uri.contains("servlet")) {
// 获得Servlet的名字
String servletName = null;
if (uri.contains("?")) {
servletName = uri.substring(uri.indexOf("servlet/") + 8, uri.indexOf("?"));
} else {
servletName = uri.substring(uri.indexOf("servlet/") + 8, uri.length());
}
// 尝试从Servlet缓存中获取Servlet对象
Servlet servlet = servletCache.get(servletName);
// 如果Servlet缓存中不存在,就创建它,并把它存放在Servlet缓存中
if (servlet == null) {
servlet = (Servlet) Class.forName("server." + servletName).newInstance();
// 先调用Servlet的init()方法
servlet.init();
servletCache.put(servletName, servlet);
}
// 调用Servlet的sercice()方法
servlet.service(buffer, socket.getOutputStream());
Thread.sleep(1000);
socket.close();
return;
}
String contentType;
// 与HTTPServer一样
...
}
}
Servet接口
package server;
import java.io.OutputStream;
public interface Servlet {
/**
* 初始化方法,当HTTPServer1创建了该接口的一个实例后,就会立即调用该实例的init()方法
*/
void init() throws Exception;
/**
* 用于响应HTTP请求,产生具体的HTTP响应结果.HTTPServer1服务器在响应HTTP请求时会调用实现了Servlet接口的特定类的service()
*/
void service(byte[] requestBuffer, OutputStream out) throws Exception;
}
Servlet实现类
package server;
import java.io.OutputStream;
public class HelloServlet implements Servlet {
@Override
public void init() throws Exception {
System.out.println("HelloServlet is inited");
}
/**
* 解析HTTP请求中的请求参数,并
*/
@Override
public void service(byte[] requestBuffer, OutputStream out) throws Exception {
String request = new String(requestBuffer);
String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
String[] parts = firstLineOfRequest.split(" ");
String method = parts[0];
String uri = parts[1];
String username = null;
if ("get".equals(method) && uri.contains("username=")) {
/**
* 假定uri="servlet/HelloServlet?username=Tom&password=1234
*/
String parameters = uri.substring(uri.indexOf("?"), uri.length());
parts = parameters.split("&");
parts = parts[0].split("=");
username = parts[1];
}
if ("post".equals(method)) {
int locate = request.indexOf("\r\n\r\n");
String content = request.substring(locate + 4, request.length());
if (content.contains("username=")) {
parts = content.split("&");
parts = parts[0].split("=");
username = parts[1];
}
}
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n\r\n".getBytes());
out.write("<html><head><title>HelloWorld</title></head><body>".getBytes());
out.write(new String("<h1>hello: " + username + "</h1></body></html>").getBytes());
}
}
1.6.5. 发布基于Web的应用程序,即Web应用
-
以浏览器作为展示客户端界面的窗口
-
客户端界面一律表现为网页形式,网页由HTML语言编写,具有交互功能
-
能完成与桌面应用程序类型的功能
-
使用浏览器/服务器架构(B/S),浏览器与服务器之间采用HTTP协议通信
-
Web应用通过Web服务器来发布
- 发展出了MVC设计模式
1.6.6. 发布Web服务
- Web服务简单理解,被客户端远程调用的各种办法
- Web服务架构采用SOAP(Simple Object Access Protocol,简单对象访问协议)
- SOAP规定客户与服务器一律用XML语言进行通信.(XML Extensible Markup Language,可扩展标记语言)
- Web服务架构采用SOAP(Simple Object Access Protocol,简单对象访问协议)
1.6.7. 推出Web2.0,它是全民共建的Web
1.7 处理HTTP请求参数及HTML表单
-
get
- 例子: http://localhost:8080/servlet/HelloServlet?username=Tom&password=123456
- "?"后面的字符串就是HTTP请求参数.
- HTTP请求参数是客户端向服务器端发送的字符串形式的数据,采用"参数名=参数值"的格式,多个参数用"&"隔开
- get请求参数放在HTTP请求的第一行的URI后面.例: GET /servlet/HelloServlet?username=Tom&password=123456 HTTP/1.1
-
post
-
post请求参数放在HTTP请求的正文部分
-
** HTML表单
-
input框
<input type="text" name="username" value="Tom">
- name属性对应请求参数名,value属性对应请求参数值
-
1.8 客户端向服务器端上传文件
HTML
<from name="uploadForm" method="POST" enctype="MULTIPART/FORM-DATA" action="serlet/UpLoadServlet">
<input type="file" name="filedata">
UploadServlet
package server;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.StringReader;
public class UploadServlet implements Servlet {
@Override
public void init() throws Exception {
System.out.println("UploadServlet is inited");
}
@Override
public void service(byte[] requestBuffer, OutputStream out) throws Exception {
String request = new String(requestBuffer);
// 获得HTTP请求的头
String headerOfRequest = request.substring(request.indexOf("\r\n") + 2, request.indexOf("\r\n\r\n"));
BufferedReader br = new BufferedReader(new StringReader(headerOfRequest));
String data = null;
String boundary = null;
while ((data = br.readLine()) != null) {
if (data.contains("Content-Type")) {
boundary = data.substring(data.indexOf("boundary=") + 9, data.length()) + "\r\n";
break;
}
}
if (boundary == null) {
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n\r\n".getBytes());
out.write("Uploading is failed".getBytes());
return;
}
// 第一个boundary出现的位置
int index1OfBoundary = request.indexOf(boundary);
int index2OfBoundary = request.indexOf(boundary, index1OfBoundary + boundary.length());
int index3OfBoundary = request.indexOf(boundary, index2OfBoundary + boundary.length());
// 文件部分的正文部分开始前的位置
int beforeOfFilePart = request.indexOf("\r\n\r\n", index2OfBoundary) + 3;
// 文件部分的正文部分结束后的位置
int afterOfFilePart = index3OfBoundary - 4;
// 文件部分的头的第一行结束后的位置
int afterOfFilePartLine1 = request.indexOf("\r\n", index2OfBoundary + boundary.length());
// 文件部分的头的第二行
String header2OfFilePart = request.substring(index2OfBoundary + boundary.length(), afterOfFilePartLine1);
// 上传文件的名字
String fileName = header2OfFilePart.substring(header2OfFilePart.lastIndexOf("\\") + 1,
header2OfFilePart.length() - 1);
// 文件部分的正文部分之后的字符串的字节长度
int len1 = request.substring(0, beforeOfFilePart + 1).getBytes().length;
// 文件部分的正文部分之后的字符串的字节长度
int len2 = request.substring(afterOfFilePart, request.length()).getBytes().length;
// 文件部分的正文部分的字节长度
int fileLen = requestBuffer.length - len1 - len2;
FileOutputStream f = new FileOutputStream("server\\root\\" + fileName);
f.write(requestBuffer, len1, fileLen);
f.close();
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n\r\n".getBytes());
out.write("<html><head><title>HelloWorld</title></head><body>".getBytes());
out.write(new String("<h1>Uploading is finished.<br></h1>").getBytes());
out.write(new String("<h1>FileName: " + fileName + "<br></h1>").getBytes());
out.write(new String("<h1>FileSize: " + fileLen + "<br></h1></body></html>").getBytes());
}
}