本人尚在java 学习阶段,不是技术大咖, 自认是技术宅, 有一点写东西的能力,因最近学习了java网络编程,决定手写一个web服务器,不喜勿喷,大神也请高抬贵手,不足之处还望指点一二,不胜感激!
目录结构
项目文件目录,是基于Maven的标准文件夹目录,src内包含7个类和一个接口,user包下的类为使用类。一个config文件夹用于存放服务器配置文件,webapps文件夹为服务器站点目录。
WebServer类
此类为本服务器的main线程类,负责启动服务器,不断接收客户端连接,使用ServerSocket对象实现基于tcp的网络对接,因为
并发线程会很多,所以使用线程池来不断接收客户端的连接,也避免了频繁创建线程的效率低下问题。这里的端口号与线程池的大
小都写入了config配置文件中,便于后期修改配置。接收到的soket对象放入ClientHandler线程类中使用。
构造函数中初始化ServerSocket对象与线程池对象。
public WebServer() {
try {
server = new ServerSocket(ServerContext.Port);
threadPool = Executors.newFixedThreadPool(ServerContext.MaxThread);
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器启动失败!");
}
}
}
写一个start方法不停循环接收浏览器请求,在调用ClientHandler线程类去处理浏览器请求。
public void start() {
try {
while (true) {
System.out.println("等待客户端连接");
Socket socket = server.accept();
threadPool.execute(new ClientHandler(socket));
}
} catch (Exception e) {
}
}
在main函数中启动服务端程序。
public static void main(String[] args) {
WebServer server = new WebServer();
server.start();
}
ClientHandler类
此类为客户端线程类,实现Runnable接口,是http连接的核心类,负责接收浏览器端发来的请求并返回响应消息,中转站的作用
。
run方法首先获取输入输出流并使用HttpRequest对象解析请求。
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
HttpRequset req = new HttpRequset(in);
boolean isPost = "POST".equals(req.getMethod());
HttpResponse res = new HttpResponse(out);
//把消息分为静态内容和动态内容
//--动态内容--
//是否为用户数据处理接口(动态内容)
if (ActionListen.isAction(req.getUri(), isPost)) {
ActionListen.doAction(req, res, req.getUri(), isPost);
return;
}
//--静态内容--
//获取请求类型
String type = req.getUri().substring(req.getUri().lastIndexOf(".") + 1);
//设置响应头
res.setHeader("Content-Type", ServerContext.getType(type));
//获取静态文件
File file = new File(ServerContext.WebRoot + req.getUri());
if (!file.exists()) {
//404 请求内容找不到
res.setStatus(404);
file = new File(ServerContext.WebRoot + "/" + ServerContext.NotFoundPage);
} else {
res.