1,分析
1,首先要有一个类似web.xml的文件来注册Servlet
2,要有一个BaseServlet
3,request和response实际上就是输入输出流
4,容器启动需要加载配置文件,保存路径和servlet的映射信息
5,利用死循环,socket接收请求,处理请求
2,BIO版本
1)BaseServlet
public abstract class BaseServlet {
public void service(MyRequest request, MyResponse response)throws Exception{
if ("GET".equalsIgnoreCase(request.getMethod()))
doGet(request,response);
else
doPost(request,response);
}
protected abstract void doPost(MyRequest request, MyResponse response)throws Exception;
protected abstract void doGet(MyRequest request, MyResponse response)throws Exception;
}
2)MyRequest
public class MyRequest {
private String method;
private String url;
public MyRequest(InputStream in) {
try {
String context = "";
byte[] buffer = new byte[1024];
int len = 0;
if ((len = in.read(buffer)) > 0) {
context=new String(buffer,0,len);
}
String line =context.split("\\n")[0];
String[]arr=line.split("\\s");
this.method=arr[0];
this.url=arr[1].split("\\?")[0];
System.out.println(context);
} catch (IOException e) {
e.printStackTrace();
}
}
public String getUrl() {
return url;
}
public String getMethod() {
return method;
}
}
3)MyResponse
public class MyResponse {
OutputStream outputStream;
public MyResponse(OutputStream out) {
this.outputStream = out;
}
public void write(String msg) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK\n")
.append("Content-Type: text/html;\n")
.append("\r\n")
.append(msg);
outputStream.write(sb.toString().getBytes());
}
}
4)web.properties
servlet.one.url=/firstServlet.do
servlet.one.className=com.yhd.servlet.FirstServlet
5)FirstServlet
public class FirstServlet extends BaseServlet {
@Override
public void doGet(MyRequest request, MyResponse response)throws Exception{
this.doPost(request,response);
}
@Override
public void doPost(MyRequest request, MyResponse response)throws Exception {
response.write("This is the First Servlet");
}
}
6)YhdTomcat
public class YhdTomcat {
private int port = 8080;
private ServerSocket server;
private Map<String, BaseServlet> servletMap = new ConcurrentHashMap<>();
private Properties webxml = new Properties();
private void init() {
try {
String WEB_INT = this.getClass().getResource("/").getPath();
FileInputStream fis = new FileInputStream(WEB_INT + "web.properties");
webxml.load(fis);
for (Object key : webxml.keySet()) {
String k = key.toString();
if (k.endsWith(".url")) {
String servletName = k.replaceAll("\\.url$", "");
String url = webxml.getProperty(k);
String className = webxml.getProperty(servletName + ".className");
BaseServlet obj = (BaseServlet) Class.forName(className).newInstance();
servletMap.put(url, obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void process(Socket client) throws Exception {
InputStream inputStream = client.getInputStream();
OutputStream outputStream = client.getOutputStream();
MyRequest request = new MyRequest(inputStream);
MyResponse response = new MyResponse(outputStream);
String url = request.getUrl();
if (servletMap.containsKey(url))
servletMap.get(url).service(request, response);
else
response.write("404 Not Found");
outputStream.flush();
outputStream.close();
inputStream.close();
}
public void start() {
init();
try {
server = new ServerSocket(this.port);
System.out.println("tomcat is start , listening port is " + port);
for (; ; ) {
Socket client = server.accept();
process(client);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new YhdTomcat().start();
}
}
3,Netty版
1)MyRequest
public class MyRequest {
private ChannelHandlerContext ctx;
private HttpRequest req;
public MyRequest(ChannelHandlerContext ctx, HttpRequest req) {
this.ctx = ctx;
this.req = req;
}
public String getUrl() {
return req.getUri();
}
public String getMethod() {
return req.getMethod().name();
}
public Map<String, List<String>> getParameters() {
QueryStringDecoder decoder = new QueryStringDecoder(req.uri());
return decoder.parameters();
}
public String getParameter(String name) {
Map<String, List<String>> params = getParameters();
List<String> param = params.get(name);
if (null == param)
return null;
else
return param.get(0);
}
}
2)MyResponse
public class MyResponse {
private ChannelHandlerContext ctx;
private HttpRequest req;
public MyResponse(ChannelHandlerContext ctx, HttpRequest req) {
this.ctx = ctx;
this.req = req;
}
public void write(String out) throws Exception {
try {
if (out == null || out.length() == 0)
return;
// 设置 http协议及请求头信息
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(out.getBytes("UTF-8")));
response.headers().set("Content-Type", "text/html;");
ctx.write(response);
} finally {
ctx.flush();
ctx.close();
}
}
}
3)MyTomcat
public class MyTomcat {
private int port = 8080;
private ServerSocket server;
private Map<String, BaseServlet> servletMap = new ConcurrentHashMap<>();
private Properties webxml = new Properties();
private void init() {
try {
String WEB_INT = this.getClass().getResource("/").getPath();
FileInputStream fis = new FileInputStream(WEB_INT + "web.properties");
webxml.load(fis);
for (Object key : webxml.keySet()) {
String k = key.toString();
if (k.endsWith(".url")) {
String servletName = k.replaceAll("\\.url$", "");
String url = webxml.getProperty(k);
String className = webxml.getProperty(servletName + ".className");
BaseServlet obj = (BaseServlet) Class.forName(className).newInstance();
servletMap.put(url, obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() {
init();
worker();
}
private void worker() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
// Worker线程
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap server = new ServerBootstrap();
// 链路式编程
server.group(bossGroup, workerGroup)
// 主线程处理类,看到这样的写法,底层就是用反射
.channel(NioServerSocketChannel.class)
// 子线程处理类 , Handler
.childHandler(new ChannelInitializer<SocketChannel>() {
// 客户端初始化处理
@Override
protected void initChannel(SocketChannel client) throws Exception {
// 无锁化串行编程
//Netty对HTTP协议的封装,顺序有要求
// HttpResponseEncoder 编码器
client.pipeline().addLast(new HttpResponseEncoder());
// HttpRequestDecoder 解码器
client.pipeline().addLast(new HttpRequestDecoder());
// 业务逻辑处理
client.pipeline().addLast(new TomcatHandler());
}
})
// 针对主线程的配置 分配线程最大数量 128
.option(ChannelOption.SO_BACKLOG, 128)
// 针对子线程的配置 保持长连接
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 启动服务器
ChannelFuture f = server.bind(port).sync();
System.out.println("Tomcat is start ,listening port is :" + port);
f.channel().closeFuture().sync();
}catch (Exception e){
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new MyTomcat().start();
}
private class TomcatHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest){
HttpRequest req = (HttpRequest) msg;
// 转交给我们自己的request实现
MyRequest request = new MyRequest(ctx,req);
// 转交给我们自己的response实现
MyResponse response = new MyResponse(ctx,req);
// 实际业务处理
String url = request.getUrl();
if(servletMap.containsKey(url)){
servletMap.get(url).service(request, response);
}else{
response.write("404 - Not Found");
}
}
}
}
}