简单 Web Server 程序

简单 Web Server 程序的设计与实现

Web 服务是 Internet 最方便与受用户欢迎的服务类型,它的影响力也远远超出了专业技术范畴,已广泛应用于电子商务、远程教育、远程医疗与信息服务等领域,并且有继续扩大的趋势。目前很多的 Internet 应用都是基于 Web 技术的,因此掌握 Web 环境的软件编程技术对软件人员是至关重要的。

编写简单的 Web Server 有助于读者了解 Web Server 的工作流程,掌握超文本传送协议( HTTP)基本原理,掌握 Windows 环境中用 socket 实现 C/S 结构程序的编程方法。

要求

实现一个简单的 Web Server,能够响应客户端的请求将指定目录下的 HTML 或 text 件通过指定的

TCP 端发送给客户端。具体编程要求是:

(1)服务器启动时可指定服务端口,默认为 8000。

(2)可指定 Web Server 的根目录。

(3)服务器应能够并发处理多个请求。要求至少能支持 Get 命令。鼓励增强 Web Server 的功能,如支持 Head、Post 以及 Delete 等命令。

(4)统计 Web Server 接收和发送的流量。

(5)在 Windows 平台实现,要求使用图形界面显示服务器的各种信息。

(6)不允许使用已有的 HTTP 库。

(7)编写必要的客户端测试程序用于发送 HTTP 请求并显示返回结果,也可使用一般的 Web 浏览器测试

执行操作:

  • 将编写好的HTML文件放在指定文件下,并修改ROOT中的路径,修改成你放置HTML文件的地方。
  • 运行程序,在浏览器打开localhost:8000/index.html

代码如下

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class webserver {
    public static String ROOT = "D:/c";

    public static void main(String[] args) throws IOException {
        //创建服务器端socket端口为8000
        ServerSocket serverSocket = new ServerSocket(8000);

        //循环地接受客户端的请求
        while (true) {
            //服务器端serverSocket接受客户端的请求,并创建客户端socket对象
            Socket socket = serverSocket.accept();

            //为在socket上获得的输入流创建一个使用默认字符集的 InputStreamReader
            InputStreamReader isr = new InputStreamReader(
                    socket.getInputStream());

            //创建一个缓冲器来包装字符流isr
            BufferedReader br = new BufferedReader(isr);


            //读取HTTP请求报文的起始行,并根据空格分割开,存入数组,因此变量temp[1]就是请求的url
            String[] temp = br.readLine().split(" ");


            //创建socket的输出流,即需要向客户端传输的东西
            OutputStream outputstream = socket.getOutputStream();

            //在控制台输出请求报文第一行来判断程序运行情况

            for (String s : temp) {
                System.out.print(s);
                System.out.print(" ");

            }
            System.out.println("");



            //调用数据处理函数
            data(outputstream, temp[1],socket);
        }

    }

    private static Object data(OutputStream outputstream, String temp,Socket s)
            throws IOException,NullPointerException {
        /* 简要介绍传入函数的三个参数
         * outputstream是输出流,也就是要向客户端输出的东西
         * temp是HTTP的请求报文所请求的url,关于这一点请参照HTTP报文结构进行理解
         * s是客户端的socket
         *
         */
        //以下操作是生成HTTP响应报文并存入变量message

        //HTTP响应报文的起始行
        String message = "HTTP/1.1 200 OK\r\n";


        if (temp.contains("html")) {
            /* 如果url中包含"html",
             * 说明客户端所请求的是个html文件,所以在响应头中要设置网页内容类型为text/html,
             * 由于报文首部与报文实体之间要有一行空格,所以下面在设置网页内容后加了两次回车换行符
             * 像Content-Type这样的东西属于报文首部,而图片这种文件属于报文实体,注意区分呀。
             */
            message += "Content-Type:text/html\r\n\r\n";

            //用本地的HTML文件生成文件输入流,并包装为缓存器
            FileInputStream fis = new FileInputStream(ROOT + temp);
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));

            //将指向HTML的缓存器逐行加入到message中,即添加进HTTP响应报文
            String temps = "";
            while ((temps = br.readLine()) != null)
                message += temps;

            //对message调用getByte()方法,将String类型的message转换为字节类型并写入到输出流
            outputstream.write(message.getBytes());

            //刷新输出流,即将此输出流返回到客户端(浏览器)进行显示,然后关闭输出流。
            outputstream.flush();
            outputstream.close();
            br.close();
        } else if (temp.contains("png")) {
            /*
             * 由于浏览器会自动根据html代码内的图片src地址来生成请求并发送到服务器以获取图片,
             * 所以浏览器的第二次请求的url中会有图片的后缀名png,因此以下代码是用来向浏览器传回图片的
             */

            //依旧是设置Content-Type响应首部,只不过这次是返回一个图片

            message += "Content-Type:image/png\r\n\r\n";
            outputstream.write(message.getBytes());


            //将图片文件逐层进行包装,最终包装为DataInputStream类型
            DataInputStream dis = new DataInputStream(new BufferedInputStream(
                    new FileInputStream(ROOT + temp)));

            /*
             * 接下来的几行代码是实现文件数据I/O的常用固定模式,buf是个字节类型的数组,用作缓存;
             * read是个标志位,当对dis(DataIuputStream类型的变量)调用read(buf)函数时,
             * 意思是将dis的数据写入buf,当文件读取完毕时,也就是最后一次调用read()时,写入buf的
             * 数据为空,因此会返回-1给标志位变量read。当变量read的值不是-1时,说明dis还没读取完,
             * 所以在循环中继续读取,并将buf写入到输出流outputstream中。read()和write()方法的详情请自行
             * 查阅资料。
             */
            byte[] buf = new byte[1024];
            int read = 0;
            while ((read = dis.read(buf)) != -1) {
                outputstream.write(buf, 0, read);
            }
            outputstream.flush();
            s.close();
            dis.close();
        }
        return null;
    }

}

运行结果如图:

image-20220103094759226

image-20220103094927569

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值