动手实现一个简单的CS模型(仿照tomcat)
Tomcat 服务器是Web 应用服务器,浏览器则可看作web应用客户端。客户端与服务端是通过点对点的socket通信,在http协议规范下进行的。
http协议分为请求部分和响应部分
请求部分格式
请求行 : 请求方法 请求路径 http协议及版本
请求头 : 键值对
请求空行
请求体 : get方法无,post方法有
例子:
GET / HTTP/1.1
Host: www.baidu.com
...
响应部分格式
响应行 http协议及版本 状态码 响应提示信息
响应头 键值对
响应空行
响应体 服务器返回的数据
HTTP/1.1 200 ok
server: Apache-Coyote/1.1
...
网页数据
基于socket和请求,模拟浏览器请求数据
public class TestClient {
public static void main(String[] args) {
Socket s = null;
OutputStream os = null;
InputStream is = null;
try {
s = new Socket("www.baidu.com",80);
os = s.getOutputStream();
is = s.getInputStream();
os.write("GET / HTTP/1.1\n".getBytes());
os.write("HOST:www.baidu.com\n".getBytes());
os.write("\n".getBytes());
int i = is.read();
while(i!=-1){
System.out.print((char)i);
i = is.read();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(s!=null){
try {
s.close();
} catch (IOException e) { }
}
if(os!=null){
try {
os.close();
} catch (IOException e) { }
}
if(is!=null){
try {
is.close();
} catch (IOException e) { }
}
}
}
}
基于socket和响应,模拟服务器响应客户端
package com.lovehot.learn.BS;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 州牧
* @description 服务器响应请求
* @since 2022-03-26 19:26
*/
public class TestServer {
public static void main(String[] args) {
ServerSocket ss = null;
Socket s = null;
OutputStream os = null;
try {
ss = new ServerSocket(8080);
while(true) {
/*监听8080端口*/
s = ss.accept();
os = s.getOutputStream();
os.write("HTTP/1.1 200 ok\n".getBytes());
os.write("Content-Type: text/html;charset=utf-8\n".getBytes());
os.write("Server: zhoumu/2020\n".getBytes());
os.write("\n\n".getBytes());
StringBuffer buffer = new StringBuffer();
buffer.append("<html>");
buffer.append("<head>");
buffer.append("<title>");
buffer.append("百度一下,你就知道");
buffer.append("</title>");
buffer.append("</head>");
buffer.append("<body>");
buffer.append("度娘");
buffer.append("</body>");
buffer.append("</html>");
os.write(buffer.toString().getBytes());
/*将缓冲区内容也返回*/
os.flush();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(s != null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
完整版本
tomcat中有分为静态请求和动态请求,在MyServer简单的分离了动静态的请求,静态请求直接返回静态资源,动态请求则会由Servlet流程负责返回动态数据,为了简便起见这里未链接数据库而实现业务。
MyServlet中定义servlet的基本行为,MyServer被请求,解析请求和分离请求。
MyServlet
package com.lovehot.learn.mycat;
import java.io.InputStream;
import java.io.OutputStream;
public interface MyServlet {
//初始化
void init();
//服务
void Service(InputStream is, OutputStream ops);
//销毁
void destroy();
}
MyServletImpl
package com.lovehot.learn.mycat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author 州牧
* @description
* @since 2022-03-26 22:11
*/
public class MyServletImpl implements MyServlet{
@Override
public void init() {
System.out.println("初始化servlet生存环境");
}
@Override
public void Service(InputStream is, OutputStream os) {
try {
os.write("已经获取到前端传来的数据,servlet进行处理".getBytes());
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void destroy() {
System.out.println("生命周期结束,销毁");
}
}
MyServer
package com.lovehot.learn.mycat;
import com.lovehot.learn.version2.Servlet;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* @author 州牧
* @description 服务器响应请求
* @since 2022-03-26 19:26
*/
public class MyServer {
//获取到项目下的WebContent目录
public static final String WEB=System.getProperty("user.dir") + "\\" + "Web";
// 代表客户端要请求资源的路径
public static String url = "";
public static Map<String,String> map = new HashMap<>();
//加载配置信息
static {
map.put("aa","MyServletImpl");
}
public static void main(String[] args) {
ServerSocket ss = null;
Socket s = null;
OutputStream os = null;
InputStream is = null;
try {
ss = new ServerSocket(8080);
/*监听端口*/
s = ss.accept();
os = s.getOutputStream();
is = s.getInputStream();
//获取url
parseUrl(is);
if(url.indexOf(".")!=-1){
//静态资源
sendStaticResource(os);
}else{
//动态资源
sendDynamicResource(os,is);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(s != null){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void sendDynamicResource(OutputStream os, InputStream is) {
try {
os.write("HTTP/1.1 200 OK\n".getBytes());
os.write("Server:apache\n".getBytes());
os.write("Content-Type:text/html;charset=utf-8\n".getBytes());
os.write("\n".getBytes());
if(map.containsKey(url)){
String value = map.get(url);
Class clazz = Class.forName(value);
Servlet servlet = (Servlet)clazz.newInstance();
servlet.init();
servlet.Service(is, os);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void sendStaticResource(OutputStream os) {
byte[] bytes = new byte[1024*2];
FileInputStream fis = null;
File file = new File(WEB+url);
try{
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 i = fis.read(bytes);
while(i!=-1){
os.write(bytes,0,i);
i = fis.read(bytes);
}
}else {
//如果文件不存在,客户端响应文件不存在消息
os.write("HTTP/1.1 404 not found\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());
os.flush();
}
}catch (Exception e){
throw new RuntimeException("发送资源出错");
}finally {
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void parseUrl(InputStream is) {
StringBuffer content = new StringBuffer(1024*3);
byte[] buffer = new byte[1024*3];
try {
int i = is.read(buffer);
for (int j = 0; j <i; j++) {
content.append((char) buffer[j]);
}
int index1,index2;
index1 =content.indexOf(" ");
index2 = content.indexOf(" ",index1+1);
/*"GET /demo.html HTTP/1.1*/
url = content.substring(index1+1,index2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过:https://www.bilibili.com/video/BV1AW41117Cu?spm_id_from=333.337.search-card.all.click 学习
完整版本源码:
链接:https://pan.baidu.com/s/12sCER8_sVPjJbwC7RkVG3A
提取码:java