自己动手编写web server(二)

来源:互联网 发布:清华大学软件学院考研 编辑:程序博客网 时间:2024/05/18 23:27

最近笔者有点忙啊,每天上班,晚上回来写毕业论文,没有太多空闲时间来学东西了。上一篇文章写完,自己读了几遍,感觉笔者的表达能力和文字功底确实垃圾,高考语文也就值103分,没有遗憾!!!

笔者有个习惯,就是遇到不会的东西总爱网上搜一搜,然后看各种博客,最近也浏览了一些技术大牛的博客,他们的浏览量和回复超越笔者的博客几条街。。。。笔者决心要写出他们那样的博客!!


闲话扯多了,进入正题!!!

上一篇,简单描述了HTTP协议,以及我们的思路,接下来,我们详细地来实现这个server,首先我们认识一下Socket!!!

说实话,笔者对网络编程一直是望而却步,究其原因,就是这个Socket,各种书或者教材翻译成“套接字”,笔者当时第一次接触这个东西时,年幼无知、不了解翻译人员的意思、发散思维较差等等原因,一直无法理解“套接字”的翻译表达了什么意思!!!现在依然无法理解!!!

Socket

现在给大家讲一下笔者对Socket的理解:Socket就是两台电脑之间的连接。。。那么两台电脑如何连接?现实生活中,我们用网线、设置IP地址等等,那么Socket也一样,充当网线与IP的功能。使用Socket对象,我们要为他设置IP以及端口,Socket对象为我们提供了输入输出功能,就像网线,可以传输数据。

public Socket (java.lang.String host, int port)

上面是Socket的其中一个构造函数,第一个参数:要请求的服务器名称或者IP地址;第二个参数:服务器端的端口号。

一旦你成功创建了一个Socket对象,你就可以通过这个对象获得与服务器之间的输入输出流。如果你要发送字节给服务器,使用getOutputStream方法来获取一个java.io.OutputStream对象,这就是输出流;如果你要读取服务器发送给你的字节,使用getInputStream方法用来返回一个java.io.InputStream对象,这是输入流。java的读取流,可以参考我以前的blog:http://blog.csdn.net/zsmj_2011/article/details/7975661和http://blog.csdn.net/zsmj_2011/article/details/7977754

ServerSocket

上面介绍了Socket类,这个类主要代表客户端与服务器端的连接,那大家肯定会想到,服务器端如何接受这个连接?如何设置监听端口?ServerSocket类主要是做这些事情:指定服务器监听端口和地址;等待客户端连接;新建一个Socket来处理客户端连接。
public ServerSocket(int port, int backLog, InetAddress bindingAddress);

上面是ServerSocket的一个构造函数,第一参数:指定服务器端监听端口号;第二参数:指定服务器端处理请求队列的大小,第三个参数:指定监听的IP地址,一般是本机。
当你成功创建了ServerSocket对象时,可以使用accept方法,监听客户端的请求,当有客户端连接进来,accept方法返回一个Socket对象,通过Socket对象,与客户端进行交互。

web server 设计

上面介绍了客户端与服务器端所使用的类,我们主要的目的是编写一个web server,因此,主要是服务器端编程。
首先,通过上一篇介绍的HTTP协议知道,服务器端需要处理客户端的请求--Request,发送响应---Response给客户端。因此,我们设计两个类:Request和Response,分别对应HTTP协议中的Request和Response;还需要一个类WebServer,就是服务器主线程类,用于接收客户端请求,调用Request与Response两个类。
不废话了,贴代码,很简单,网上也有很多类似的
package com.la.webserver;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;public class WebServer {//设置静态资源的目录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 static void main(String[] args) {WebServer server=new WebServer();server.await();}public void await() {ServerSocket serverSocket=null;//监听8080端口int port=8080;try {serverSocket=new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));} catch (IOException e) {e.printStackTrace();System.exit(1);}//循环等待客户端连接while (!shutdown) {Socket socket=null;InputStream inputStream=null;OutputStream outputStream=null;try {socket=serverSocket.accept();//当有客户端连接时inputStream=socket.getInputStream();//获取请求字符流outputStream=socket.getOutputStream();//输出流//处理客户端请求---RequestRequest request=new Request(inputStream);request.parse();//向客户端发送ResponseResponse response=new Response(outputStream);response.setRequest(request);response.sendStaticResource();//关闭socketsocket.close();//检查如果是shutdown命令,关闭服务器shutdown=request.getUri().equals(SHUTDOWN_COMMAND);} catch (Exception e) {e.printStackTrace();continue;}}}}

package com.la.webserver;import java.io.IOException;import java.io.InputStream;public class Request {private InputStream inputStream;private String uri;public String getUri() {return uri;}public void setUri(String uri) {this.uri = uri;}public  Request(InputStream inputStream) {this.inputStream=inputStream;}public void parse() {//从socket读取字符StringBuffer request=new StringBuffer(2048);int i;byte[] buffer=new byte[2048];try {i=inputStream.read(buffer);} catch (IOException e) {e.printStackTrace();i=-1;}for (int j = 0; j < i; j++) {request.append((char)buffer[j]);}System.out.println(request.toString());uri=parseUri(request.toString());}/** * 从客户端请求字符串中截取请求资源uri * @param requeString 请求字符串,包括请求头、请求方法、请求资源的uri * @return 请求资源的uri */private String parseUri(String requeString){int index1,index2;//比如GET /index.html HTTP/1.1//请求方法、请求资源、版本之间以空格隔开index1=requeString.indexOf(' ');if (index1!=-1) {index2=requeString.indexOf(' ', index1+1);if (index2>index1) {return requeString.substring(index1+1, index2);}}return null;}}

package com.la.webserver;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.OutputStream;public class Response {private static final int BUFFER_SIZE=1024;Request request;OutputStream outputStream;public  Response(OutputStream outputStream) {this.outputStream=outputStream;}public void setRequest(Request request) {this.request=request;}//发送静态资源public void sendStaticResource() throws IOException {byte[] bytes=new byte[BUFFER_SIZE];FileInputStream fis=null;try {File file=new File(WebServer.WEB_ROOT, request.getUri());if (file.exists()) {//如果请求的静态资源文件存在fis=new FileInputStream(file);int ch=fis.read(bytes, 0, BUFFER_SIZE);while (ch!=-1) {outputStream.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:test/html\r\n"+"Content-Length:23\r\n"+"\r\n"+"<h1>File Not Found</h1>";outputStream.write(errorMessage.getBytes());}} catch (Exception e) {System.out.println(e.toString());}finally{if (fis!=null) {fis.close();}}}}

首先你的程序目录里要有个文件夹“webroot”,这个文件夹里要有你请求的静态资源,运行效果如下:

在浏览器中输入地址

运行正常,说明我们的webserver还是不错的,可以工作!!!

需要改进的地方

这只是一个简单的webserver,只能返回静态资源,像Jsp这样的页面,需要更多的处理;另外,这个服务器一次只能响应一个请求,也就是说其他想要连接服务器的请求只能等候;阻塞通信,可以使用NIO来进行改进。
接下来,我们的目标是:
  1. 可以处理servlet、jsp;
  2. 使用多线程技术,优化服务器处理
  3. 使用NIO技术
敬请期待下一篇blog!
原创粉丝点击