`

Java Socket 编写的 HTTP 服务器应用【转】

阅读更多

转自http://hi.baidu.com/zpls2003/blog/item/0ae5b61996b6334342a9ad4d.html

/**
* SimpleHttpServer.java
*/

import java.io.*;
import java.net.*;
import java.util.StringTokenizer;

/**
* 一个简单的用 Java Socket 编写的 HTTP 服务器应用, 演示了请求和应答的协议通信内容以及
* 给客户端返回 HTML 文本和二进制数据文件(一个图片), 同时展示了 404, 200 等状态码.
* 首先运行这个程序,然后打开Web浏览器,键入http://localhost ,则这个程序能够显示出浏览器发送了那些信息
* 并且向浏览器返回一个网页和一副图片, 并测试同浏览器对话.
* 当浏览器看到 HTML 中带有图片地址时, 则会发出第二次连接来请求图片等资源.
* 这个例子可以帮您理解 Java 的 HTTP 服务器软件是基于 J2SE 的 Socket 等软件编写的概念, 并熟悉
* HTTP 协议.
* 相反的用 Telnet 连接到已有的服务器则可以帮忙理解浏览器的运行过程和服务器端的返回内容.
*
* <pre>
*       当用户在Web浏览器地址栏中输入一个带有http://前缀的URL并按下Enter后,或者在Web页面中某个以http://开头的超链接上单击鼠标,HTTP事务处理的第一个阶段--建立连接阶段就开始了.HTTP的默认端口是80.
*    随着连接的建立,HTTP就进入了客户向服务器发送请求的阶段.客户向服务器发送的请求是一个有特定格式的ASCII消息,其语法规则为:
* < Method > < URL > < HTTP Version > <\n>
* { <Header>:<Value> <\n>}*
* <\n>
* { Entity Body }
*    请求消息的顶端是请求行,用于指定方法,URL和HTTP协议的版本,请求行的最后是回车换行.方法有GET,POST,HEAD,PUT,DELETE等.
* 在请求行之后是若干个报头(Header)行.每个报头行都是由一个报头和一个取值构成的二元对,报头和取值之间以":"分隔;报头行的最后是回车换行. 常见的报头有Accept(指定MIME媒体类型),Accept_Charset(响应消息的编码方式),Accept_Encoding(响应消息的 字符集),User_Agent(用户的浏览器信息)等.
*    在请求消息的报头行之后是一个回车换行,表明请求消息的报头部分结束.在这个\n之后是请求消息的消息实体(Entity Body).具体的例子参看httpRequest.txt.
*     Web服务器在收到客户请求并作出处理之后,要向客户发送应答消息.与请求消息一样,应答消息的语法规则为:
* < HTTP Version> <Status Code> [<Message>]<\n>
* { <Header>:<Value> <\n> } *
* <\n>
* { Entity Body }
*    应答消息的第一行为状态行,其中包括了HTTP版本号,状态码和对状态码进行简短解释的消息;状态行的最后是回车换行.状态码由3位数字组成,有5类:
* 参看:HTTP应答码及其意义
*
* 1XX 保留
* 2XX 表示成功
* 3XX 表示URL已经被移走
* 4XX 表示客户错误
* 5XX 表示服务器错误
* 例如:415,表示不支持改媒体类型;503,表示服务器不能访问.最常见的是200,表示成功.常见的报头有:Last_Modified(最后修改时间),Content_Type(消息内容的MIME类型),Content_Length(内容长度)等.
*    在报头行之后也是一个回车换行,用以表示应答消息的报头部分的结束,以及应答消息实体的开始.
*    下面是一个应答消息的例子:
* HTTP/1.0 200 OK
* Date: Moday,07-Apr-97 21:13:02 GMT
* Server:NCSA/1.1
* MIME_Version:1.0
* Content_Type:text/html
* Last_Modified:Thu Dec 5 09:28:01 1996
* Coentent_Length:3107
*
* <HTML><HEAD><TITLE></HTML>
*
* 在用Java语言实现HTTP服务器时,首先启动一个java.net.ServerSocket在提?服务的端?上监听连接.向客户返回文本时,可以用 PrintWriter,但是如果返回二进制数据,则必须使用OutputStream.write(byte[])方法,返回的应答消息字符串可以使用 String.getBytes()方法转换为字节数组返回,或者使用PrintStream的print()方法写入文本,用 write(byte[])方法写入二进制数据.
*
* </pre>
* @author 刘长炯
* @version 1.0 2007-07-24 Sunday
* @version 1.1 2008-05-18 Sunday
* 支持浏览器发出的POST信息打印,修正POST方式无法完成响应造成阻赛的BUG
*/
public class SimpleHttpServer implements Runnable {
    /**
     *
     */
    ServerSocket serverSocket;//服务器Socket

    /**
     * 服务器监听端口, 默认为 80.
     */
    public static int PORT=80;//标准HTTP端口

    /**
     * 开始服务器 Socket 线程.
     */
    public SimpleHttpServer() {
        try {
            serverSocket=new ServerSocket(PORT);
        } catch(Exception e) {
            System.out.println("无法启动HTTP服务器:"+e.getLocalizedMessage());
        }
        if(serverSocket==null) System.exit(1);//无法开始服务器
        new Thread(this).start();
        System.out.println("HTTP服务器正在运行,端口:"+PORT);
    }

    /**
     * 运行服务器主线程, 监听客户端请求并返回响应.
     */
    public void run() {
        while(true) {
            try {
                Socket client=null;//客户Socket
                int contentLength = 0;// 客户端发送的 HTTP 请求的主体的长度
                client=serverSocket.accept();//客户机(这里是 IE 等浏览器)已经连接到当前服务器
                if(client!=null) {
                    System.out.println("连接到服务器的用户:"+client);
                    try {
                        // 第一阶段: 打开输入流
                        BufferedReader in=new BufferedReader(new InputStreamReader(
                                client.getInputStream()));

                        System.out.println("客户端发送的请求信息:\n===================");
                        // 读取第一行, 请求地址
                        String line=in.readLine();
                        System.out.println(line);
                        String resource=line.substring(line.indexOf('/'),line.lastIndexOf('/')-5);
                        //获得请求的资源的地址
                        resource=URLDecoder.decode(resource, "UTF-8");//反编码 URL 地址
                        String method = new StringTokenizer(line).nextElement().toString();// 获取请求方法, GET 或者 POST

                        // 读取所有浏览器发送过来的请求参数头部信息
                        while( (line = in.readLine()) != null) {
                            System.out.println(line);

                            // 读取 POST 等数据的内容长度
                            if(line.startsWith("Content-Length")) {
                                try {
                                    contentLength = Integer.parseInt(line.substring(line.indexOf(':') + 1).trim());
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }

                            if(line.equals("")) break;
                        }

                        // 显示 POST 表单提交的内容, 这个内容位于请求的主体部分
                        if("POST".equalsIgnoreCase(method) && contentLength > 0) {
                            System.out.println("以下内容为 POST 方式提交的表单数据");
                            for(int i = 0; i < contentLength; i++) {
                                System.out.print((char)in.read());
                            }

                            System.out.println();
                        }

                        System.out.println("请求信息结束\n===================");
                        System.out.println("用户请求的资源是:"+resource);
                        System.out.println("请求的类型是: " + method);

                        // GIF 图片就读取一个真实的图片数据并返回给客户端
                        if(resource.endsWith(".gif")) {
                            fileService("images/test.gif", client);
                            closeSocket(client);
                            continue;
                        }

                        // 请求 JPG 格式就报错 404
                        if(resource.endsWith(".jpg")) {
                                                    PrintWriter out=new PrintWriter(client.getOutputStream(),true);
                        out.println("HTTP/1.0 404 Not found");//返回应答消息,并结束应答
                        out.println();// 根据 HTTP 协议, 空行将结束头信息
                        out.close();
                        closeSocket(client);
                        continue;
                        } else {
                            // 用 writer 对客户端 socket 输出一段 HTML 代码
                            PrintWriter out=new PrintWriter(client.getOutputStream(),true);
                            out.println("HTTP/1.0 200 OK");//返回应答消息,并结束应答
                            out.println("Content-Type:text/html;charset=GBK");
                            out.println();// 根据 HTTP 协议, 空行将结束头信息

                            out.println("<h1> Hello Http Server</h1>");
                            out.println("你好, 这是一个 Java HTTP 服务器 demo 应用.<br>");
                            out.println("您请求的路径是: " + resource + "<br>");
                            out.println("这是一个支持虚拟路径的图片:<img src='abc.gif'><br>" +
                                    "<a href='abc.gif'>点击打开abc.gif, 是个服务器虚拟路径的图片文件.</a>");
                            out.println("<br>这是个会反馈 404 错误的的图片:<img src='test.jpg'><br><a href='test.jpg'>点击打开test.jpg</a><br>");
                            out.println("<form method=post action='/'>POST 表单 <input name=username value='用户'> <input name=submit type=submit value=submit></form>");
                            out.close();

                            closeSocket(client);
                        }
                    } catch(Exception e) {
                        System.out.println("HTTP服务器错误:"+e.getLocalizedMessage());
                    }
                }
                //System.out.println(client+"连接到HTTP服务器");//如果加入这一句,服务器响应速度会很慢
            } catch(Exception e) {
                System.out.println("HTTP服务器错误:"+e.getLocalizedMessage());
            }
        }
    }

    /**
     * 关闭客户端 socket 并打印一条调试信息.
     * @param socket 客户端 socket.
     */
    void closeSocket(Socket socket) {
        try {
            socket.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
                            System.out.println(socket + "离开了HTTP服务器");
    }

    /**
     * 读取一个文件的内容并返回给浏览器端.
     * @param fileName 文件名
     * @param socket 客户端 socket.
     */
        void fileService(String fileName, Socket socket)
    {

        try
        {
            PrintStream out = new PrintStream(socket.getOutputStream(), true);
            File fileToSend = new File(fileName);
            if(fileToSend.exists() && !fileToSend.isDirectory())
            {
                out.println("HTTP/1.0 200 OK");//返回应答消息,并结束应答
                out.println("Content-Type:application/binary");
                out.println("Content-Length:" + fileToSend.length());// 返回内容字节数
                out.println();// 根据 HTTP 协议, 空行将结束头信息

                FileInputStream fis = new FileInputStream(fileToSend);
                byte data[] = new byte[fis.available()];
                fis.read(data);
                out.write(data);
                out.close();
                fis.close();
            }
        }
        catch(Exception e)
        {
            System.out.println("传送文件时出错:" + e.getLocalizedMessage());
        }
    }

    /**
     * 打印用途说明.
     */
    private static void usage() {
        System.out.println("Usage: java SimpleHttpServer <port>\nDefault port is 80.");
    }


    /**
     * 启动简易 HTTP 服务器
     * @param args
     */
    public static void main(String[] args) {
        try {
            if(args.length != 1) {
                usage();
            } else if(args.length == 1) {
                PORT = Integer.parseInt(args[0]);
            }
        } catch (Exception ex) {
            System.err.println("Invalid port arguments. It must be a integer that greater than 0");
        }

        new SimpleHttpServer();
    }

}

分享到:
评论
1 楼 贾懂凯 2011-05-28  
接下来可以尝试加入servlet,以及过滤其等模块

相关推荐

    JAVA使用Socket编写HTTP服务器

    以上就是使用Java Socket编写HTTP服务器的基本流程。实际的项目中,可能还需要考虑更多复杂情况,如多线程处理请求、支持HTTPS、缓存机制、错误处理、日志记录等。通过阅读和理解这些代码,可以深入学习网络编程和...

    java socket服务器客户端程序

    Java Socket服务器客户端程序是网络编程中的基础组件,用于实现两台计算机...理解并掌握这些知识点,对于编写Java Socket服务器客户端程序至关重要。通过实际的项目练习,你可以更好地了解如何在Java中实现网络通信。

    java socket编写的上网代理

    Java Socket 编写的上网代理是一种基于TCP/IP协议的网络通信方式,它允许应用程序通过Socket连接到其他网络服务,实现数据的传输。在这个特定的项目中,开发者使用Java语言实现了代理服务器的功能,允许用户通过该...

    Java_Socket开发高并发小型服务器

    Java的一个关键特性是“一次编写,到处运行”,这意味着Java程序可以在任何支持Java的平台上运行,这为开发网络应用提供了极大的便利,尤其是对于需要跨平台的服务器应用程序。 在实现高并发小型服务器时,开发者...

    JAVA Socket 经典教程

    通过学习Java Socket编程,开发者可以构建功能丰富的网络应用程序,如FTP服务器、邮件系统、实时通信软件等。实践是检验理论的最好方式,动手编写Socket程序,不断尝试和调试,将有助于深化理解和提升技能。

    java编写的SOCKET客户端

    这个软件可以任意设置目标IP和端口实现连接相应的主机服务器,编写语言为java,安装时候需要有相应的java环境运行。

    JAVA编写客户端向服务器传输文件

    总结起来,使用Java编写客户端向服务器传输文件涉及到的主要知识点有:Java的Socket和ServerSocket类,网络通信的基本原理,文件的输入/输出流操作,以及可能涉及的多线程、异常处理和安全性策略。理解这些概念并能...

    JAVA基于Socket编写的Ftp程序

    本项目"JAVA基于Socket编写的FTP程序"是一个利用Java语言实现的简单FTP(File Transfer Protocol)客户端和服务端应用。FTP是一种用于在互联网上可靠地传输文件的标准协议,它允许用户从远程主机下载文件或上传文件...

    基于socket编写的java 多用户五子棋小游戏 | java 源码

    【标题】: "基于Socket编写的Java多用户五子棋小游戏" 在计算机编程领域,Java是一种广泛应用的编程语言,尤其在开发网络应用方面表现出色。本项目是一个利用Java Socket技术实现的多用户在线五子棋游戏。Socket是...

    java编写的浏览器和服务器

    本项目涉及的是使用Java编写的一个简单的浏览器和服务器,这对于学习Java网络编程的初学者来说是一个很好的实践案例。 首先,我们要理解浏览器和服务器的基本工作原理。浏览器是客户端应用程序,用于向服务器发送...

    JAVA Socket远程执行任务

    Java Socket远程执行任务是一种通过网络通信在客户端与服务器之间实现命令传递和执行的技术。在这个例子中,客户端将命令和一个名为"FindWordCount.jar"的Java可执行文件发送到服务器,然后服务器执行这个jar包并...

    java socket使用加密协议传输对象

    在Java开发中,Socket编程是一种常见的网络通信方式,它允许不同计算机上的应用程序通过网络进行交互。然而,对于涉及敏感信息的应用场景,如金融交易、个人隐私数据处理等,仅仅依靠Socket的基础功能是远远不够的,...

    java socket聊天程序,有界面

    Java Socket聊天程序是一种基于网络通信的软件应用,它利用了Java的Socket API来实现客户端与服务器之间的实时交互。在这个程序中,"有界面"意味着它不仅包含后台的网络通信逻辑,还具有用户友好的图形用户界面(GUI...

    Java Socket 实现SMTP邮件发送,支持SSL/TSL

    1. **Java Socket类**:Java的`java.net.Socket`类是网络通信的基础,它代表了客户端与服务器之间的连接。创建Socket实例时,需要指定服务器的IP地址和端口号,然后就可以通过Socket的输入/输出流进行数据交换。 2....

    Java Socket 实用教程

    在Java中,`java.net.Socket`类代表客户端Socket,`java.net.ServerSocket`类则用于服务器端,监听客户端连接。 2. **创建Socket**:客户端通过`new Socket("服务器地址", 端口号)`创建Socket实例,与服务器建立...

    JAVA SOCKET通讯程序

    ### JAVA SOCKET通讯程序知识点 #### 一、Java Socket编程简介 在Java中,Socket编程是一种常用的网络通信方式,它允许程序之间通过TCP/IP协议进行数据交换。Java中的`java.net.Socket`类和`java.net.ServerSocket...

    java socket 长连接实例

    在编写Java Socket程序时,需要关注以下几个关键点: 1. **Socket的创建**:服务器端需要使用`ServerSocket`类创建一个监听特定端口的服务器,客户端则使用`Socket`类与服务器建立连接。 2. **I/O流**:一旦连接...

    基于Android和java的音乐类游戏 ,基于java socket实现的服务器端代码.zip

    3. 网络通信模块:使用Java Socket编写客户端和服务器之间的通信代码,可能使用JSON或自定义格式传输数据。 4. 服务器端逻辑:接收和处理客户端请求,更新和返回游戏状态。 5. 数据存储:可能使用数据库来持久化玩家...

    java socket 经典教程

    通过学习本Java Socket经典教程,开发者将掌握如何使用Java实现高效的网络通信,从而能够在各种分布式系统和互联网应用中构建可靠的客户端-服务器架构。实践是检验理论的最好方式,因此在学习过程中,不断编写和调试...

    用java写的socket服务器测试桩

    Java编写的Socket服务器测试桩是用于模拟实际网络服务或接口的一种工具,它可以帮助开发者在实际服务器未准备就绪或者不便使用时进行应用的功能验证和性能测试。本文将深入讲解Java Socket服务器测试桩的核心概念、...

Global site tag (gtag.js) - Google Analytics