用 Java 实现一个简单的多线程 web 服务器

来源:互联网 发布:社交网络关系图 编辑:程序博客网 时间:2024/05/09 16:57

用 Java 实现一个简单的多线程 web 服务器

1. 整体思路

  • 主线程
    1. 建立一个ServerSocket
    2. 调用ServerSocket的accept方法。该方法一直阻塞,等待连接。如果连接建立,就会返回一个Socket对象。
    3. 生成一个子线程处理Socket。
    4. 执行步骤2。
  • 子线程
    1. 从Socket获得输入流,读入请求报文,找出请求资源的路径。
    2. 从Socket获得输出流,响应请求的资源(资源存在) 或 返回错误页面(资源不存在)。

2. 源码

  • Tomtiger类(主线程)
package tomtiger;/** * @author liuleilei liuleilei2015@gmail.com * @date 2017年11月20日 下午4:52:55 * @Description:the new Web Server Tomtiger, haha!  */import java.io.File;import java.io.IOException;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java.util.List;import java.util.LinkedList;public class Tomtiger {    //定义html页面等的存放位置    public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "WEB_ROOT";    //定义web服务器占用的端口号    public static final int port = 8080;    //定义一个列表,存放 为每个连接的socket建立的线程。    private List<Thread> connectlist = null;    //main方法,服务器开始启动    public static void main(String[] args) {        //webserver 开始启动        Tomtiger server = new Tomtiger();        server.start();    }    public void start() {        ServerSocket serversocket = null;        try {            //server 监听127.0.0.1:8080            serversocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));            //server 已经启动            System.out.println("Tomtiger is running!!");        }catch(IOException e) {            e.printStackTrace();        }        //用来记录求情的数量        int count = 0;        //各线程存放的列表        connectlist = new LinkedList<Thread>();        while(true) {            Socket socket = null;            try {                //为该请求建立连接                socket = serversocket.accept();                System.out.println("连接"+ count +"以建立!!");                //为该socket建立多线程,启动,并加入列表                ConnectionThread connectionthread = new ConnectionThread(socket);                Thread thread = new Thread(connectionthread);                thread.start();                connectlist.add(thread);                System.out.println("连接"+ count++ +"的线程已启动并加入队列!!");            }catch(Exception e) {                e.printStackTrace();                break;            }        }    }}
  • ConnectionThread类(子线程)
package tomtiger;/** * @author liuleilei liuleilei2015@gmail.com * @date 2017年11月20日 下午2:46:53 * @Description: implement the thread for every connection */import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.io.IOException;public class ConnectionThread implements Runnable{    Socket socket = null;    InputStream inputstream = null;    OutputStream outputstream = null;    // 根据socket初始化socket的多线程类    public ConnectionThread(Socket socket) {        this.socket = socket;    }    /* (non-Javadoc)     * @see java.lang.Runnable#run()     */    @Override    public void run() {        // TODO Auto-generated method stub        try {            // 获得该连接的输入输出流            inputstream = socket.getInputStream();            outputstream = socket.getOutputStream();            //为该连接建立request,request从inputstream中读入http请求报文,转化为字符串,并截取到请求的资源。            Request request = new Request(inputstream);            //从request中获得请求资源的uri            String uri = request.getUri();            // 为请求建立response,response根据传入的outputstream,和uri。建立请求资源文件到outputstream之间的通路,并相应相应的资源。若资源不存在,响应默认页面            Response response = new Response(outputstream);            // response响应请求的资源            response.responseResource(uri);        }catch(IOException e) {            e.printStackTrace();        }finally {        }    }}
  • Request类(处理Socket的输入流)
package tomtiger;/** * @author liuleilei liuleilei2015@gmail.com * @date 2017年11月20日 下午4:00:14 * @Description: implement the request for connection to get the uri */import java.io.InputStream;import java.io.IOException;import java.io.BufferedReader;import java.io.InputStreamReader;public class Request {    private InputStream inputstream = null;    private String uri = null;    // 根据inputstream初始化request    public Request(InputStream inputstream) {        this.inputstream = inputstream;        parseUri();    }    // 读取inputstream并将其转化为字符串    @SuppressWarnings("finally")    private String requestToString() {        String requestString = null;        // 字节流转字符流        BufferedReader bfreader = new BufferedReader(new InputStreamReader(inputstream));        StringBuffer buffer = new StringBuffer();        // 这里我开始用的常用的while进行读取,但是一直卡在这里(不理解)!!!!        char[] temp = new char[2048];        int length = 0;        try {            length = bfreader.read(temp);            buffer.append(temp,0,length);               requestString = buffer.toString();        }catch(IOException e) {            e.printStackTrace();        }finally {            // 输出request            System.out.println("request为:");            System.out.println(requestString);            return requestString;        }    }    // 根据请求报文的特点,请求的文件在第一个和第二个空格之间。所以有了以下方法    public void parseUri() {        String request = requestToString();        if(request != null) {            int space1 = -1;            int space2 = -1;            space1 = request.indexOf(' ');            if(space1 != -1) {                space2 = request.indexOf(' ',space1 + 1);            }            if(space2 > space1) {                // 截取第一个和第二个空格之间的字符串,即请求资源的uri                uri = request.substring(space1 + 1, space2);            }        }    }    // 返回请求资源的uri    public String getUri() {        return uri;    }}
  • Response类(处理Socket的输出流)
package tomtiger;/** * @author liuleilei liuleilei2015@gmail.com * @date 2017年11月20日 下午4:37:49 * @Description: implement the response of connection */import java.io.OutputStream;import java.io.IOException;import java.io.File;import java.io.FileInputStream;public class Response {    // 定义读取文件时byte[] 数组的大小    private static final int BUFFER_SIZE = 1024;    // 用来响应的outputstream,该输出流从 为该请求建立的socket中获得,并传入    OutputStream outputstream = null;    // 用outputstream初始化response    public Response(OutputStream outputstream) {        this.outputstream = outputstream;    }    // 该请求资源的文件路径    public void responseResource(String uri) {        // 读取文件时的byte[]        byte[] resourcetemp = new byte[BUFFER_SIZE];        // 输入流        FileInputStream fileinputstream = null;        if(uri == null)            return;        try {            // 建立文件            File resource = new File(Tomtiger.WEB_ROOT,uri);            // 判断文件是否存在            if(resource.exists()) {                System.out.println("请求的资源是:" + resource.getName());                fileinputstream = new FileInputStream(resource);                int length = 0;                String responsehead = "HTTP/1.1 200 OK\r\n" +  "\r\n";                outputstream.write(responsehead.getBytes());                while((length = fileinputstream.read(resourcetemp)) > 0) {                    outputstream.write(resourcetemp, 0, length);                }            }            else {                String errorPage = "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>";                outputstream.write(errorPage.getBytes());            }            outputstream.close();        }catch(IOException e){            e.printStackTrace();        }finally {            outputstream = null;            fileinputstream = null;        }    }}

3. 执行

  • 控制台
    服务器运行

  • 浏览器
    浏览器

4. 参考

博客