本次改动:
完成客户端通过地址:http://localhost:8080/index.html,访问后可以看到该页面内容.
1.准备页面index.html
在项目目录中创建一个目录webapps,该目录在web应用。
在Tomcat中,可以理解 每个网站就是一个webapp,Tomcat作为服务端容器,可以同时保存多个网站内容,那么每个网站的内容可以作为一个子目录存到webapps中.
我们也可以参考这样的设计,在webapps中创建一个子目录myweb,然后将index.html页面存到myweb中,浏览器中输入的资源路径是一个相对路径,对于地址输入的:http://localhost:8080/myweb/index.html.
那么请求行中解析url的部分为:/myweb/index/index.html
所以我们要想实际找到该文件,就需要自行指定该相对路径的相对点.这里我们定义相对点为webapps目录,所以从url中取得路径都默认从webapps目录中开始找,那么相对路径就是:webapps/myweb/index.html
2.在ClientHandler中通过request对象获取用户请求的url地址,然后从webapps目录中找到对应的资源,并给客户端回复一个HTTP响应内容.
代码:
package com.se.day16;
import java.net.ServerSocket;
import java.net.Socket;
/*
* WebServer主类
*/
public class WebServer{
private ServerSocket server;
/*
* 构造方法用于初始化WebServer
*/
public WebServer() {
try {
/*
* 8080是tomcat使用的默认端口号
*/
System.out.println("正在初始化服务端..");
server=new ServerSocket(8080);
System.out.println("服务端初始化完毕!");
}catch(Exception e) {
e.printStackTrace();
}
}
/*
* 服务端开始工作的方式
*/
public void start() {
try {
System.out.println("等待客户端连接...");
Socket socket=server.accept();
System.out.println("一个客户端连接了!..");
//启动一个线程处理该客户端
ClientHandler handler=new ClientHandler(socket);
Thread t=new Thread(handler);
t.start();
}catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WebServer server=new WebServer();
server.start();
}
}
-------------------------------------------------------------------------
package com.se.day16;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
* 该线程是用来处理指定客户端的请求并给予响应
*/
public class ClientHandler implements Runnable{
private Socket socket;
public ClientHandler(Socket socket) {
this.socket=socket;
}
public void run() {
System.out.println("开始处理客户端请求!");
/*
* 处理客户端请求的大致流程
* 1.解析请求并用一个对象保存请求内容
* 2.创建响应对象,表示给客户端回复的内容
* 3.分析请求内容并处理该请求,将处理结果(即将给客户端回复的内容)设置到相应的对象中
* 4.将相应对象中的内容以HTTP相对格式发送回给客户端
*/
try {
HttpRequest request=new HttpRequest(socket);
String url=request.getUrl();
/*
* 得到url用户要请求的资源路径后,从Webapps目录中找到对应相对路径的资源
*
*/
File file=new File("webapps"+url);
if(file.exists()) {
System.out.println("找到该文件!");
//将该文件内容回复给客户端
OutputStream out=socket.getOutputStream();
/*
* 通过输出流,向客户端发送一个HTTP响应
* 三部分:1.状态行
* 2.响应头
* 3.响应正文
*
*/
//发送状态行
String line="HTTP/1.1 200 Ok";
out.write(line.getBytes("ISO8859-1"));
out.write(13); //written CR
out.write(10); //written LF
//发送响应头
line="Content-Type:text/html";
out.write(line.getBytes("ISO8859-1"));
out.write(13); //written CR
out.write(10); //written LF
//发送正文,将文件数据发送给客户端
FileInputStream fis=new FileInputStream(file);
byte[] data=new byte[1024*10];
int len=-1;
while((len=fis.read(data))!=-1) {
out.write(data, 0, len);
}
fis.close();
}else {
System.out.println("没有该文件");
}
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
socket.close();
}catch(Exception e) {
e.printStackTrace();
}
}
System.out.println("处理请求完毕!");
}
}
-------------------------------------------------------------------------
package com.se.day16;
import java.io.InputStream;
import java.net.Socket;
/*
* 请求对象
* HttpRequest的每一个实例用于表示客户端发送过来的请求数据
* 一个请求包含内容:请求行,消息头,消息正文信息
*/
public class HttpRequest{
private Socket socket;
private InputStream in;
/*
* 定义请求行相关信息
*/
//请求的方式:
private String method;
//请求的资源路径:
private String url;
//请求使用的http协议版本
private String protocol;
/*
* 通过给定的Socket获取对应客户端发送的请求信息并初始化当前HttpRequest对象
*
*/
public HttpRequest(Socket socket) {
System.out.println("开始解析请求");
try {
this.socket=socket;
this.in=socket.getInputStream();
/*
* 解析请求行分为三步:
* 1.解析请求行
* 2.解析消息头
* 3.解析消息正文
*/
parseRequestLine();
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("解析请求完毕");
}
/*
* 解析请求行
*/
private void parseRequestLine() {
System.out.println("开始解析请求行...");
try {
//读取请求行内容
String line=readLine();
System.out.println("请求行内容"+line);
//这里将来会抛出数组下标越界
//拆分字符串
String[] data=line.split("\\s");
this.method=data[0];
this.url=data[1];
this.protocol=data[2];
System.out.println("method:"+method);
System.out.println("url:"+url);
System.out.println("protocol:"+protocol);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println("解析请求行完毕!..");
}
/*
* 从输入流中读取一行字符串(以CRLF结尾),返回的字符串不包含的CRLF
*
*/
private String readLine() {
StringBuilder builder=new StringBuilder();
try {
/*
* 实现思路
* 从输出流中顺序读取若干字符,当连续读取到两个字符分别是CR回车13/LF10
*
*/
int d=-1;
//c1表示上次读到的字符,c2表示本次读到的字符
char c1='a',c2='a';
while((d=in.read())!=-1) {
c2=(char)d;
builder.append(c2);
if(c1==13&&c2==10) {
break;
}
c1=c2;
}
}catch(Exception e) {
e.printStackTrace();
}
return builder.toString().trim();
}
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
public String getProtocol() {
return protocol;
}
}