??? 之前有javaeyer推荐了一本书《how tomcat works》,今天晚上看了看,确实不错,第一眼就着迷了。 于是乎就学着书上的例子敲了敲,学会了一个简单web服务器的大概实现,当然,这个简直就无法称之为web服务器,但是也算是走进web服务器的第一步吧。
???? 这篇文章仅限于学习记录,文笔凌乱之处,还望各位见谅。 OK,下面进入正题;
????? 开始之前,首先我们要清楚以下几个内容。
?
??? ?首先,一个最简单服务器包括三个部分:
???? web服务器----HttpServer
??????????????? 请求---Request
???? ?????????? 响应---Response
?
? ?? 这个服务器如何使用:
????? 1. 请求静态资源 通过 http://localhost:8090/index.html
?
??????????????????? 成功 则返回页面, 否则会返回 File Not Found 的错误信息.
?????? PS:比如上面的index.html 必须放在一个webroot目录下.
????
????? 2. 服务器的关闭通过uri来处理
??????? 通过http://lcoalhost:8090/SHUTDOWN
这个Uri来停止服务器.
???
?? 需要注意的其他几个知识点:
????? 1. HTTP/1.1 协议的知识。? 比如 请求,响应的结构。 发送与接收形式等.
????? 2. Java中网络的相关只是???? ServerSocket 与 Socket 的使用.
?
?OK, 放上代码, 代码只有3个类,都挺简单的, 这个应用只是一个最最简单的雏形:
?
?主类: HttpServer
?
package chapter1.simplewebserver; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.ServerSocket; import java.net.Socket; /** * * 下午11:59:29 * @author gogole_09 * 简单web服务器 */ public class HttpServer { //定位到webroot目录 public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot"; //停止命令 private static final String SHUTDOWN_COMMAND="/SHUTDOWN"; //是否接收到了关闭命令 private boolean shutdown=false; /** * 等待命令 */ public void await(){ ServerSocket serverSocket=null; int port=8090; try { serverSocket=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1")); } catch (Exception e) { e.printStackTrace(); System.exit(1); } //监听端口,等待请求。 while(!shutdown){ Socket socket=null; InputStream input=null; OutputStream output=null; try { socket=serverSocket.accept(); //输入流 有过jsp编程的哥们都知道这是干嘛的 input=socket.getInputStream(); //输出流 output=socket.getOutputStream(); //构建请求 Request request=new Request(input); //解析请求信息 request.parse(); //响应类 Response response=new Response(output); response.setRequest(request); response.sendStaticResource(); //处理完毕,关闭 socket.close(); //检查提供的URI是否为shutdown命令 shutdown=request.getUri().equals(SHUTDOWN_COMMAND); } catch (Exception e) { e.printStackTrace(); continue; } } } public static void main(String[] args) { HttpServer server=new HttpServer(); server.await(); } }
?
接下来是请求类:?
?? Request
?
package chapter1.simplewebserver; import java.io.IOException; import java.io.InputStream; /** * * 上午12:09:36 * @author gogole_09 * 简单服务器的请求类 */ public class Request { private InputStream input; private String uri; public Request(InputStream in) { this.input=in; } /** * 解析Uri操作 */ public void parse() { StringBuffer buf=new StringBuffer(); int i; byte[] buffer=new byte[2048]; try { i=input.read(buffer); } catch (IOException e) { e.printStackTrace(); i=-1; } for(int j=0;j<i;j++){ buf.append((char)buffer[j]); } System.out.println(buf.toString()); uri=parseUri(buf.toString()); } /** * 解析Uri * 为什么要以' '为做标识 , 这里需要了解HTTP协议的相关结构; * 一个请求行 以 请求方法开头 + 请求URI+ 请求协议版本 + CRLF字符结束 * 比如,你请求index.html 用GET方式 ,那么Uri形式为: * * GET /index.html HTTP/1.1 * * * @param requestString * @return */ private String parseUri(String requestString){ int index1,index2; index1=requestString.indexOf(' '); if(index1!=-1){ index2=requestString.indexOf(' ',index1+1); if(index2>index1){ return requestString.substring(index1+1,index2); } } return null; } public String getUri() { return uri; } }
?
?
?有了请求了,服务器就得响应啊,试想,我发个请求没反应,你的第一反应是不是想砸电脑呢?
?OK,Response类来了。
?
package chapter1.simplewebserver; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; /** * * 上午12:10:58 * * @author gogole_09 简易服务器的响应类 */ public class Response { /** * HTTP响应格式:= Status-Line * *((general-header|response-header|entity-header)CRLF) CRLF [message-body] * Status-Line=HTTP-Version SP(空格) Status-Code SP Reason-Phrase CRLF * */ private static final int BUFFER_SIZE = 1024; Request request; private OutputStream output; public Response(OutputStream output) { this.output = output; } public void setRequest(Request request) { this.request = request; } /** * 服务器解析并发送静态资源 * @throws IOException */ public void sendStaticResource() throws IOException { byte[] bytes = new byte[BUFFER_SIZE]; FileInputStream fis = null; try { File file = new File(HttpServer.WEB_ROOT, request.getUri()); if (file.exists()) { fis = new FileInputStream(file); int ch = fis.read(bytes, 0, BUFFER_SIZE); while (ch != -1) { output.write(bytes, 0, ch); ch = fis.read(bytes, 0, BUFFER_SIZE); } } else { //文件没找到 String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + "Content-Type:text/html\r\n" + "Content-Length:23\r\n"+"\r\n<ht>File Not Found</h1>"; output.write(errorMessage.getBytes()); } } catch (Exception e) { System.out.println(e.toString()); }finally{ if(fis!=null) fis.close(); } } }
?
?
?
?OK,现在这个应用可以运行了,但是你运行起来,没办法用,为什么, 因为还需要有用户请求的静态资源。
? 在HttpServer类中,我们hard code了一个webroot目录,约定把所有的静态资源都放到这个目录下面的。
??下面我们就把这个目录建好,并放一点资源进去 ,我是这样放的, 如图:
?
?
?index.html 中 就只有一行代码:
?
?
?
?OK, 下面我们运行一下 HttpServer ,得到页面 如下图:
?
? 在控制台,你将会看到程序打印出:
?
?
?
?接着,我们尝试请求一个不存在的资源:?? 我们会的到一个404的错误页面.
?
OK, 一个简单的web服务器就完成了, 虽然简单,但是我们可以通过这个了解一个大概的流程。
?以及复习一下java net包与HTTP/1.1协议的一些知识。
?
也希望这个篇文章对都有需要的人有帮助。
?
?
?PS: 最近有很多朋友都问这本书哪里有买,我本人并非看的纸质的,而是电子版的, 需要下载的可以去新浪共享频道找找, 我的是从哪里下来的, 中文书名就叫 <tomcat工作原理>
是Http Watch. 查看请求url,传递的参数,以及服务器返回数据的好东西。
嘿嘿,是的。 书名写错了,马上修正。
占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理
tomcat内部也是处理socket的, 不知有什么不对吗?
占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理
这个port监听不止是单独处理关闭,所有的请求都通过端口的,只是为了简单,根据URI来确定是否关闭。 实际在tomcat内部,还用到了ShutDownHook这个东西来处理 tomcat的关闭。 有兴趣的可以看看how tomcat works的ShutDownHook这个章节。 呵呵, 欢迎交流。
现在的场景是我用sh脚本nohup启动一个java进程后台运行,然后怎么友好关闭这个线程呢? 最简单的就是向一个端口发stop请求。
占用一个port监听关闭命令,呵呵。 这里可以对java项目的友好关闭做一下处理
这个port监听不止是单独处理关闭,所有的请求都通过端口的,只是为了简单,根据URI来确定是否关闭。 实际在tomcat内部,还用到了ShutDownHook这个东西来处理 tomcat的关闭。 有兴趣的可以看看how tomcat works的ShutDownHook这个章节。 呵呵, 欢迎交流。