`
bjmike
  • 浏览: 283999 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

基于Java实现简单Http服务器(转)

 
阅读更多
本文将详细介绍如何基于java语言实现一个简单的Http服务器,文中将主要介绍三个方面的内容:1)Http协议的基本知识、2)java.net.Socket类、3)java.net.ServerSocket类,读完本文后你可以把这个服务器用多线程的技术重新编写一个更好的服务器。
          由于Web服务器使用Http协议通信的因此也把它叫做Http服务器,Http使用可靠的TCP连接来工作,它是面向连接的通信方式,这意味着客户端和服务器每次通信都建立自己的连接,它又是无状态的连接,当数据传输完毕后客户端和服务器端的连接立刻关闭,这样可以节省服务器的资源,当然如果需要传输大量的数据,你可以在Request的头设置Connection=keep-alive使得可以复用这一个连接通道。在HTTP协议中非常重要的两个概念就是:请求(Request)和(响应)这也是我在这里要讲述的如果你想了解Http更多的内容那么请参考http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf。

         一个Http请求包括三个重要的部分:
Method-URI-Protocol/Version
Request headers
Entity body
下面是一个Http请求的例子:
POST /servlet/default.jsp HTTP/1.1
Accept: text/plain; text/html
Accept-Language: en-gb
Connection: Keep-Alive
Host: localhost
Referer: http://localhost/ch8/SendDetails.htm
User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)
Content-Length: 33
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate

LastName=Franks&FirstName=Michael
其中第一行是Method-URI-Protocol/Version ,这是非常重要的部分,你需要从中读取客户端数据传输的方式,URI以及协议和版本,在这里分别是POST / servlet/default.jsp http/1.1,我们的简单的服务器的思路就是从request中得到URI后在你的服务器上找到这个资源,比如是一个静态的html页面,然后把它发送给浏览器。记住URI是相对于你的HTTP服务器的根目录的,所以以/来开头。接下来的部分是请求头信息它们都是以name:value这样的方式构成的,这里不再多介绍了。在Header和Entity body之间有一空行叫做CRLF,这用来标记Entity body的开始的,意思是下面的是传输的数据了。

         HTTP响应和请求非常相似,同样包括三个部分:
Protocol-Status code-Description
Response headers
Entity body
下面是一个具体的例子:
HTTP/1.1 200 OK
Server: Microsoft-IIS/4.0
Date: Mon, 3 Jan 1998 13:13:33 GMT
Content-Type: text/html
Last-Modified: Mon, 11 Jan 1998 13:23:42 GMT
Content-Length: 112

something  in html style......................

通常在J2ME联网中我们需要判断响应的状态码来决定下一步的操作,比如200代表连接成功。现在你应该清楚为什么这么做了吧。同样在Header和Entity body中有一个CRLF分割。

        现在我们来看看java中的Socket类,socket其实是对编程语言的一种抽象,它提供了在网络上端对端访问的可能,但是它并不依赖于编程语言,你完全可以使用java和c语言通过socket来进行通信,在java中是通过java.net.Socket来实现的,当你要构建一个socket的时候,你只是需要调用它的构造器
public Socket(String host,int port),其中host代表目标主机的地址或名字,port代表端口,比如80。当我们创建了一个Socket的实例后我们就可以进行通信了,如果你要基于字节来通信,那么你可以通过调用getOutputStream()和getInputStream()来得到OutputStream和InputStream的对象,如果你是基于字符通信的话那么你可以用PrintWriter和BufferedReader进行二次包装,例如PrintWriter pw = new PrintWriter(socket.getOutputStream(),true)。下面是简单的使用socket通信的代码片断,实现了向127.0.0.1:8080发送Http请求的功能

Socket socket = new Socket("127.0.0.1", "8080");
OutputStream os = socket.getOutputStream();
boolean autoflush = true;
PrintWriter out = new PrintWriter( socket.getOutputStream(), autoflush );
BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream() ));
 
// send an HTTP request to the web server
out.println("GET /index.jsp HTTP/1.1");
out.println("Host: localhost:8080");
out.println("Connection: Close");
out.println();
 
// read the response
boolean loop = true;
StringBuffer sb = new StringBuffer(8096);
 
while (loop) {
 if ( in.ready() ) {
 int i=0;
 while (i!=-1) {
        i = in.read();
       sb.append((char) i);
 }
loop = false;
 }
 Thread.currentThread().sleep(50);
}
 
// display the response to the out console
System.out.println(sb.toString());
socket.close();


       接下来介绍与Socket类对应的ServerSocket类的使用,与Socket代表客户端不同的是,ServerSocket是代表服务器端的,因为它必须在某个端口不停的监视是否有客户端连接进来。通过调用ServerSocket的构造器我们可以建立起监听特定端口的Server。例如
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
这样我们在本机的8080端口建立起来了ServerSocket。当你调用ServerSocket的accept()方法后,只有有连接进来的时候,这个方法才会返回一个Socket的对象,这样你就可以使用这个实例来接受或者发送数据了。记住当我们传输数据结束后要记得调用clos()方法来释放资源。

在上篇文章中我们介绍了Http协议的基本知识,以及Java中两个重要的类Socket和SocketServer类,下面我们将主要介绍如何实现一个基于java的Http服务器。
         Http服务器主要由三个类构成:HttpServer、Request和Response。其中程序的入口在HttpServer类,它调用await()方法,使得Server开始等候客户端的连接。当客户端连接后,它将把静态的页面内容发送给客户端浏览器。下面分别介绍这三个类:
1:HttpServer类
     HttpServer需要有一个服务器的根目录这在WEB_ROOT变量中定义的:
public static final String WEB_ROOT =System.getProperty("user.dir") + File.separator  + "webroot";当我们运行服务器的时候可以通过-D选项指定环境变量user.dir的值。这个类中最重要的方法就是await()方法,内容如下:
public void await() {
    ServerSocket serverSocket = null;
    int port = 8080;
    try {
      serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
    }
    catch (IOException e) {
      e.printStackTrace();
      System.exit(1);
    }

    // Loop waiting for a request
    while (!shutdown) {
      Socket socket = null;
      InputStream input = null;
      OutputStream output = null;
      try {
        socket = serverSocket.accept();
        input = socket.getInputStream();
        output = socket.getOutputStream();

        // create Request object and parse
        Request request = new Request(input);
        request.parse();

        // create Response object
        Response response = new Response(output);
        response.setRequest(request);
        response.sendStaticResource();

        // Close the socket
        socket.close();

        //check if the previous URI is a shutdown command
        shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
      }
      catch (Exception e) {
        e.printStackTrace();
        continue;
      }
    }
  }
await()方法内构造一个ServerSocket的实例,等客户端连接进来的时候把socket.getInputStream()传递给Request类进行解析,把socket.getOutputStream()传递给Response类,然后再把request对象传递给Response,最后调用Response.sendStaticResource()方法发送数据给客户端。socket.close()后监测是不是接受到了关闭Server的命令,如果是的话跳出循环结束程序。

2. Request类
Request类的主要目的是对http请求进行封装,它有一个InputStream类型参数的构造器
public Request(InputStream input) {
    this.input = input;
  }
同时它还有一个重要的String类型的成员变量uri,Request的目的就是从inputStream中提取uri,这是由两个函数实现的
public void parse() {
    // Read a set of characters from the socket
    StringBuffer request = new StringBuffer(2048);
    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++) {
      request.append((char) buffer[j]);
    }
    System.out.print(request.toString());
    uri = parseUri(request.toString());
  }

  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;
  }
其中parseUri(String request)方法是私有方法,它把String参数进行解析把两个空格之间的字符串返回,这是遵循Http请求的定义规则的,如果你不清楚可以参考基于java实现http服务器之一。

2.Response类
Response的两个重要的成员变量分别是OutputStream类型的output和Requeset类型的request,这个类的功能就是从Request的实例里面得到uri,然后和WEB_ROOT进行相加得到文件所在的绝对路径,然后读取这个文件的内容把它写入到socket.getOutputStream()里面去。
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 {
        // file not found
        String errorMessage = "HTTP/1.1 404 File Not Found/r/n" +
          "Content-Type: text/html/r/n" +
          "Content-Length: 23/r/n" +
          "/r/n" +
          "<h1>File Not Found</h1>";
        output.write(errorMessage.getBytes());
      }
    }
    catch (Exception e) {
      // thrown if cannot instantiate a File object
      System.out.println(e.toString() );
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }

          下面说明如何运行这个应用程序,首先你从下面的地址取得源代码:http://www.onjava.com/onjava/2003/04/23/examples/HowWebServersWork.zip,把它解到你的D盘例如temp目录下,它包括三个目录src lib webroot其中webroot内有一个index.html文件供测试用。从Command中运行
cd temp
javac -d . src/ex01/pyrmont/*.java
这样在当前目录(d:/temp)会出现ex01/pyrmont目录,里面是编译得到的class文件,接下来运行
java -Duser.dir=d:/temp ex01.pyrmont.HttpServer
然后从浏览器输入http://localhost:8080/index.html,这样你就可以看到Server发送给你的静态页面index.html了。
分享到:
评论

相关推荐

    用java实现的http服务器端例程

    用java实现的http服务器端例程

    java实现的http服务器

    Java实现的HTTP服务器是一个基础的网络编程项目,它利用了Java的Socket编程和HTTP协议的知识。这个简单的HTTP服务器能够处理浏览器发起的GET请求,将指定的静态资源(如HTML、CSS、JavaScript文件)发送回客户端,...

    java实现http服务器实例

    本实例将带你一步步探索如何利用Java实现一个简单的HTTP服务器,并通过源码解析来深化对HTTP协议的理解。 首先,让我们了解HTTP协议的基础。HTTP(超文本传输协议)是互联网上应用最为广泛的一种网络协议,用于从...

    计算机网络实验一 Windows环境下用java实现web服务器

    总的来说,"计算机网络实验一:Windows环境下用Java实现web服务器"是一次综合性的学习体验,涵盖了HTTP协议、网络编程、Java编程以及服务器与前端的交互等多个重要知识点。通过实践,学生将能够更全面地理解Web服务...

    基于Java的邮件服务器的设计与实现

    ### 基于Java的邮件服务器的设计与实现 #### 引言 随着互联网技术的发展,邮件作为信息交流的重要工具,在日常生活中扮演着不可或缺的角色。为了提高邮件系统的性能、可维护性和扩展性,本文提出了一种基于Java...

    基于java socket实现的ftp客户端和服务端交互

    本项目是使用java Socket编程实现的一个简单的FTP服务器和客户端。 客户端目前实现的功能是登录,刷新,上传和下载。同时具有主动模式和被动模式两种模式。 服务器端实现的功能有登录、刷新、上传、下载、列出文件...

    Java实现简单的web文件服务器

    在本文中,我们将深入探讨如何使用Java来实现一个简单的Web文件服务器。这个项目不仅适合Java初学者,也是学习网络编程的绝佳实践。我们将讨论以下几个关键知识点: 1. **TCP与UDP协议**: - TCP(Transmission ...

    http简易服务器 java 源码

    【标题】:“http简易服务器 java 源码”是指基于Java...以上就是基于Java实现的HTTP简易服务器的关键知识点和功能解析。理解这些概念有助于你更好地理解和使用这个源码,甚至可以作为学习网络编程和服务器开发的基础。

    基于java的简单聊天系统源代码~

    【标题】"基于Java的简单聊天系统源代码"是一个适合初学者学习的项目,它展示了如何使用Java语言构建一个基础的聊天应用。这个系统可能包括客户端(Client.java)和服务器端(Server.java)两个主要部分,是理解网络...

    基于Java的ftp简易服务器.zip

    标题"基于Java的ftp简易服务器.zip"暗示了这个项目或教程是关于如何使用Java开发一个简单的FTP服务器。这种服务器可能包含了基本的文件上传、下载、目录浏览等功能,旨在帮助初学者理解FTP协议的工作原理以及如何用...

    Java Socket实现简单的web服务器

    Java Socket实现简单的Web服务器是一个基础但实用的编程实践,它涉及到网络编程、HTTP协议和文件I/O等核心概念。在本项目中,我们利用Java的Socket API构建了一个基本的服务器,该服务器仅处理HTTP GET请求并返回...

    基于Java的HTTP协议简易文件服务器设计源码

    简易文件服务器:基于Java开发,包含53个文件,包括22个Java类文件、17个XML配置文件、3个META文件、2个.gitignore文件、2个Markdown文档、2个Properties配置文件、1个配置文件、1个HTML文件、1个Idea项目文件(.iml...

    基于java的ftp简易服务器.zip

    Java FTP简易服务器是...总的来说,创建一个基于Java的FTP简易服务器需要深入理解FTP协议、网络通信、文件操作、多线程以及安全性等多个方面。通过不断迭代和优化,可以构建出满足特定需求的、高效且安全的FTP服务器。

    网络编程实验:HTTP服务器 java实现

    然而,这个简单的Java实现为理解基本的HTTP服务器工作原理提供了一个良好的起点。 总结来说,本实验涵盖了Java网络编程的基本概念,特别是使用Socket实现客户端与服务器间的通信,以及对HTTP协议的理解和应用。通过...

    基于JAVA开发的邮件服务器

    本文将深入探讨Java实现邮件服务器的关键技术和相关知识点。 首先,JavaMail API是Java平台上的核心库,它允许开发者在Java应用程序中处理邮件收发。JavaMail API提供了丰富的接口和类,用于创建、发送、读取和管理...

    java实现rtsp/rtmp转m3u8/flv/hls/mp4等实现web直播和回放

    综上所述,这个Java实现涉及的技术栈主要包括Java编程、FFmpeg处理、Nginx服务器配置以及Web前端的M3U8播放。通过这样的架构,可以实现从RTSP/RTMP源获取视频流,将其转化为适应Web的M3U8、FLV、HLS或MP4格式,同时...

    Http服务器与客户端实例(Java版)

    使用Java实现一个简单的HTTP服务器,主要依赖于`java.net.ServerSocket`和`java.net.Socket`类。首先,服务器需要监听特定端口上的连接请求,一旦有请求到来,就创建一个新的线程处理这个请求。例如,你可以创建一...

    基于java编程的服务器程序

    【基于Java编程的服务器程序】 Java作为一种广泛使用的编程语言,其在服务器端的应用十分广泛,尤其是在构建高性能、可扩展的网络应用程序中。本篇将深入探讨如何使用Java来编写服务器程序,以及涉及到的相关技术与...

    Java基于socket实现的客户端和服务端通信功能完整实例

    本文将详细介绍Java基于Socket实现的客户端和服务端通信功能,包括客户端和服务器端的实现、连接和通信的过程。 Socket的基本概念 Socket是网络编程的基础,它是操作系统提供的一种机制,允许程序通过网络进行通信...

Global site tag (gtag.js) - Google Analytics