Java使用Scoket实现一个简单的Web服务器

一、项目介绍

该项目是用JAVA使用Scoket实现一个简单的Web服务器,项目的实现受到了 HaHa_Sir《Java写一个简单的Web服务器Socket实现》 的启发。

项目已实现功能:

  1. 用户能自由设置WebServer端口(1024-65535);
  2. 允许多个用户同时访问服务器;
  3. 实现HTML和JPG图片的解析;
  4. 资源文件实现相对路径解析;

二、整体思路

实现WebServer的逻辑是很清晰的:

  1. 设置WebServer端口号,其范围在1024-65535之间;
  2. 使用 ServerSocket.accept()方法,轮询监听用户请求;
  3. 用户使用浏览器输入地址,向WebServer发出请求;
  4. 服务器监听到用户请求,为该请求新建一个HttpServer来处理该请求;
  5. HttpServer解析用户请求并作出响应;
  6. 用户浏览器显示响应结果;

三、代码实现

该项目为WebServer,整个项目包含两个java文件,分别是webserver.java 和 httpserver.java。

1. WebServer.java

其中,WebServer.java 是整个项目的主线程,用于设置服务器端口号;监听用户的请求,为每一个监听到的请求新建一个httpserver线程,来处理用户请求。

函数 介绍
public static void startServer(int port) 轮询serverSocket.accept(),监听用户请求,为每一个监听到的请求,新建一个httpserver线程响应
public static void main(String[] args) 用于根据用户输入来设置服务器端口号,其后,启动Webserver服务器
package com.webServer;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;


public class WebServer {
    
    public static void startServer(int port){
        try {
            @SuppressWarnings("resource")
            ServerSocket serverSocket = new ServerSocket(port);
            while(true){
                Socket socket = serverSocket.accept();
                new HttpServer(socket).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    
    
    public static void main(String[] args){
        @SuppressWarnings("resource")
        Scanner in =new Scanner(System.in);
        System.out.print("Please input the Port Number( 1024 -65535 ):");
        int portNumber=80;
        do{
            portNumber=in.nextInt();
            if(portNumber>=1024 && portNumber <= 65535 )
                break;
            else
                System.out.print("The Input Port Number is wrong,please input again( 1024 -65535 ):");
        }while(in.hasNext());
        System.out.println("**********WebServer start!*********");
        startServer(portNumber);
    }
}

2. Httpserver.java

Httpserver.java 继承自Thread,用于读取用户访问路径,根据路径响应请求。

函数 介绍
public HttpServer(Socket socket) 初始化socket对象,获取对应 输入,输出流
public void run() 重载Run()函数,调用Read()、response()函数
private void response(String filePath) 根据读取的路径,进行响应。以流的形式读取文件,再以流的形式输出文件
private String read() 解析请求路径
package com.webServer;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
 

public class HttpServer extends Thread {
    //web资源路径,此处为相对路径
    public static final String ROOT = "./resource";
    
    //输入流对象,读取浏览器请求
    private InputStream input;
    
    //输出流对象,响应内容给浏览器
    private OutputStream out;
    
    //因为编码问题,text/html,若直接使用out输出会使用默认编码,而不是UTF-8,因此会导致中文乱码
    private OutputStreamWriter osw;
 
    //初始化socket对象,获取对应 输入,输出流
    public HttpServer(Socket socket) {
        try {
            input = socket.getInputStream();
            out = socket.getOutputStream();
            osw = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    //多线程方法调用
    @Override
    public void run() {
        String filePath = read();
        response(filePath);
    }
 
    private void response(String filePath) {
        File file = new File(ROOT + filePath);
        //System.out.println("filePath:"+filePath);
        System.out.println("filePath():"+file.getPath());
        //System.out.println("file.getName():"+file.getName());
        
        //防止用户直接输入"localhost:portnumber",导致路径为".\resource"而出现错误。
        if (file.exists() && !file.getPath().equals(".\\resource")) {
            //资源存在,读取资源
            try {
                //获取文件类型,判断Content-Type
                String fileType =file.getName().split("\\.")[1];
                //System.out.println("fileType:"+fileType);
                if(fileType.equals("html")) {
                    FileInputStream fis = new FileInputStream(file);   
                    InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
                    BufferedReader reader = new BufferedReader(isr);
                    StringBuffer sb = new StringBuffer();
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        sb.append(line).append("\r\n");
                    }
                    StringBuffer result = new StringBuffer();
                    result.append("HTTP/1.1 200 OK \r\n");
                    result.append("accept-ranges: bytes \r\n");
                    result.append("Content-Type:text/html;charset=UTF-8 \r\n");
                    result.append("Content-Length:" + file.length() + "\r\n");
                    result.append("\r\n" + sb.toString());
                    //System.out.println(result.toString());   
                    osw.write(result.toString());   
                    osw.flush();
                    osw.close();
                    fis.close();
                    isr.close();
                    reader.close();
                }else if(fileType.equals("jpg")) {
                    StringBuffer result = new StringBuffer();
                    result.append("HTTP/1.1 200 OK \r\n");
                    result.append("accept-ranges: bytes \r\n");
                    result.append("Content-Type:image/jpeg \r\n");
                    result.append("Content-Length:" + file.length() + "\r\n");
                    result.append("\r\n");
                    //System.out.println(result.toString());   
                    out.write(result.toString().getBytes());   
                    FileInputStream fis = new FileInputStream(file);
                        byte[] buf=new byte[1024];
                        int len=0; 
                    while((len=fis.read(buf))!=-1){
                       out.write(buf,0,len);
                    }
                    out.flush();
                    out.close();
                    fis.close();
                }
                
            } catch (Exception e) {
                e.printStackTrace();                                    
            }
 
        } else {   
            //资源不存在,提示 File not found
            try {
                StringBuffer error = new StringBuffer();
                String html="<h1>404 File Not Found.</h1>";
                
                error.append("HTTP/1.1 404 Not Found \r\n");
                error.append("Content-Type:text/html;charset=UTF-8 \r\n");
                error.append("Content-Length:").append(html.length()).append("\r\n").append("\r\n");
                error.append(html);

                osw.write(error.toString());
                osw.flush();
                osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
    }
 
    //解析请求路径
    private String read() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        try {
            // 读取请求头, 如:GET /index.html HTTP/1.1
            String readLine = reader.readLine();
            // 防止用户直接输入"localhost:portnumber"而出现错误。
            System.out.println(readLine);
            if(readLine==null)
                return "/";
            String[] split = readLine.split(" ");
            if (split.length != 3) {
                return "/";
            }
            return split[1];
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

3.Index.html

用户请求的页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebServerIndex</title>
</head>
<body>
<div style="margin-top: 5%; text-align: center;height: 100%;">
<!-- 该图片路径为resource下的相对路径,若想放自己的图片,则将其命名为picture.jpg即可 -->
    <img src="/picture.jpg" alt=""/>
    <h1 style="font-size: 72px">Hello World!你好,世界!</h1>
</div>
</body>
</html>

4.资源路径

为了项目的可移植性,文件的解析路径是相对路径,整个文件都放在文件夹resource下(若想修改相对路径,在HttpServer.java ROOT中修改即可)。


1.jpg

resource文件夹下文件示例:


2.jpg

四、项目测试

  1. 将项目导出jar包;

  2. 在windows 系统cmd下,输入java -jar WebServer.jar运行WebServer.jar,根据提示,输入合适的端口号,运行webServer;


    5.jpg
  3. 用户使用浏览器进行请求(这里使用的请求链接是 http://localhost:1025/index.html 1025 为我们输入设置的端口号);

    4.jpg

  4. 与此同时,cmd运行的WebServer情况如下;


    3.jpg

备注:Index.html在WebServer.jar路径下resource文件夹下

作者简介:一木一生,一个散发着单身狗清香的程序猿。同时,欢迎关注我的CSDN博客 Vito_w7

本文为作者原创,未经允许,不得转载,违者必究!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容

  • 一. Java基础部分.................................................
    wy_sure阅读 3,808评论 0 11
  • 转自陈明乾的博客,可能有一定更新。 转原文声明: 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 ...
    LUNJINGJIE阅读 3,969评论 1 33
  • 逆向思维 什么是逆向思维呢?正向思维可能大家都很熟悉,比如说今天出门看到天上乌云密布,判断可能要下雨,知道出门要带...
    何慧阅读 486评论 0 0
  • 8月的第二天,周日,夏日的炎热让人心烦意乱,真的仅仅是夏日的炎热吗? 对我来说,整个七月应该是黑色,股市投资失利,...
    _Record_阅读 170评论 0 1
  • 蝶恋花(词林正韵) 寂寞庭深愁绪闹,落叶情堪,秋雨寒风啸。伫望梧桐凋寂悄,残灯独晃知天晓。 欲尽此情痕泪照,难与倾...
    叶楚易阅读 587评论 9 18