用 Java 来打造属于自己的Web服务器
很久很久以前,没有web服务器的概念,就知道在windows下配置一下IIS,用来跑asp。到后来的asp.net也还是IIS。接着又接触到java,于是知道了还有apache和tomcat。然后是php,也还是和apache在打交道。直至某一天,我又知道了这个世界上还有俄罗斯人写的优秀的nginx,以及lighttpd等web服务器。
一直以来,我都以为web服务器是一个非常复杂的系统,需要有着高深的理论知识才能去写这么一个软件,它应该是一个团队才可以应付的事情。当然,也想过它的工作原理,无非是绑定一个端口,然后处理web请求并做出相应的响应。不过,始终都认为那应该是一件神奇的事情,不是我等凡夫俗子可以对付得了。
这两天对各种服务器的配置都做了一些了解,对于写web服务器的冲动又一下子涌上心头了。最开始想用c,于是便下载了一个有若干年没有碰过的tc,稍微调试了一番,发现还缺少很多头文件,一时半会还找不全;而对于c++,又觉得过于繁杂,不想去碰它;c#的话,总感觉微软的东东过于庞大臃肿,也不想理会;于是,自然而然就想到了java。
很简单地,最开始肯定是用socket绑定一个端口,接下来试着从浏览器访问这个端口。没想到,还真能将请求发到这个socket里头来,把请求内容输出以后,发现正是http协议的标准写法,熟悉的GET / HTTP/1.1\r\n 映入了眼帘。在这一刻,我似乎对于web这一回事又有了一个更深刻的认识。浏览器只不过按照http协议的要求,把请求封装成标准格式,打包发送到web服务器上。web服务器将请求收集起来,按照http协议的规定将请求解析,然后把对应的内容找到,又一次按照http协议的要求封装起来,返回给浏览器端。浏览器接下来把这些返回的内容解析,并渲染出来。这些流程,以前也有很多次见识过,不过,总还是感觉有些抽象。不过,今天这样随便一写,却感觉这一切都变得那么具体和熟悉起来。
对于请求的处理,需要涉及到多线程,因此干脆从网上搜索了一篇文章:WebServer.java 用JAVA编写Web服务器,并对其进行了相应的修改,使得扩展性和可读性更强。在这篇文章的基础上,加入了两个新类——PageRequest和PageResponse,分别用来处理请求和响应。

Code
//AnyWebServer.java 用JAVA编写Web服务器
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;


public class AnyWebServer
{

public static void main(String args[])
{
int i = 1, PORT = 1300;
ServerSocket server = null;
Socket client = null;

try
{
server = new ServerSocket(PORT);
System.out.println("Web Server is listening on port "
+ server.getLocalPort());

for (;;)
{
client = server.accept(); // 接受客户机的连接请求
new ConnectionThread(client, i).start();
i++;
}

} catch (Exception e)
{
System.out.println(e);
}
}
}


/**//* ConnnectionThread类完成与一个Web浏览器的通信 */

class ConnectionThread extends Thread
{
// 网站根目录
private static final String WEB_ROOT = "d:\\workspace\\anyws\\site\\";
Socket client; // 连接Web浏览器的socket字
int counter; // 计数器


public ConnectionThread(Socket cl, int c)
{
client = cl;
counter = c;
}

public void run() // 线程体

{

try
{
String destIP = client.getInetAddress().toString(); // 客户机IP地址
int destport = client.getPort(); // 客户机端口号
System.out.println("Connection " + counter + ":connected to "
+ destIP + " on port " + destport + ".");
PrintStream outstream = new PrintStream(client.getOutputStream());
DataInputStream instream = new DataInputStream(client
.getInputStream());
PageRequest req = new PageRequest(instream);
req.init();
System.out.println("Received:" + req.getMethod() + " " + req.getFile() + " " + req.getVersion());

if (req.getMethod().equalsIgnoreCase("GET"))
{ // 如果是GET请求
String filename = req.getFile();
if (filename.equals("/")) filename = "index.html";
File file = new File(WEB_ROOT + filename);
PageResponse res = new PageResponse(outstream);

if (file.exists())
{ // 若文件存在,则将文件送给Web浏览器
System.out.println(filename + " requested.");
long lastModified = file.lastModified();
String eTag = String.valueOf(lastModified);
res.addHeader("ETag", eTag);

if (eTag.equals(req.getHeader("If-None-Match")))
{
res.setStatus(304);

} else
{
int len = (int) file.length();
res.addHeader("Content-Length", String.valueOf(len));
Date now = new Date();
res.addHeader("Date", now.toString());
res.addHeader("Expires", new Date(
now.getTime() + 3600 * 24 * 1000).toString());
res.appendFile(file);
}

} else
{ // 文件不存在时
System.out.println(filename + " not exist.");
String msg = "<html><head><title>Not Found</title></head><body><h1>HTTP/1.0 404 not found</h1></body></html>";
res.setStatus(404);
res.addHeader("Content-type", "text/html");
res.addHeader("Content-Length", String
.valueOf(msg.length() + 2));
res.write(msg);
}
res.flush();
}
instream.close();
outstream.close();
long m1 = 1; // 延时

while (m1 < 11100000)
{
m1++;
}
client.close();

} catch (IOException e)
{
System.out.println("Exception:" + e);
}
}
}


/** *//**
* 页面请求类
* @author Administrator
*
*/

class PageRequest
{
private DataInputStream in;

public PageRequest(DataInputStream in)
{
this.in = in;
}
private HashMap<String, String> headers = new HashMap<String, String>();
private String method;

/** *//**
* 请求方法
* @return
*/

public String getMethod()
{
return this.method;
}

private String file;

public String getFile()
{
return this.file;
}
private String version;

public String getVersion()
{
return this.version;
}
@SuppressWarnings("deprecation")

public void init()
{

try
{
String firstLine = in.readLine();
String[] arr = firstLine.split(" ");
method = arr[0];
file = arr[1];
version = arr[2];
String line = null;

while (!(line = in.readLine()).equals(""))
{
arr = line.split(": ");
headers.put(arr[0], arr[1]);
}
System.out.print(line);
//TODO

} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/** *//**
* 获取请求头
* @param key
* @return
*/

public String getHeader(String key)
{
return headers.get(key);
}
}


/** *//**
* 页面响应类
*
* @author Administrator
*/

class PageResponse
{
private PrintStream ps;

/** *//**
* 页面响应头
*/
private HashMap<String, String> headers = new HashMap<String, String>();
private static HashMap<Integer, String> codes = null;
private StringBuffer content = new StringBuffer();
private int code = 200;


public PageResponse(PrintStream ps)
{
this.ps = ps;

initHeader();
initCodes();
}


private void initHeader()
{
headers.put("Server", "Deng Web Server");
headers.put("Date", new Date().toString());
}


private void initCodes()
{

if (codes == null)
{
codes = new HashMap<Integer, String>();
codes.put(200, "OK");
codes.put(304, "Not Modified");
codes.put(400, "Not Found");
codes.put(401, "Unauthorized");
codes.put(402, "Payment Required");
codes.put(403, "Forbidden");
codes.put(500, "Internal Server Error");
}
}


/** *//**
* 设置响应状态值
*
* @param code
*/

public void setStatus(int code)
{
this.code = code;
}


/** *//**
* 添加响应头
*
* @param key
* @param value
*/

public void addHeader(String key, String value)
{
headers.put(key, value);
}


/** *//**
* 添加内容
*
* @param str
*/

public void write(String str)
{
content.append(str);
}


/** *//**
* 将页面内容输出
*/

public void flush()
{
ps.println("HTTP/1.1 " + code + " " + codes.get(code) + "\r");

for (String key : headers.keySet())
{
ps.println(key + ":" + headers.get(key));
}
ps.println();
ps.print(content.toString());
ps.flush();
}


/** *//**
* 输出文件
*
* @param file
*/

public void appendFile(File file)
{

try
{
DataInputStream in = new DataInputStream(new FileInputStream(file));
int len = (int) file.length();
byte buf[] = new byte[len];
in.readFully(buf);
content.append(new String(buf, 0, len));
in.close();

} catch (Exception e)
{
System.out.println("Error retrieving file.");
System.exit(1);
}
}
}
//WebServer.java 用JAVA编写Web服务器
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;


public class AnyWebServer
{

public static void main(String args[])
{
int i = 1, PORT = 1300;
ServerSocket server = null;
Socket client = null;

try
{
server = new ServerSocket(PORT);
System.out.println("Web Server is listening on port "
+ server.getLocalPort());

for (;;)
{
client = server.accept(); // 接受客户机的连接请求
new ConnectionThread(client, i).start();
i++;
}

} catch (Exception e)
{
System.out.println(e);
}
}
}


/**//* ConnnectionThread类完成与一个Web浏览器的通信 */

class ConnectionThread extends Thread
{
// 网站根目录
private static final String WEB_ROOT = "d:\\workspace\\anyws\\site\\";
Socket client; // 连接Web浏览器的socket字
int counter; // 计数器


public ConnectionThread(Socket cl, int c)
{
client = cl;
counter = c;
}

public void run() // 线程体

{

try
{
String destIP = client.getInetAddress().toString(); // 客户机IP地址
int destport = client.getPort(); // 客户机端口号
System.out.println("Connection " + counter + ":connected to "
+ destIP + " on port " + destport + ".");
PrintStream outstream = new PrintStream(client.getOutputStream());
DataInputStream instream = new DataInputStream(client
.getInputStream());
PageRequest req = new PageRequest(instream);
req.init();
System.out.println("Received:" + req.getMethod() + " " + req.getFile() + " " + req.getVersion());

if (req.getMethod().equalsIgnoreCase("GET"))
{ // 如果是GET请求
String filename = req.getFile();
if (filename.equals("/")) filename = "index.html";
File file = new File(WEB_ROOT + filename);
PageResponse res = new PageResponse(outstream);

if (file.exists())
{ // 若文件存在,则将文件送给Web浏览器
System.out.println(filename + " requested.");
long lastModified = file.lastModified();
String eTag = String.valueOf(lastModified);
res.addHeader("ETag", eTag);

if (eTag.equals(req.getHeader("If-None-Match")))
{
res.setStatus(304);

} else
{
int len = (int) file.length();
res.addHeader("Content-Length", String.valueOf(len));
Date now = new Date();
res.addHeader("Date", now.toString());
res.addHeader("Expires", new Date(
now.getTime() + 3600 * 24 * 1000).toString());
res.appendFile(file);
}

} else
{ // 文件不存在时
System.out.println(filename + " not exist.");
String msg = "<html><head><title>Not Found</title></head><body><h1>HTTP/1.0 404 not found</h1></body></html>";
res.setStatus(404);
res.addHeader("Content-type", "text/html");
res.addHeader("Content-Length", String
.valueOf(msg.length() + 2));
res.write(msg);
}
res.flush();
}
instream.close();
outstream.close();
long m1 = 1; // 延时

while (m1 < 11100000)
{
m1++;
}
client.close();

} catch (IOException e)
{
System.out.println("Exception:" + e);
}
}
}


/** *//**
* 页面请求类
* @author Administrator
*
*/

class PageRequest
{
private DataInputStream in;

public PageRequest(DataInputStream in)
{
this.in = in;
}
private HashMap<String, String> headers = new HashMap<String, String>();
private String method;

/** *//**
* 请求方法
* @return
*/

public String getMethod()
{
return this.method;
}

private String file;

public String getFile()
{
return this.file;
}
private String version;

public String getVersion()
{
return this.version;
}
@SuppressWarnings("deprecation")

public void init()
{

try
{
String firstLine = in.readLine();
String[] arr = firstLine.split(" ");
method = arr[0];
file = arr[1];
version = arr[2];
String line = null;

while (!(line = in.readLine()).equals(""))
{
arr = line.split(": ");
headers.put(arr[0], arr[1]);
}
System.out.print(line);
//TODO

} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/** *//**
* 获取请求头
* @param key
* @return
*/

public String getHeader(String key)
{
return headers.get(key);
}
}


/** *//**
* 页面响应类
*
* @author Administrator
*/

class PageResponse
{
private PrintStream ps;

/** *//**
* 页面响应头
*/
private HashMap<String, String> headers = new HashMap<String, String>();
private static HashMap<Integer, String> codes = null;
private StringBuffer content = new StringBuffer();
private int code = 200;


public PageResponse(PrintStream ps)
{
this.ps = ps;

initHeader();
initCodes();
}


private void initHeader()
{
headers.put("Server", "Deng Web Server");
headers.put("Date", new Date().toString());
}


private void initCodes()
{

if (codes == null)
{
codes = new HashMap<Integer, String>();
codes.put(200, "OK");
codes.put(304, "Not Modified");
codes.put(400, "Not Found");
codes.put(401, "Unauthorized");
codes.put(402, "Payment Required");
codes.put(403, "Forbidden");
codes.put(500, "Internal Server Error");
}
}


/** *//**
* 设置响应状态值
*
* @param code
*/

public void setStatus(int code)
{
this.code = code;
}


/** *//**
* 添加响应头
*
* @param key
* @param value
*/

public void addHeader(String key, String value)
{
headers.put(key, value);
}


/** *//**
* 添加内容
*
* @param str
*/

public void write(String str)
{
content.append(str);
}


/** *//**
* 将页面内容输出
*/

public void flush()
{
ps.println("HTTP/1.1 " + code + " " + codes.get(code) + "\r");

for (String key : headers.keySet())
{
ps.println(key + ":" + headers.get(key));
}
ps.println();
ps.print(content.toString());
ps.flush();
}


/** *//**
* 输出文件
*
* @param file
*/

public void appendFile(File file)
{

try
{
DataInputStream in = new DataInputStream(new FileInputStream(file));
int len = (int) file.length();
byte buf[] = new byte[len];
in.readFully(buf);
content.append(new String(buf, 0, len));
in.close();

} catch (Exception e)
{
System.out.println("Error retrieving file.");
System.exit(1);
}
}
}
一直以来,我都以为web服务器是一个非常复杂的系统,需要有着高深的理论知识才能去写这么一个软件,它应该是一个团队才可以应付的事情。当然,也想过它的工作原理,无非是绑定一个端口,然后处理web请求并做出相应的响应。不过,始终都认为那应该是一件神奇的事情,不是我等凡夫俗子可以对付得了。
这两天对各种服务器的配置都做了一些了解,对于写web服务器的冲动又一下子涌上心头了。最开始想用c,于是便下载了一个有若干年没有碰过的tc,稍微调试了一番,发现还缺少很多头文件,一时半会还找不全;而对于c++,又觉得过于繁杂,不想去碰它;c#的话,总感觉微软的东东过于庞大臃肿,也不想理会;于是,自然而然就想到了java。
很简单地,最开始肯定是用socket绑定一个端口,接下来试着从浏览器访问这个端口。没想到,还真能将请求发到这个socket里头来,把请求内容输出以后,发现正是http协议的标准写法,熟悉的GET / HTTP/1.1\r\n 映入了眼帘。在这一刻,我似乎对于web这一回事又有了一个更深刻的认识。浏览器只不过按照http协议的要求,把请求封装成标准格式,打包发送到web服务器上。web服务器将请求收集起来,按照http协议的规定将请求解析,然后把对应的内容找到,又一次按照http协议的要求封装起来,返回给浏览器端。浏览器接下来把这些返回的内容解析,并渲染出来。这些流程,以前也有很多次见识过,不过,总还是感觉有些抽象。不过,今天这样随便一写,却感觉这一切都变得那么具体和熟悉起来。
对于请求的处理,需要涉及到多线程,因此干脆从网上搜索了一篇文章:WebServer.java 用JAVA编写Web服务器,并对其进行了相应的修改,使得扩展性和可读性更强。在这篇文章的基础上,加入了两个新类——PageRequest和PageResponse,分别用来处理请求和响应。








































































































































































































































































































































































































































































































































































































































































































































【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端