java多线程web服务器

一、原理说明

HTTP协议的作用原理包括四个步骤:

(1)连接:Web浏览器与Web服务器建立连接,打开一个称为socket(套接字)的虚拟文件,此文件的建立标志着连接建立成功。

(2)请求:Web浏览器通过socket向Web服务器提交请求。HTTP的请求一般是GET或POST命令(POST用于FORM参数的传递)。GET命

令的格式为:GET路径/文件名HTTP/1.0文件名指出所访问的文件,HTTP/1.0指出Web浏览器使用的HTTP版本。

(3)应答:Web浏览器提交请求后,通过HTTP协议传送给Web服务器。Web服务器接到后,进行事务处理,处理结果又通过HTTP传回给Web浏览器,从而在Web浏览器上显示出所请求的页面。

二、Java实现Web服务器功能的程序设计

  根据上述HTTP协议的作用原理,实现GET请求的Web服务器程序的方法如下:

(1)创建ServerSocket类对象,监听端口5000。这是为了区别于HTTP的标准TCP/IP端口80而取的;

(2)等待、接受客户机连接到端口5000,得到与客户机连接的socket;

(3)创建与socket字相关联的输入流instream和输出流outstream;

(4)从与socket关联的输入流instream中读取一行客户机提交的请求信息,请求信息的格式为:GET路径/文件名HTTP/1.0

(5)从请求信息中获取请求类型。如果请求类型是GET,则从请求信息中获取所访问的HTML文件名。没有HTML文件名时,则以index.html作为文件名;

(6)如果HTML文件存在,则打开HTML文件,把HTTP头信息和HTML文件内容通过socket传回给Web浏览器,然后关闭文件。否则发送错误信息给Web浏览器;

(7)关闭与相应Web浏浏览器连接的socket字。

三、java代码


主程序类WebServer.java部分-----它是一个启动类,主要用来启动web服务器

package webbook.chapter2;   //程序包声明语句。表示该文件中声明的全部类都属于这个包

import java.io.IOException;           //导入输入输出流的文件包中的子包--异常处理包
import java.net.ServerSocket;         //导入网络包中的子包--服务器端套接字包
import java.net.Socket;              //导入网络包中的子包—客户端套接字包

public class Webserver {           // 定义Webserver类
                              //主程序类中要求只能有一个公共类
                              //JAVA解释器要求公共类必须放在与它同名的文件中
  public static final int HTTP_PORT=5030;    //声明并初始化一个静态最终类整型常量,作为
                                        服务器端的端口
                                       //静态成员变量可直接引用而不用实例化(其
                                         他必须实例化)
                                       //最终类不能被继承,避免被修改
  private ServerSocket serverSocket;     //声明一个私有的ServerSocket类的成员变量

  public void startServer(int port){     //声明构造一个共有的无返回值带参量输入的成员
                                    方法startServer
    try{                                            //捕捉异常方法体
        serverSocket=new ServerSocket(port);          //实例化建立服务器端监听套接字
        System.out.println("Web Server startup on "+port); //标准打印输出一行并换行
        while (true) {                               //循环体 (布尔型常量)
            Socket socket=serverSocket.accept();       //等待客户端连接呼叫,建立连接
                                                   套接字
            new Processor(socket).start();            //实例化并启动线程,直接调用run
          }                                         方法
    } catch (IOException e) {                         //匹配异常
        e.printStackTrace();                     //处理异常—在控制台上显示异常信息
    }
}

    public static void main(String[] argv) throws Exception {  //声明构造主函数体(带用户输
                                            入参数),抛出所有异常,程序执行入口
    Webserver server=new Webserver();             //声明实例化Webserver类对象server
        if (argv.length==1) {     
            server.startServer(Integer.parseInt(argv[0]));//数据类型强制转换为整型(默认
                                                java将键盘输入的数据看作字符串)
        } else {
            server.startServer(Webserver.HTTP_PORT);//启动服务
        }  
     }   
 }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Processor.java部分,是一个线程类,处理客户浏览器的访问请求,并把所请求的资源发送给客户机


package webbook.chapter2;              //程序包声明

import java.io.*;                       //导入输入输出流包,且是导入包中所有类
import java.io.BufferedReader;        //字符缓冲输入流包导入
import java.io.File;                   //文件流包导入
import java.io.IOException;           //输入输出流异常处理包导入
import java.io.InputStream;           //字节输入流包导入
import java.io.PrintStream;           //字节输出打印流包导入,它是
                                           OutputStream的子类
import java.net.Socket;               //网络功能包套接字子包导入


public class Processor extends Thread { //声明线程子类Processor,他的超类是
                                               thread
  private PrintStream out;                  //声明私有成员变量—out(输出流)
  private InputStream input;               //声明私有成员变量—input(输入流)
  public static final String WEB_ROOT="C:\\Users\\Administrator\\workspace\\webbook.chapter2";
                   //声明并初始化静态最终类字符串常量---服务器提供文件存储位置

  public Processor(Socket socket) {          //声明构造成员方法---创建线程
        try {                                  //创建异常处理机制
            input=socket.getInputStream();  //赋值表达式—从套接字获得客户端
                                                 连接输入,返回字节输入流
                                                 inputstream对象并赋值给input
            out=new PrintStream(socket.getOutputStream());//实例化输出流对
                                                               象—输出到客户端
        } catch (IOException e) {           //捕捉匹配异常
            e.printStackTrace();            //显示异常信息
        }
    }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


public void run() {     //创建run方法,它是对象的线程体(这里涉及子类对超类的
                            方法覆盖)-----在线程被创建启动后,直接调用
        try {            //构造监视块,捕捉异常
            String fileName=parse(input); // 调用方法parse解析客户端请求信息
                                                获取请求文件名
            readFile(fileName);           // 调用方法readFile打开,读取文件
        } catch (IOException e) {        //捕捉匹配异常
            e.printStackTrace();         //显示异常信息
        }
    }



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



public String parse(InputStream input) throws IOException {  //声明并   
                     构造方法parse解析客户端输入请求,并抛出可能引发的所有异常
    BufferedReader in=new BufferedReader(new InputStreamReader(input));
                           //声明并实例化字符缓冲输入流对象—用于服务器缓冲输入
        String inputContent=in.readLine();//声明成员变量,用于存储调用in的成
                                               员方法读入的客户端输入数据
        if (inputContent==null||inputContent.length()==0) {
            sendError(400,"Client invoke error");//如果变量值为空或零,输出
                                                         客户端调用错误信息
            return null;                           //并返回空值,关闭连接
        }


        String request[]=inputContent.split(" "); //声明字符串数组,存放成
                            员变量inputstream中内容以空格为界分割后的数据信息
        if(request.length !=3) {                 //判断字符串数组长度是否为3
            sendError(400,"Client invoke error");//若不为3,输出错误信息
            return null;
        }

        String method=request[0];//声明成员变量method并赋值第一个字符数组中
                                        内容
        String fileName=request[1];

        String httpVersion=request[2];

        System.out.println("Method:"+method+",file name:"
        +fileName +", HTTP version:"+httpVersion);  //显示输出信息
        return fileName;
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

public void readFile(String fileName) throws IOException {//构造方法读取文件内容,若发生错误抛出所有异常,并将异常处理交给调用者
        File file=new File(Processor.WEB_ROOT+fileName);//实例化文件对象并
                                                            与实际文件建立关联
        if (!file.exists()) {                             //判断文件是否存在
            sendError(404,"File Not Found");//文件不存在则输出错误信息—是输
                                                  出显示在客户端
            return;
        }

        InputStream in=new FileInputStream(file);//创建文件输入流对象并打开
                                                       源文件
        byte content[]=new byte[(int) file.length()];     //实例化字节数组         in.read(content);        //从输入流对象中读入数据到字节数组content中
        out.println("HTTP/1.1 200 sendFile");              //输出流打印信息
        out.println("Content-length:"+content.length);
        out.println();//这段打印信息并不会显示在web浏览器网页源代码中,应该是
给http协议理解的,过滤掉了
        out.write(content);    //将字节数组内容写入输出流—真正的请求文件信息
        out.flush();                          //强制输出缓冲区数据
        out.close();                          //关闭打印流
        in.close();                           //关闭输入流
    }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    public void sendError(int errNum,String errMsg) {//构造错误信息发送方out.println("HTTP/1.1"+errNum+" "+errMsg);
        out.println("Content-type:text/html");
        out.println();      //输出信息---不会在客户端请求web页面原代码中出现
        out.println("<html>");   //以下信息会出现在客户端请求web页面原代码中
        out.println("<head><title>Error"+errNum+"--"+errMsg+"</title></head>");                                                       //输出HTML语言信息
        out.println("<h1"+errNum+" "+errMsg+"</h1>");
        out.println("</html>");
        out.println();
        out.flush();                                     //强制输出缓冲区数据
        out.close();                                     //关闭输出流
    }
}

四、实验效果

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值