`
gogo_yueyue
  • 浏览: 3980 次
  • 性别: Icon_minigender_2
  • 来自: 北京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

JAVA使用Socket编写HTTP服务器

阅读更多
HTTP服务器核心就是Socket短连接
先简单说概念:
1、socket就是TCP/IP实现的套接字,就是应用层调用下层服务的接口。

2、短连接指的是连接建立后,双方进行数据交互(通常是一个数据包,也可以是多个),交互完毕后立即关闭连接的TCP/IP实现方式就是常说的短连接,最常见的短连接例子就是HTTP协议。

3、长连接则指的是双方交互完毕后,不关闭连接,而让连接一直空闲着等待下一次交互,这样在一次交互前就免去了再重新建立连接的消耗,本机测试一次 socket连接需要耗时23毫秒。

优点就是性能好。缺点有二,一是实现方式比较复杂,需要单独用线程收,发倒是无所谓;二是需要增加链路检测的机制,由于连接在空闲时双方都无法确认对端是否出现异常退出,因为根据TCP/IP,如果连接的一方正常退出,对端都会收到一个信号,而当一方异常退出时,对端是无法收到信号的,这时就会出现 connection reset、connection reset by peer和broken pipe异常。

接下来说说JAVA如何实现短连接。一、先来看发送到方法,这个比较简单。直接获得socket的OutputStream流,然后用write 方法,flush方法即可。这里要说明的就是,之前认为使用flush方法在底层就是一个TCP包,其实不然,上层何时按照上面策略封装TCP包上层根本无法知道,经过测试可知,下层封装的TCP包大小与flush无必然联系。这里有个参数可以设置,就是 sock.setTcpNoDelay(true),如果设置为true,则对于缓冲区不进行拼接,立即发送。这里涉及nagle算法,用于解决小封包问题,感兴趣的朋友可以自己搜索

热身运动
Java代码

   1. /**
   2.  * 单个文件的HTTP Server,输入本机地址,返回d:/index.html文件
   3.  */ 
   4. public class SingleFileHttpServer extends Thread { 
   5.  
   6.   private byte[] content; 
   7.   private byte[] header; 
   8.   private int port; 
   9.  
  10.   public SingleFileHttpServer(byte[] data, String encoding, String MIMEType) throws UnsupportedEncodingException { 
  11.     this.content = data; 
  12.     this.port = 80; 
  13.     String header = "HTTP/1.0 200 OK\r\n" + "Server: OneFile 1.0 \r\n"  
  14.         + "Content-length: " + this.content.length 
  15.         + "\r\n" + "Content-type: " + MIMEType + "\r\n"; 
  16.     this.header = header.getBytes(encoding); 
  17.   } 
  18.  
  19.   public void run() { 
  20.     ServerSocket server; 
  21.     try { 
  22.       server = new ServerSocket(port); 
  23.       System.out.println("Accepting connections on port " + server.getLocalPort()); 
  24.       System.out.println("Data to be sent:"); 
  25.       System.out.write(this.content); 
  26.       while (true) { 
  27.         Socket conn = null; 
  28.         try { 
  29.           conn = server.accept(); 
  30.           OutputStream out = conn.getOutputStream(); 
  31.           InputStream in = conn.getInputStream(); 
  32.           int temp; 
  33.           StringBuilder request = new StringBuilder(); 
  34.           if ((temp = in.read()) != -1) 
  35.             request.append((char) temp); 
  36.           if (request.toString().indexOf("HTTP/") != -1) 
  37.             out.write(header); 
  38.           out.write(content); 
  39.           out.flush(); 
  40.         } catch (IOException e) { 
  41.           throw new RuntimeException(e); 
  42.         } finally { 
  43.           conn.close(); 
  44.         } 
  45.       } 
  46.     } catch (IOException e) { 
  47.       throw new RuntimeException(e); 
  48.     } 
  49.   } 
  50.  
  51.   public static void main(String[] args) { 
  52.     try { 
  53.       String encoding = "ASCII"; 
  54.       String fileName = "D:/index.html"; 
  55.       String contentType = "text/plain"; 
  56.       if (fileName.endsWith("html") || fileName.endsWith("htm")) 
  57.         contentType = "text/html"; 
  58.       InputStream in = new FileInputStream(new File(fileName)); 
  59.       ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
  60.       int temp; 
  61.       while ((temp = in.read()) != -1) 
  62.         baos.write(temp); 
  63.       byte[] data = baos.toByteArray(); 
  64.       SingleFileHttpServer server =  
  65.           new SingleFileHttpServer(data, encoding, contentType); 
  66.       server.start(); 
  67.     } catch (FileNotFoundException e) { 
  68.     } catch (IOException e) { 
  69.     } 
  70.   } 
  71. } 
  72.  
  73.  
  74. /**
  75.  * 跳转地址,输入本机地址,自动跳转到sina
  76.  */ 
  77. public class Redirector implements Runnable { 
  78.  
  79.   private int port; 
  80.   private String siteUrl; 
  81.  
  82.   public Redirector(int port, String siteUrl) { 
  83.     this.port = port; 
  84.     this.siteUrl = siteUrl; 
  85.   } 
  86.  
  87.   public void run() { 
  88.     try { 
  89.       ServerSocket server = new ServerSocket(port); 
  90.       while (true) { 
  91.         Socket conn = server.accept(); 
  92.         Thread t = new RedirectThread(conn); 
  93.         t.start(); 
  94.       } 
  95.     } catch (IOException e) { 
  96.       throw new RuntimeException(e); 
  97.     } 
  98.   } 
  99.   private class RedirectThread extends Thread { 
100.  
101.     private Socket conn; 
102.  
103.     public RedirectThread(Socket conn) { 
104.       this.conn = conn; 
105.     } 
106.  
107.     public void run() { 
108.       try { 
109.         Writer out = new OutputStreamWriter(conn.getOutputStream()); 
110.         Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
111.         StringBuilder sb = new StringBuilder(); 
112.         int temp; 
113.         while (true) { 
114.           temp = in.read(); 
115.           if (temp == '\r' || temp == '\n' || temp == -1) 
116.             break; 
117.           sb.append((char) temp); 
118.         } 
119.         String request = sb.toString(); 
120.         int firstSpace = request.indexOf(' '); 
121.         int secondSpace = request.indexOf(' ', firstSpace + 1); 
122.         String theFile = request.substring(firstSpace + 1, secondSpace); 
123.         if (request.indexOf("HTTP") != -1) { 
124.           // 这是一个HTTP响应码,告知客户端要被重定向 
125.           out.write("HTTP/1.0 302 FOUND\r\n"); 
126.           // 服务器当前时间 
127.           out.write("Date: " + new Date() + "\r\n"); 
128.           // 服务器的名称和版本【可选的】 
129.           out.write("Server: Redirector 1.0\r\n"); 
130.           // 告知要重定向的位置,浏览器会自动跳转 
131.           out.write("Location: " + siteUrl + theFile + "\r\n"); 
132.           // 指示客户端看到的是HTML,发送一个空行表明结束 
133.           out.write("Content-type: text/html\r\n\r\n"); 
134.           out.flush(); 
135.         } 
136.         // 有些老浏览器,不支持redirection,我们需要生成HTML说明 
137.         out.write("<HTML><HEAD><TITLE>Document moved</TITLE></HEAD>\r\n"); 
138.         out.write("<BODY><H1>Document moved</H1>\r\n"); 
139.         out.write("The document " + theFile + " has moved to\r\n<A HREF=\""  
140.             + siteUrl + theFile + "\">" + siteUrl + theFile  
141.             + "</A>.\r\n Please update your bookmarks<P>"); 
142.         out.write("</BODY></HTML>\r\n"); 
143.         out.flush(); 
144.       } catch (IOException e) { 
145.         throw new RuntimeException(e); 
146.       } finally { 
147.         if (conn != null) 
148.           try { 
149.             conn.close(); 
150.           } catch (IOException e) { 
151.             throw new RuntimeException(e); 
152.           } 
153.       } 
154.     } 
155.   } 
156.  
157.   public static void main(String[] args) { 
158.     int port = 80; 
159.     String siteUrl = "http://www.sina.com.cn"; 
160.     Thread server = new Thread(new Redirector(port, siteUrl)); 
161.     server.start(); 
162.   } 
163. } 

/**
* 单个文件的HTTP Server,输入本机地址,返回d:/index.html文件
*/
public class SingleFileHttpServer extends Thread {

  private byte[] content;
  private byte[] header;
  private int port;

  public SingleFileHttpServer(byte[] data, String encoding, String MIMEType) throws UnsupportedEncodingException {
    this.content = data;
    this.port = 80;
    String header = "HTTP/1.0 200 OK\r\n" + "Server: OneFile 1.0 \r\n"
        + "Content-length: " + this.content.length
        + "\r\n" + "Content-type: " + MIMEType + "\r\n";
    this.header = header.getBytes(encoding);
  }

  public void run() {
    ServerSocket server;
    try {
      server = new ServerSocket(port);
      System.out.println("Accepting connections on port " + server.getLocalPort());
      System.out.println("Data to be sent:");
      System.out.write(this.content);
      while (true) {
        Socket conn = null;
        try {
          conn = server.accept();
          OutputStream out = conn.getOutputStream();
          InputStream in = conn.getInputStream();
          int temp;
          StringBuilder request = new StringBuilder();
          if ((temp = in.read()) != -1)
            request.append((char) temp);
          if (request.toString().indexOf("HTTP/") != -1)
            out.write(header);
          out.write(content);
          out.flush();
        } catch (IOException e) {
          throw new RuntimeException(e);
        } finally {
          conn.close();
        }
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public static void main(String[] args) {
    try {
      String encoding = "ASCII";
      String fileName = "D:/index.html";
      String contentType = "text/plain";
      if (fileName.endsWith("html") || fileName.endsWith("htm"))
        contentType = "text/html";
      InputStream in = new FileInputStream(new File(fileName));
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      int temp;
      while ((temp = in.read()) != -1)
        baos.write(temp);
      byte[] data = baos.toByteArray();
      SingleFileHttpServer server =
          new SingleFileHttpServer(data, encoding, contentType);
      server.start();
    } catch (FileNotFoundException e) {
    } catch (IOException e) {
    }
  }
}


/**
* 跳转地址,输入本机地址,自动跳转到sina
*/
public class Redirector implements Runnable {

  private int port;
  private String siteUrl;

  public Redirector(int port, String siteUrl) {
    this.port = port;
    this.siteUrl = siteUrl;
  }

  public void run() {
    try {
      ServerSocket server = new ServerSocket(port);
      while (true) {
        Socket conn = server.accept();
        Thread t = new RedirectThread(conn);
        t.start();
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
  private class RedirectThread extends Thread {

    private Socket conn;

    public RedirectThread(Socket conn) {
      this.conn = conn;
    }

    public void run() {
      try {
        Writer out = new OutputStreamWriter(conn.getOutputStream());
        Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        StringBuilder sb = new StringBuilder();
        int temp;
        while (true) {
          temp = in.read();
          if (temp == '\r' || temp == '\n' || temp == -1)
            break;
          sb.append((char) temp);
        }
        String request = sb.toString();
        int firstSpace = request.indexOf(' ');
        int secondSpace = request.indexOf(' ', firstSpace + 1);
        String theFile = request.substring(firstSpace + 1, secondSpace);
        if (request.indexOf("HTTP") != -1) {
          // 这是一个HTTP响应码,告知客户端要被重定向
          out.write("HTTP/1.0 302 FOUND\r\n");
          // 服务器当前时间
          out.write("Date: " + new Date() + "\r\n");
          // 服务器的名称和版本【可选的】
          out.write("Server: Redirector 1.0\r\n");
          // 告知要重定向的位置,浏览器会自动跳转
          out.write("Location: " + siteUrl + theFile + "\r\n");
          // 指示客户端看到的是HTML,发送一个空行表明结束
          out.write("Content-type: text/html\r\n\r\n");
          out.flush();
        }
        // 有些老浏览器,不支持redirection,我们需要生成HTML说明
        out.write("<HTML><HEAD><TITLE>Document moved</TITLE></HEAD>\r\n");
        out.write("<BODY><H1>Document moved</H1>\r\n");
        out.write("The document " + theFile + " has moved to\r\n<A HREF=\""
            + siteUrl + theFile + "\">" + siteUrl + theFile
            + "</A>.\r\n Please update your bookmarks<P>");
        out.write("</BODY></HTML>\r\n");
        out.flush();
      } catch (IOException e) {
        throw new RuntimeException(e);
      } finally {
        if (conn != null)
          try {
            conn.close();
          } catch (IOException e) {
            throw new RuntimeException(e);
          }
      }
    }
  }

  public static void main(String[] args) {
    int port = 80;
    String siteUrl = "http://www.sina.com.cn";
    Thread server = new Thread(new Redirector(port, siteUrl));
    server.start();
  }
}



核心开始
Java代码

   1. /**
   2.  * Java版 HTTP服务器
   3.  */ 
   4. public class JHTTPServer extends Thread { 
   5.  
   6.   private File docRootDir; 
   7.   private String indexFileName; 
   8.   private ServerSocket server; 
   9.   private int numThread = 50; 
  10.  
  11.   public JHTTPServer(File docRootDir) throws IOException { 
  12.     this(docRootDir, 80, "index.html"); 
  13.   } 
  14.  
  15.   public JHTTPServer(File docRootDir, int port, String indexFileName)  
  16.       throws IOException { 
  17.     this.docRootDir = docRootDir; 
  18.     this.indexFileName = indexFileName; 
  19.     server = new ServerSocket(port); 
  20.   } 
  21.  
  22.   public void run() { 
  23.     for (int i = 0; i < numThread; i++) { 
  24.       Thread t = new Thread(new RequestProcessor(docRootDir, indexFileName)); 
  25.       t.start(); 
  26.     } 
  27.     System.out.println("Accepting connections on port " + server.getLocalPort()); 
  28.     System.out.println("Document Root: " + docRootDir); 
  29.     while(true){ 
  30.       try { 
  31.         Socket conn = server.accept(); 
  32.         RequestProcessor.processRequest(conn); 
  33.       } catch (IOException e) { 
  34.         throw new RuntimeException(e); 
  35.       } 
  36.     } 
  37.   } 
  38.  
  39.   public static void main(String[] args) { 
  40.     File docRoot = new File("D:/src/HTML_CSS"); 
  41.     try { 
  42.       new JHTTPServer(docRoot).start(); 
  43.     } catch (IOException e) { 
  44.       System.out.println("Server could not start because of an " + e.getClass()); 
  45.       System.out.println(e); 
  46.     } 
  47.   } 
  48. } 
  49.  
  50. /**
  51.  * 服务线程池
  52.  */ 
  53. public class RequestProcessor implements Runnable { 
  54.  
  55.   private static List<Socket> pool = new LinkedList<Socket>(); 
  56.   private File docRootDir; 
  57.   private String indexFileName; 
  58.  
  59.   public RequestProcessor(File docRootDir, String indexFileName) { 
  60.     if (docRootDir.isFile()) 
  61.       throw new IllegalArgumentException( 
  62.           "documentRootDirectory must be a directory, not a file"); 
  63.     this.docRootDir = docRootDir; 
  64.     try { 
  65.       this.docRootDir = docRootDir.getCanonicalFile(); 
  66.     } catch (IOException ex) { 
  67.     } 
  68.     this.indexFileName = indexFileName; 
  69.   } 
  70.    
  71.   public static void processRequest(Socket conn) { 
  72.     synchronized (pool) { 
  73.       pool.add(pool.size(), conn); 
  74.       pool.notifyAll(); 
  75.     } 
  76.   } 
  77.    
  78.   public void run() { 
  79.     String root = docRootDir.getPath(); 
  80.     while (true) { 
  81.       Socket conn; 
  82.       synchronized (pool) { 
  83.         while (pool.isEmpty()) { 
  84.           try { 
  85.             pool.wait(); 
  86.           } catch (InterruptedException e) { 
  87.             throw new RuntimeException(e); 
  88.           } 
  89.         } 
  90.         conn = pool.remove(0); 
  91.       } 
  92.       try { 
  93.         OutputStream raw = new BufferedOutputStream(conn.getOutputStream()); 
  94.         Writer out = new OutputStreamWriter(raw); 
  95.         Reader in = new InputStreamReader(new BufferedInputStream(conn.getInputStream())); 
  96.         StringBuilder request = new StringBuilder(); 
  97.         int c; 
  98.         while (true) { 
  99.           c = in.read(); 
100.           if (c == '\r' || c == '\n') 
101.             break; 
102.           request.append((char) c); 
103.         } 
104.         String get = request.toString(); 
105.         // 记录请求日志 
106.         System.out.println(get); 
107.         StringTokenizer st = new StringTokenizer(get); 
108.         String method = st.nextToken(); 
109.         String version = "", fileName, contentType; 
110.         if (method.equals("GET")) { 
111.           fileName = st.nextToken(); 
112.           if (fileName.endsWith("/")) 
113.             fileName += indexFileName; 
114.           contentType = guessContentTypeFromName(fileName); 
115.           if (st.hasMoreTokens()) 
116.             version = st.nextToken(); 
117.           File theFile = new File(docRootDir, fileName.substring(1, fileName.length())); 
118.           // 不让请求超出文档根目录 
119.           if (theFile.canRead() && theFile.getCanonicalPath().startsWith(root)) { 
120.             DataInputStream dis = new DataInputStream( 
121.                 new BufferedInputStream(new FileInputStream(theFile))); 
122.             byte[] theData = new byte[(int) theFile.length()]; 
123.             dis.readFully(theData); 
124.             dis.close(); 
125.             if (version.startsWith("HTTP ")) { 
126.               out.write("HTTP/1.0 200 OK\r\n"); 
127.               out.write("Date" + new Date() + "\r\n"); 
128.               out.write("Server: JHTTP/1.0\r\n"); 
129.               out.write("Content-length: " + theData.length + "\r\n"); 
130.               out.write("Content-type: " + contentType + "\r\n\r\n"); 
131.               out.flush(); 
132.             } 
133.             // 发送文件,可能是图片或其它二进制数据,所以使用底层的输出流不是书写器 
134.             raw.write(theData); 
135.             raw.flush(); 
136.           } else { // 没有找到文件 
137.             if (version.startsWith("HTTP ")) { // send a MIME header 
138.               out.write("HTTP/1.0 404 File Not Found\r\n"); 
139.               Date now = new Date(); 
140.               out.write("Date: " + now + "\r\n"); 
141.               out.write("Server: JHTTP/1.0\r\n"); 
142.               out.write("Content-type: text/html\r\n\r\n"); 
143.             } 
144.             out.write("<HTML>\r\n"); 
145.             out.write("<HEAD><TITLE>File Not Found</TITLE>\r\n"); 
146.             out.write("</HEAD>\r\n"); 
147.             out.write("<BODY>"); 
148.             out.write("<H1>HTTP Error 404: File Not Found</H1>\r\n"); 
149.             out.write("</BODY></HTML>\r\n"); 
150.             out.flush(); 
151.           } 
152.         } else { // method does not equal "GET" 
153.           if (version.startsWith("HTTP ")) { // send a MIME header 
154.             out.write("HTTP/1.0 501 Not Implemented\r\n"); 
155.             Date now = new Date(); 
156.             out.write("Date: " + now + "\r\n"); 
157.             out.write("Server: JHTTP 1.0\r\n"); 
158.             out.write("Content-type: text/html\r\n\r\n"); 
159.           } 
160.           out.write("<HTML>\r\n"); 
161.           out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n"); 
162.           out.write("</HEAD>\r\n"); 
163.           out.write("<BODY>"); 
164.           out.write("<H1>HTTP Error 501: Not Implemented</H1>\r\n"); 
165.           out.write("</BODY></HTML>\r\n"); 
166.           out.flush(); 
167.         } 
168.       } catch (IOException e) { 
169.         throw new RuntimeException(e); 
170.       } finally { 
171.         try { 
172.           conn.close(); 
173.         } catch (IOException ex) {} 
174.       } 
175.     } 
176.   } 
177.  
178.   private String guessContentTypeFromName(String name) { 
179.     if (name.endsWith(".html") || name.endsWith(".htm")) { 
180.       return "text/html"; 
181.     } else if (name.endsWith(".txt") || name.endsWith(".java")) { 
182.       return "text/plain"; 
183.     } else if (name.endsWith(".gif")) { 
184.       return "image/gif"; 
185.     } else if (name.endsWith(".class")) { 
186.       return "application/octet-stream"; 
187.     } else if (name.endsWith(".jpg") || name.endsWith(".jpeg")) { 
188.       return "image/jpeg"; 
189.     } else 
190.       return "text/plain"; 
191.   } 
192. } 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics