一、项目介绍
该项目是用JAVA使用Scoket实现一个简单的Web服务器,项目的实现受到了 HaHa_Sir 的《Java写一个简单的Web服务器Socket实现》 的启发。
项目已实现功能:
- 用户能自由设置WebServer端口(1024-65535);
- 允许多个用户同时访问服务器;
- 实现HTML和JPG图片的解析;
- 资源文件实现相对路径解析;
二、整体思路
实现WebServer的逻辑是很清晰的:
- 设置WebServer端口号,其范围在1024-65535之间;
- 使用 ServerSocket.accept()方法,轮询监听用户请求;
- 用户使用浏览器输入地址,向WebServer发出请求;
- 服务器监听到用户请求,为该请求新建一个HttpServer来处理该请求;
- HttpServer解析用户请求并作出响应;
- 用户浏览器显示响应结果;
三、代码实现
该项目为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="404 File Not Found.
";
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
用户请求的页面
WebServerIndex
Hello World!你好,世界!
4.资源路径
为了项目的可移植性,文件的解析路径是相对路径,整个文件都放在文件夹resource下(若想修改相对路径,在HttpServer.java ROOT中修改即可)。
resource文件夹下文件示例:
四、项目测试
将项目导出jar包;
-
在windows 系统cmd下,输入java -jar WebServer.jar运行WebServer.jar,根据提示,输入合适的端口号,运行webServer;
-
用户使用浏览器进行请求(这里使用的请求链接是 http://localhost:1025/index.html 1025 为我们输入设置的端口号);
-
与此同时,cmd运行的WebServer情况如下;
备注:Index.html在WebServer.jar路径下resource文件夹下
作者简介:一木一生,一个散发着单身狗清香的程序猿。同时,欢迎关注我的CSDN博客 Vito_w7 。
本文为作者原创,未经允许,不得转载,违者必究!