在学习javaweb的时候我感觉自己是很懵逼的,因为好像就是写servlet然后配置web.xml,就可以让服务器提供服务了,对于这整个过程都不是很了解。为了弄懂整个的过程,我就找了黑马的自制简易Tomcat的视频,现将代码记录与这篇文章中。相信你看完整个过程,会对javaweb有更深的理解
模拟浏览器给服务器发送请求
// 模拟浏览器向服务器发送HTTP请求
public class TestClient {
private static Socket socket = null;
private static InputStream is = null;
private static OutputStream os = null;
public static void main(String[] args) throws Exception {
//1_建立一个socket连接,连接itcast域名的80端口
socket = new Socket("www.itcast.cn", 80);
//2_获取到输出流对象
is = socket.getInputStream();
//3_获取到输入流对象
os = socket.getOutputStream();
//4_将HTTP协议的请求部分发送到服务端
os.write("GET index.html HTTP/1.1\n".getBytes());
os.write("HOST:www.itcast.cn\n".getBytes());
os.write("\n".getBytes());
//5_读取来自服务端的数据打印到控制台
int i = is.read();
while(i != -1){
System.out.print((char)i);
i = is.read();
}
//6_释放资源
if(socket!=null){
socket.close();
socket = null;
}
if(is!=null){
is.close();
is = null;
}
if(os!=null){
os.close();
os = null;
}
}
}
模拟服务器响应客户端请求
public class TestServer {
private static ServerSocket serverSocket = null;
private static Socket socket = null;
private static OutputStream os = null;
public static void main(String[] args) throws IOException {
try {
//1_创建ServerSocket对象,监听本机的8080端口号
serverSocket = new ServerSocket(8080);
while(true) {
//2_等待来自哭护短的请求获取和客户端对应的Socket对象
socket = serverSocket.accept();
//3_通过获取到的Socket对象获取到输出流对象
os = socket.getOutputStream();
//4_通过获取得到的输出流对象将HTTP协议的响应部分发送到客户端
os.write("HTTP/1.1 200 OK\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("Server:Apache-Coyote/1.1\n".getBytes());
os.write("\n\n".getBytes());
StringBuffer buf = new StringBuffer();
buf.append("<html>");
buf.append("<head><title>我是标题</title></head>");
buf.append("<body>");
buf.append("<h1> I am header 1</h1>");
buf.append("</body>");
buf.append("</html>");
os.write(buf.toString().getBytes());
os.flush();
}
} catch (Exception e){
e.printStackTrace();
} finally {
//5_释放资源
if(serverSocket != null){
serverSocket.close();
serverSocket = null;
}
if(socket != null){
socket.close();
socket = null;
}
if(os != null){
os.close();
os = null;
}
}
}
}
模拟服务器响应客户端对于静态资源的请求
我们要做的实际上就是读取客户端要访问的静态资源,然后将静态资源输出到客户端。
public class TestServer {
// 定义一个变量,存放服务器WebContent的绝对路径
public static String WEB_ROOT = System.getProperty("user.dir") + "\\" + "WebContent\\";
// 定义静态变量,用于存放本次请求的静态页面名称
private static String url = "";
public static void main(String[] args) throws IOException {
//System.out.println(WEB_ROOT);
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
OutputStream os = null;
// 创建一个ServerSocket,监听本机8080端口,等待客户端请求
try {
serverSocket = new ServerSocket(8080);
while(true){
socket = serverSocket.accept();
is = socket.getInputStream();
os = socket.getOutputStream();
// 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
parse(is);
// 发送静态资源
sendStaaticResource(os);
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源
if(serverSocket != null){
serverSocket.close();
serverSocket = null;
}
if(socket != null){
socket.close();
socket = null;
}
if(is != null){
is.close();
is = null;
}
if(os != null){
os.close();
os = null;
}
}
}
// 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
public static void parse(InputStream is) throws IOException {
// 定义一个变量,存放HTTP协议请求部分数据
StringBuffer content = new StringBuffer(2048);
// 定义一个数组,存放HTTP协议请求部分数据
byte[] buffer = new byte[2048];
// 定义一个变量i,代表读取数据到数组中之后,数据量的带下
int i;
// 读取数据
i = is.read(buffer);
// 遍历字节数组,将数据追加到content变量中
for(int j=0; j<i; j++){
content.append((char)buffer[j]);
}
// 打印HTTP协议请求部分数据
System.out.println(content);
// 截取客户端请求的资源路径demo.html,赋值给url
parseURL(content.toString());
}
// 截取客户端请求的资源路径demo.html,赋值给url
private static void parseURL(String content) {
int index1, index2;
index1 = content.indexOf(" ");
if(index1 != -1){
index2 = content.indexOf(" ", index1+1);
if(index2 > index1){
url = content.substring(index1+2, index2);
}
}
System.out.println(url);
}
// 发送静态资源
public static void sendStaaticResource(OutputStream os) throws IOException {
byte[] bytes = new byte[2048];
FileInputStream fis = null;
try{
File file = new File(WEB_ROOT, url);
if(file.exists()){
os.write("HTTP/1.1 200 OK\n".getBytes());
os.write("Server:apache-Coyote/1.1\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
fis = new FileInputStream(file);
// 读取静态资源的内容到数组中
int ch = fis.read(bytes);
while(ch!=-1){
os.write(bytes, 0, ch);
ch = fis.read(bytes);
}
} else {
os.write("HTTP/1.1 404 OK\n".getBytes());
os.write("Server:apache-Coyote/1.1\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
String errorMessage="file not found";
os.write(errorMessage.getBytes());
}
} catch (Exception e){
e.printStackTrace();
} finally {
if(fis!=null){
fis.close();
fis = null;
}
}
}
}
模拟在响应请求的过程中执行java程序
这个项目有点复杂。
我们最终实现的效果是,浏览器输入一个url。服务器解析完请求后,如果是动态请求就会通过反射调用相应的servlet提供服务;如果是静态请求就给客户端发送静态页面。
我们要写一个Servlet接口,所有的提供服务的java类都要实现这个接口
public interface Servlet {
public void init();
public void service(InputStream is, OutputStream os) throws IOException;
public void destroy();
}
然后我们需要编写AAServlet.java和BBServlet.java
AAServlet.java
public class AAServlet implements Servlet{
@Override
public void init() {
System.out.println("aaServlet...init");
}
@Override
public void service(InputStream is, OutputStream os) throws IOException {
System.out.println("aaServlet...service");
os.write("I am from AAServlet!".getBytes());
os.flush();
}
@Override
public void destroy() {
System.out.println("aaServlet...destroy");
}
}
BBServlet.java
public class BBServlet implements Servlet{
@Override
public void init() {
System.out.println("bbServlet...init");
}
@Override
public void service(InputStream is, OutputStream os) throws IOException {
System.out.println("bbServlet...service");
os.write("I am from BBServlet!".getBytes());
os.flush();
}
@Override
public void destroy() {
System.out.println("bbServlet...destroy");
}
}
之后我们需要创建一个配置文件conf.properities,在里面填写映射关系
conf.properities
aa=TOMCAT2.AAServlet
bb=TOMCAT2.BBServlet
TestServer.java
public class TestServer {
// 定义一个变量,存放服务器WebContent的绝对路径
public static String WEB_ROOT = System.getProperty("user.dir") + "\\" + "WebContent\\";
// 定义静态变量,用于存放本次请求的静态页面名称
private static String url = "";
// 定义一个静态的map,用来存储服务器中conf.properities中的信息
private static HashMap<String, String> map = new HashMap();
static {
Properties prop = new Properties();
try {
prop.load(new FileInputStream(WEB_ROOT + "//conf.properities"));
Set set = prop.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
String key = (String)iterator.next();
String value = prop.getProperty(key);
map.put(key, value);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException, ClassNotFoundException {
//System.out.println(WEB_ROOT);
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
OutputStream os = null;
// 创建一个ServerSocket,监听本机8080端口,等待客户端请求
try {
serverSocket = new ServerSocket(8080);
while(true){
socket = serverSocket.accept();
is = socket.getInputStream();
os = socket.getOutputStream();
// 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
parse(is);
if(url!=null){
if(url.indexOf(".") != -1){
// 发送静态资源
sendStaaticResource(os);
} else {
// 发送动态资源
sendDynamicResource(is, os);
}
}
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源
if(serverSocket != null){
serverSocket.close();
serverSocket = null;
}
if(socket != null){
socket.close();
socket = null;
}
if(is != null){
is.close();
is = null;
}
if(os != null){
os.close();
os = null;
}
}
}
// 发送动态资源
private static void sendDynamicResource(InputStream is, OutputStream os) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
// 将HTTP协议的响应头和响应行发送给对方
os.write("HTTP/1.1 OK\n".getBytes());
os.write("Server:Apache\n".getBytes());
os.write("Content-type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
// 判断map中是否存在一个key和本次待请求的资源路径一致
if(map.containsKey(url)){
// 如果包含指定的key,就可以获取到响应的value
String value = map.get(url);
// 通过反射将java程序加载到内存
Class clazz = Class.forName(value);
Servlet servlet = (Servlet)clazz.newInstance();
// 执行init()方法
servlet.init();
// 执行service()方法
servlet.service(is, os);
}
}
// 获取协议请求部分,截取客户端要访问的资源名称,将这个资源名称赋值给url
public static void parse(InputStream is) throws IOException {
// 定义一个变量,存放HTTP协议请求部分数据
StringBuffer content = new StringBuffer(2048);
// 定义一个数组,存放HTTP协议请求部分数据
byte[] buffer = new byte[2048];
// 定义一个变量i,代表读取数据到数组中之后,数据量的带下
int i;
// 读取数据
i = is.read(buffer);
// 遍历字节数组,将数据追加到content变量中
for(int j=0; j<i; j++){
content.append((char)buffer[j]);
}
// 打印HTTP协议请求部分数据
System.out.println(content);
// 截取客户端请求的资源路径demo.html,赋值给url
parseURL(content.toString());
}
// 截取客户端请求的资源路径demo.html,赋值给url
private static void parseURL(String content) {
int index1, index2;
index1 = content.indexOf(" ");
if(index1 != -1){
index2 = content.indexOf(" ", index1+1);
if(index2 > index1){
url = content.substring(index1+2, index2);
}
}
System.out.println(url);
}
// 发送静态资源
public static void sendStaaticResource(OutputStream os) throws IOException {
byte[] bytes = new byte[2048];
FileInputStream fis = null;
try{
File file = new File(WEB_ROOT, url);
if(file.exists()){
os.write("HTTP/1.1 200 OK\n".getBytes());
os.write("Server:apache-Coyote/1.1\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
fis = new FileInputStream(file);
// 读取静态资源的内容到数组中
int ch = fis.read(bytes);
while(ch!=-1){
os.write(bytes, 0, ch);
ch = fis.read(bytes);
}
} else {
os.write("HTTP/1.1 404 OK\n".getBytes());
os.write("Server:apache-Coyote/1.1\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
String errorMessage="file not found";
os.write(errorMessage.getBytes());
}
} catch (Exception e){
e.printStackTrace();
} finally {
if(fis!=null){
fis.close();
fis = null;
}
}
}
}
最后实现的效果如下
总结
这段程序我是跟着视频敲的,只是一个超级简化版的Tomcat,有许多不足的地方,但是对于理解Tomcat的运行原理是足够了。希望你能有所收获!