当前位置:网站首页>手写一个简单的web服务器(B/S架构)
手写一个简单的web服务器(B/S架构)
2022-07-31 22:22:00 【IT_WEH_coder】
涉及Web服务器的相关技术
IO流、Socket网络编程、HTTP协议、文件、多线程(同时处理多个浏览器的请求)、List集合
1 web应用服务器运行原理:
连接过程:Web服务器与其浏览器之间建立的连接,判定两者之间是否连接成功;若用户可以找到并打开虚拟文件套接字,说明Web服务器与其浏览器之间成功建立了连接。
请求过程:Web浏览器利用socket文件向其服务器发出各种请求。
获取请求资源的地址
根据是静态资源还是动态资源做出相应的处理
响应过程:在请求过程中发出的请求通过使用HTTP协议传输到Web服务器,然后执行任务处理。
关闭连接:响应过程完成后,Web服务器与其浏览器断开连接的过程。
2 代码展示
(1)Main类
package com.weh;
import com.weh.httpserver.MyHttpServer;
public class Main {
// 开启多线程
public static void main(String[] args) {
new Thread(new MyHttpServer()).start();
}
}
(2)MyHttpServer类
package com.weh.httpserver;
import com.weh.response.MyResponse;
import com.weh.request.MyRequest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/* * 1.通过Runnable接口的实现类,来开启线程 * 2.创建并开启服务器 * 3.实现服务端与客户端进行连接通信 * */
public class MyHttpServer implements Runnable{
public static final int port = 8080; //服务器端口号
public static final String WEB_ROOT=System.getProperty("user.dir")+
File.separator+ "webapps"; //服务器的当前文件目录路径
@Override
public void run() {
ServerSocket serverSocket=null;
Socket socket;
InputStream input=null;
OutputStream output=null;
try {
System.out.println("Web服务器已开启");
serverSocket = new ServerSocket(port); //传入端口号并创建服务器
} catch (Exception e) {
System.exit(0);
}
//服务端与客户端浏览器进行通信
while(true){
try {
socket=serverSocket.accept();//请求
input=socket.getInputStream();//获取客户端发过来信息
output=socket.getOutputStream();
MyRequest request=new MyRequest(input); //数据处理
request.parse();
MyResponse response=new MyResponse(output);
response.setMyRequest(request);
response.sendStaticResource();//发送
} catch (Exception e) {
e.printStackTrace();
continue;
}finally {
// 释放资源
release(input,output);
}
}
}
// 释放资源
public void release(InputStream inputStream,OutputStream outputStream){
try {
if(inputStream != null) {
inputStream.close();
}
if(outputStream != null){
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
(3)MyRequest类
package com.weh.request;
import java.io.InputStream;
/* * 自定义MyRequest类 * 1.主要处理客户端发过来的信息进行处理并输出 * 2.获取客户端切换到哪个文件的名称 * */
public class MyRequest {
private InputStream input;
private String uri;
public MyRequest(InputStream input){
this.input=input;
}
// 输出客户端发过来的信息
public void parse(){
StringBuffer request=new StringBuffer(2048);
int len;
byte[] buffer=new byte[2048];
try {
len=input.read(buffer);
} catch (Exception e) {
len=-1;
}
for(int j=0;j<len;j++){
request.append((char)buffer[j]);
}
System.out.print(request.toString());
parseUri(request.toString());
}
// 获取切换后的文件名
public void parseUri(String srt){
int index1,index2;
index1=srt.indexOf(" ");
if(index1!=-1){
index2=srt.indexOf(" ",index1+1);
if(index2>index1){
setUri(srt.substring(index1+1,index2));
}
}
}
public void setUri(String uri) {
this.uri = uri;
}
public String getUri(){
return this.uri;
}
}
(4)MyResponse类
package com.weh.response;
import com.weh.httpserver.MyHttpServer;
import com.weh.request.MyRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
/* * 自定义MyResponse类 * 1.向Socket的输出流中写请求给浏览器作出响应的结果 * 2.获取当前目录下的所有文件并创建文件类型的数组 * 3.数据的读取及转发 * */
public class MyResponse {
private static final int BUFFER_SIZE=1024;
MyRequest request;
OutputStream output;
FileInputStream fis=null;
PrintWriter printWriter =null;
public MyResponse(OutputStream output){
this.output=output;
}
public void setMyRequest(MyRequest request){
this.request=request;
}
public void sendStaticResource()throws IOException{
byte[] bytes=new byte[BUFFER_SIZE];
try {
printWriter = new PrintWriter(output); //创建缓冲字符输出流
File file=new File(MyHttpServer.WEB_ROOT+request.getUri());
if(file.exists()){
//判断文件目录是否存在
//向Socket的输出流中写请求给浏览器作出响应的结果
printWriter.println("HTTP/1.1 200 OK");
printWriter.println("Content-Type:text/html;charset=UTF-8");
printWriter.println();
// 判断文件是否为普通文件
if(file.isFile()){
fis=new FileInputStream(file.getPath()); //创建文件字节输入流
fis.read(bytes); //读取直接流
printWriter.write(new String(bytes));
// 判断文件是否为文件目录
}else if(file.isDirectory()){
List<File> list= Arrays.asList(new File(file.getPath()).listFiles());//获取当前目录下的所有文件并创建文件类型的数组
for(File filename : list){
//数组遍历
if(filename.getName().indexOf(".htm")>0) {
//判断是否含有.htm的字符的文件
printWriter.write("<li>");
printWriter.write("<a style='text-decoration:blink;' href='" +
"./" + filename.getName() + "'>"
+ filename.getName() + "</a>");
printWriter.write("</li>");
}
}
}
// 若该文件不存在,则输出信息 File Not Found
}else{
printWriter.println("HTTP/1.1 404 File Not Found");
printWriter.println("Content-Type:text/html;charset=UTF-8");
printWriter.println("Content-Length:23");
printWriter.println();
printWriter.write("<h1>File Not Found</h1>");
}
printWriter.flush();//不返回任何值,只用于刷新流
} catch (Exception e) {
e.printStackTrace();
}finally{
if(printWriter!=null){
printWriter.close();
}
if(output!=null){
output.close();
}
if(fis!=null){
fis.close();
}
}
}
}
3项目的结构设计

4 效果展示

边栏推荐
- Count characters in UTF-8 string function
- The old music player WinAmp released version 5.9 RC1: migrated to VS 2019, completely rebuilt, compatible with Win11
- Pytest first experience
- 【论文精读】iNeRF
- Fixed-length usage of nanopb string type based on RT1052 Aworks (27)
- @JsonFormat(pattern=“yyyy-MM-dd“)时间差问题
- LevelSequence source code analysis
- Basic Grammar Introduction of Carbon Tutorial (Tutorial)
- flowable workflow all business concepts
- Douyin fetches video list based on keywords API
猜你喜欢
SQL27 View user details of different age groups

一款国外开发的高质量WordPress下载站模板主题

数据分析(一)——matplotlib

Binary tree non-recursive traversal

嵌入式开发没有激情了,正常吗?

20. Support vector machine - knowledge of mathematical principles

Realization of character makeup

The article you worked so hard to write may not be your original

Implementation of a sequence table

角色妆容的实现
随机推荐
「SDOI2016」征途 题解
Commonly used security penetration testing tools (penetration testing tools)
Unity - LineRenderer show a line
Basic Grammar Introduction of Carbon Tutorial (Tutorial)
"SDOI2016" Journey Problem Solution
高通cDSP简单编程例子(实现查询高通cDSP使用率、签名),RK3588 npu使用率查询
Istio introduction
支付模块实现
UserAgent resolution
无状态与有状态的区别
HTC using official firmware as bottom bag made ROM brush card bag tutorial
#yyds干货盘点# 面试必刷TOP101:链表中环的入口结点
基于RT1052 Aworks nanopb string 类型固定长度使用方式(二十七)
SQL27 View user details of different age groups
Realize serial port receiving data based on STM32 ring queue
【公开课预告】:超分辨率技术在视频画质增强领域的研究与应用
grep command written test questions
Architecture Battalion Module 8 Homework
Flink_CDC construction and simple use
如何减少软件设计和实现之间鸿沟