JavaSE02_Day03(下)-WebServer项目(一、二、三)、Map接口、HashMap
一、WebServer项目
1.1 项目描述
1.2 版本一
实现功能:实现了一个Client向Server发送数据,Server接受到数据(客户端向服务端发送数据,服务端接收数据)
WebServer.java
package cn.tedu.core;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* webserver主类
* @author cjn
*
*/
public class WebServer {
//声明服务器端socket属性
private ServerSocket serverSocket;
/**
* 构造器
* 用于进行对属性的初始化操作,
* 也就意味着进行启动服务器端
*/
public WebServer() {
try {
System.out.println("正在启动服务器端......");
serverSocket = new ServerSocket(8888);
System.out.println("服务器端已经启动成功!!!");
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 服务器端处理客户端请求的方法
*/
public void start() {
try {
System.out.println("正在等待客户端连接......");
Socket socket = serverSocket.accept();
System.out.println("一个客户端已经连接完毕!!!");
//启动线程进行处理客户端的请求
ClientHandler clientHandler = new ClientHandler(socket);
Thread thread = new Thread(clientHandler);
thread.start();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WebServer server = new WebServer();
server.start();
}
}
ClientHandler.java
package cn.tedu.core;
/**
* 处理客户端请求的任务序列类
* @author cjn
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
public class ClientHandler implements Runnable{
//声明获取连接客户端以后,获取的socket对象
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
/**
* 线程所执行的任务序列逻辑
*/
public void run() {
try {
//获取输入流对象
InputStream is = socket.getInputStream();
//不能使用缓冲字符输入流BufferReader进行读取,使用字节方案进行读取
int d = -1;
while ((d = is.read()) != -1) {
//按照字节读取以后,再转换为字符
char c = (char)d;
System.out.print(c);
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试:启动WebServer主类,然后打开浏览器,在浏览器的地址栏中输入http://localhost:8888
控制台结果如下:
正在启动服务器端......
服务器端已经启动成功!!!
正在等待客户端连接......
一个客户端已经连接完毕!!!
GET / HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
同学常见的错误:
-
在浏览器的地址栏输入localhost:8888浏览器前面没有自动补填http://,那么自己手动加
-
未在WebServer主类中的start方法进行开启线程
1.3 版本二
实现功能:在ClientHandler类中自定义一个readLine方法,并测试按行读取功能
提示: 该方法可以读取一行的字符串,因为一个请求中的请求行和消息头都是一行一行的字符串,并且也CRLF结尾。
关于如何判断读到CRLF的示意图:
测试:启动WebServer主类,然后打开浏览器,在浏览器的地址栏中输入http://localhost:8888
控制台结果如下:
正在启动服务器端......
服务器端已经启动成功!!!
正在等待客户端连接......
一个客户端已经连接完毕!!!
GET / HTTP/1.1
1.4 版本三(上)
实现功能:解析请求(请求行,消息头,消息正文),将客户端发过来的请求以一个HttpRequest对象的形式保存,以便于后期处理时可以方便的获取请求中的各个信息。
提示:
-
新建一个包cn.tedu.http
-
在该包中定义一个类:HttpRequest
-
在该类中定义请求中各部分信息对应的属性
-
定义构造方法来进行初始化(解析)请求
HttpRequest.java
package cn.tedu.http;
/**
* 解析客户端发送的请求
* @author cjn
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
public class HttpRequest {
/*
* 定义请求行相关的属性信息
*/
private String method;//请求方式
private String url;//请求路径
private String protocol;
/*
* 定义消息头相关的属性信息
*/
/*
* 定义消息正文相关的属性信息
*/
/*
* 定义客户端连接的相关属性信息
*/
private Socket socket;//客户端返回的socket
private InputStream is;//通过socket获取的输入流
/**
* 构造方法,用于初始化解析请求
* @param socket 需要外界传入客户端socket对象
*/
public HttpRequest(Socket socket) {
try {
this.socket = socket;
this.is = socket.getInputStream();
System.out.println("HttpRequest开始解析请求......");
/*
* 解析请求:
* 1、解析请求行
* 2、解析消息头
* 3、解析消息正文
*/
parseRequestLine();
parseHeader();
parseContext();
System.out.println("HttpRequest解析请求完毕......");
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 解析请求行:
* 解析请求行,也就是对于当前请求行中的信息内容
* 进行拆分,将请求方式,请求路径和协议版本
* 存储到上方定义的属性中
* 例如:GET / HTTP/1.1
*/
public void parseRequestLine() {
try {
System.out.println("开始解析请求行");
//获取请求行操作
String line = readLine();
System.out.println("请求行:" + line);
//解析请求行操作(按照空格进行拆分),并且完成对相关的属性的赋值
String[] data = line.split("\\s");
method = data[0];
url = data[1];
protocol = data[2];
System.err.println("method:" + method);
System.err.println("url:" + url);
System.err.println("protocal:"