模拟web服务器 (小项目) 搭建+部署
模拟web服务器:可以从浏览器中访问到自己编写的服务器中的资源,将其资源显示在浏览器中。
技术选型:
corejava
线程池 同任务并发执行
IO流 传递数据 客户端也会像服务端发送数据, 服务器像客户端发送数据也是流
网络(Socket编程) 通过网络获取客户端的数据以及通过网络给客户端返回数据
ubuntu
软件都是部署Linux(ubuntu)
使用c/s架构,只编写服务器端,客户端由浏览器代替,浏览器向服务器发送一个请求Request,服务器给浏览器返回一个响应Response。
使用的是HTTP协议。
项目整体的文件结构:

应用包appliction,包含启动类
封装包pojo,包含了两个封装类 ,request类(请求类) response类(响应类)为了防止后序有对象信息的传输,所以提前给两个类实现Serializable接口(序列化、反序列化)。
工具包util,包含请求的文件类型FileTpye,线程池工具PoolUtil,Socket转换成Request对象,和Request转换成Response对象的工具类SocketTrans,装载着状态码信息的一个接口类 StateCodeEnum。
所需的文件资源放在了source文件下。
GET请求方式:
浏览器向服务器发请求:http://127.0.0.1:80/index.html
请求数据为:
//请求行包括:请求方式+空格+/+请求资源+空格+协议版本+会车换行符
GET /index.html HTTP/1.1
//请求方式为GET 请求资源为index.html 协议版本为HTTP/1.1 回车换行符为\r\n
//请求头:请求行下的都是请求头 包含客户端的一些基本信息
Host: 127.0.0.1:80 //客户端的ip和端口 都是以空格隔开
Connection: keep-alive //客户端的连接状态
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
//客户端的基本信息
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
//客户端可以接收的数据类型
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
发送一个GET请求由几部分组成:
请求行(请求方式,/请求资源名,协议名字/版本号)会车符 换行符
请求头 key:value 回车符 换行符
POST请求方式:由Postman软甲配合完成 选择POST方式 输入ip 端口 访问的资源
http://127.0.0.1:9999/login
从Body中输入key value参数

输入结束后连接即可。因为POST方式是向指定的资源提交要被处理的数据 ,查询字符串POST方式是在POST请求的HTTP主体中发送,而GET方式查询字符串是在GET请求的URL中发送,所以使用Postman方式进行模拟。
Request:需要封装请求行中的请求方式,用来根据不同的请求方式做不同的处理(GET没有请求体,而POST有请求体)、封装请求行中的请求资源,用来根据不同的请求资源发送不同的响应体、封装请求行中的协议版本(协议版本一般固定)、封装请求头(请求头中的内容都是key value值,所以使用一个map进行封装)、封装请求体。
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map; public class Request implements Serializable{ private static final long serialVersionUID = 1L; //请求方式
private String requestMethod;
//请求资源
private String requestSource;
//协议版本
private String agreement;
//请求头
private Map<String, String> requestHead = new HashMap<String, String>();
//请求体
private String requestBody; public String getRequestSource() {
return requestSource;
}
public void setRequestSource(String requestSource) {
this.requestSource = requestSource;
} public String getAgreement() {
return agreement;
}
public void setAgreement(String agreement) {
this.agreement = agreement;
}
public Map<String, String> getRequestHead() {
return requestHead;
}
public void setRequestHead(Map<String, String> requestHead) {
this.requestHead = requestHead;
}
public String getRequestBody() {
return requestBody;
}
public void setRequestBody(String requestBody) {
this.requestBody = requestBody;
}
public String getRequestMethod() {
return requestMethod;
}
public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
@Override
public String toString() {
return "Request [requestMethod=" + requestMethod + ", requestSource=" + requestSource + ", agreement="
+ agreement + ", requestHead=" + requestHead + ", requestBody=" + requestBody + "]";
} }
Response:和请求对应,响应有响应行、响应头、响应体。响应行中有版本信息,状态码信息。响应头中包含的是key value类型的数据,用一个map封装,响应体用Object类型修饰,因为不知道响应的是什么类型的数据。

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map; import com.briup.server.util.StateCodeEnum; public class Response implements Serializable{ private static final long serialVersionUID = 1L;
//响应行的版本信息
private String agreement = "HTTP/1.1";
//响应行的状态码信息
private StateCodeEnum stateCode;
//key值代表响应头的key value代表响应头的value
private Map<String, String> responseHeader = new HashMap<String, String>();
//响应体
private Object responseBody;
public String getAgreement() {
return agreement;
}
public void setAgreement(String agreement) {
this.agreement = agreement;
}
public StateCodeEnum getStateCode() {
return stateCode;
}
public void setStateCode(StateCodeEnum stateCode) {
this.stateCode = stateCode;
}
public Map<String, String> getResponseHeader() {
return responseHeader;
}
public void setResponseHeader(Map<String, String> responseHeader) {
this.responseHeader = responseHeader;
}
public Object getResponseBody() {
return responseBody;
}
public void setResponseBody(Object responseBody) {
this.responseBody = responseBody;
}
@Override
public String toString() {
return "Response [agreement=" + agreement + ", stateCode=" + stateCode + ", responseHeader=" + responseHeader
+ ", responseBody=" + responseBody + "]";
} }
FileType:响应头里的各种信息,根据key值得到响应的value值进行对响应头信息的封装。
import java.util.HashMap;
import java.util.Map; public class FileType {
public static final Map<String, String> TYPE;
static {
TYPE=new HashMap<>();
TYPE.put("txt","text/html;charset=tf-8");
TYPE.put("html","text/html;charset=tf-8");
TYPE.put("jpg","image/jpeg;charset=tf-8");
TYPE.put("jpeg","images/jpeg;charset=tf-8");
TYPE.put("png","image/png;charset=tf-8");
TYPE.put("mp4","video/mpeg4;charset=tf-8");
TYPE.put("pdf","application/pdf;charset=tf-8"); }
}
PoolUtil:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import javax.print.attribute.ResolutionSyntax; import com.briup.server.pojo.Request;
import com.briup.server.pojo.Response; public class PoolUtil { public static final ExecutorService pool;
static {
pool = Executors.newCachedThreadPool();
}
private static void writeToBrowser(Socket socket,Response response) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
StringBuilder builder = new StringBuilder();
builder.append(response.getAgreement()).append(" ").append(response.getStateCode().getCode()).append(" ").append(response.getStateCode().getMsg()).append("\r\n");
bos.write(builder.toString().getBytes());
//删除builder里面的数据 从0开始到最后
builder.delete(0, builder.length()); Set<Entry<String,String>> set = response.getResponseHeader().entrySet();
for (Entry<String, String> entry : set) {
builder.append(entry.getKey()).append(" ").append(entry.getValue()).append("\r\n");
}
//响应头
bos.write(builder.toString().getBytes());
//空行
bos.write("\r\n".getBytes());
//响应体
//如果是文件就写回文件,
if(response.getResponseBody() instanceof File) {
//将资源从本地磁盘读取
BufferedInputStream bis = new BufferedInputStream(new FileInputStream((File)response.getResponseBody()));
int count=-1;
byte []bytes = new byte[1024];
while((count=bis.read(bytes))!=-1) {
bos.write(bytes, 0, count);
}
bos.flush();
socket.shutdownOutput(); } }
private static void doGet(Request request,Response response,Socket socket) throws IOException {
//通过request对象获取requestBody
String string = request.getRequestBody();
if (string!=null &&!"".equals(string)) {
String[] infos = string.split("[&]");
if ("username=123".equals(infos[0]) && "password=123".equals(infos[1])) {
response.setStateCode(StateCodeEnum.OK);
response.setResponseBody(new File("source/success.html")); } else {
response.setStateCode(StateCodeEnum.LOGIN_FAIL);
response.setResponseBody(new File("source/fail.html"));
}
}
//将数据写回给浏览器
writeToBrowser(socket,response);
}
private static void doPost(Request request,Response response,Socket socket) throws IOException{
doGet(request, response, socket);
writeToBrowser(socket,response);
}
public static void service( Request request,Response response,Socket socket) {
pool.execute(()->{ try {
if(("GET").equals(request.getRequestMethod())) {
PoolUtil.doGet(request, response, socket);
}
else if(("POST").equals(request.getRequestMethod())){
PoolUtil.doPost(request, response, socket);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}); }
}
SocketTrans:
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket; import com.briup.server.pojo.Request;
import com.briup.server.pojo.Response; public class SocketTrans {
public static Request socketToRequest(Socket socket) throws IOException {
if(socket ==null) {
throw new RuntimeException("参数为空"); }
Request request = new Request();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//得到请求行
String line = reader.readLine();
String[] split = line.split(" ");
request.setAgreement(split[2]);
request.setRequestMethod(split[0]);
String[] split2 = split[1].split("[?]");
request.setRequestSource(split2[0]);
//数组长度大于1 代表后面有数据
if(split2.length>1) {
request.setRequestBody(split2[1]);
}
//得到请求头 while((line = reader.readLine())!=null&&!"".equals(line)) {
String[] herderInfo = line.split(":");
request.getRequestHead().put(herderInfo[0], herderInfo[1].trim());
}
//判断请求头里有没有Content-Length,有则代表有请求体
if (request.getRequestHead().containsKey("Content-Length")) {
request.setRequestBody(reader.readLine());
}
//封装完后 input流使用完毕了
socket.shutdownInput();
return request; }
public static Response getResponse(Request request) { if(request==null) {
throw new RuntimeException("参数为空");
}
Response response = new Response();
//设置协议版本
response.setAgreement(request.getAgreement());
//判断资源是否存在
String fileName = request.getRequestSource();
File file = new File("source",fileName);
//设置响应体
//得到请求资源的后缀名
fileName= fileName.substring(fileName.lastIndexOf(".")+1);
//文件存在后缀名正确 不存在后缀名可能正确 也可能不正确
if(file.exists()) {
response.setStateCode(StateCodeEnum.OK);
response.setResponseBody(file);
response.getResponseHeader().put("Content-Type:", FileType.TYPE.get(fileName));
}else {
response.setStateCode(StateCodeEnum.NOT_FOUND);
response.setResponseBody(new File("source/404.html"));
response.getResponseHeader().put("Content-Type:", FileType.TYPE.get("html"));
}
return response;
}
}
StateCodeEnum :
public enum StateCodeEnum {
OK(200,"OK"),NOT_FOUND(404,"NOT_FOUND"),LOGIN_FAIL(401,"UNAUTHORIZED");
private int code;
private String msg;
private StateCodeEnum(int code,String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
项目的代码基本结束,可以实现从浏览器端向服务器端。
将项目打包,右击项目,选择Export ,选择jarFile,选择要打包的地点。
将项目部署到Ubuntu的一个镜像中,方便测试,

在Ubuntu中,点击文件,点击打开,选中server.ovf(镜像文件),将其导入。
为方便远程操作服务器,使用一个远程连接工具srct814-x64软件。

在Ubuntu中查看ip 使用ifconfig ,在srct814-x64中添加此ip并连接。
在srct-x64中安装jdk,配置环境变量,使其具备Java环境。
在srct-x64中点击file,打开connect SFTP session ,将打包好的server.jar文件和资源文件夹source拖进这个窗口。

在该控制台窗口中整理文件,将source和jar包放在一个文件夹下,此时项目就算部署好了,在该窗口运行jar包
Java -jar server.jar
打开浏览器输入服务器的ip和端口号和你要访问的资源就可以访问到了。
如我的Ubuntu中ip是192.168.235.129 端口号为9999
在浏览器中输入 192.168.235.129:9999/1.png
就可以访问到服务器中的1.png文件
模拟web服务器 (小项目) 搭建+部署的更多相关文章
- 云服务器+tomcat+mysql+web项目搭建部署
云服务器+tomcat+mysql+web项目搭建部署 1.老样子,开头墨迹两句. 作为我的第二篇文章,有很多感慨,第一篇人气好低啊,有点小丧气,不过相信我还是经验少,分享的都是浅显的,所以大家可能不 ...
- 模拟XShell的小项目
不知道大家有没有用过XShell这款工具,这款工具通过windows可以远程操作处于开机状态的linux操作系统,也就是说把你的电脑和一台服务器连入网络,你通过输入服务器所在的IP地址建立一个会话就可 ...
- Web服务器集群搭建关键步骤纪要
前言:本文记述了搭建一个小型web服务器集群的过程,由于篇幅所限,系统.软件的安装和基本配置我这里就省略了,只记叙关键配置和脚本内容.假如各位朋友想了解各软件详细配置建议查阅官方文档. 一 需求分析: ...
- web实践小项目<一>:简单日程管理系统(涉及html/css,javascript,python,sql,日期处理)
暑假自学了些html/css,javascript和python,苦于学完无处练手几乎过目即忘...最后在同学的建议下做了个简单日程管理系统.借第一版完成之际,希望能将实践期间犯过的错误和获得的新知进 ...
- 如何搭建web服务器 使用Nginx搭建反向代理服务器 .
引言:最近公司有台服务器遭受DDOS攻击,流量在70M以上,由于服务器硬件配置较高所以不需要DDOS硬件防火墙.但我们要知道,IDC机房是肯定不允许这种流量一直处于这么高的,因为没法具体知道后面陆续攻 ...
- LNMP小项目搭建,Centos7.6环境搭建Linux+nginx+mysql+php,wordpress个人博客的搭建(完整搭建步骤)
一.LNMP搭建,基于nginx服务器搭建wordpress个人博客 准备环境:centos7.6环境下web服务器(nginx+php):主机名:web01,ip:192.168.248.172my ...
- Linux Ubuntu从零开始部署web环境及项目-----搭建ssh环境(一)
linux搭建ssh环境 1,用户登录 成功输入用户名和密码后 进入Ubuntu界面 2,配置网络 参考:http://blog.csdn.net/liu782726344/article/deta ...
- 第一篇 先用socket模拟web服务器
一.用socket来模拟网站访问 socket为python2.7 #!/usr/bin/env python # -*- coding:utf-8 -*- import socket def han ...
- 模拟web服务器http请求应答
我们在浏览器打开网页,其实是向远端服务器提出页面发送请求,远端服务器在接到请求后,就开始执行请求页面的程序文件,然后将执行结果通过html格式,发送到你的浏览器,再显示出来.以下用百度(www.bai ...
随机推荐
- SLAM01
上周末发现了一个巨大的问题,就是我们目前构建的室内定位的方法中,一个基本的假设是错的----这就非常尴尬了. 于是乎赶紧抱一波佛脚,学习一下slam里相关的问题是怎么解决的,找找灵感. 结果看了个开头 ...
- 深信服EDR3.2.21任意代码执行
漏洞原理: dev_linkage_launch.php 为设备联动的新入口点主要是将联动的接口构造成业务统一处理的接口 主要调用 跟进 可以看到 第一个检查为 $req_url = $_SERVE ...
- gcc选项 笔记
gcc –E hello.c –o hello.i 使用gcc的选项"-E" 让gcc在预处理结束后停止编译过程. gcc –S hello.i –o hello.s &q ...
- Flink-v1.12官方网站翻译-P025-Queryable State Beta
可查询的状态 注意:可查询状态的客户端API目前处于不断发展的状态,对所提供接口的稳定性不做保证.在即将到来的Flink版本中,客户端的API很可能会有突破性的变化. 简而言之,该功能将Flink的托 ...
- springboot中扩展ModelAndView实现net mvc的ActionResult效果
最近在写spring boot项目,写起来感觉有点繁琐,为了简化spring boot中的Controller开发,对ModelAndView进行简单的扩展,实现net mvc中ActionResul ...
- 2019牛客暑期多校训练营(第四场)D-triples I
>传送门< 题意:求最少需要多少个3的倍数按位或后可以得到数字a 思路:利用3的倍数对应的二进制数的性质来先选出一个x,然后根据数字a再配一个y出来 首先,我们都知道十进制中,任意一个数只 ...
- P4755 Beautiful Pair (分治 + 主席树)
题意:1e5的数组 计算有多少对 ai * aj <= max(ai ai+1...aj-1 aj) 题解:在处理这种涉及到区间极值的题时 好像是个套路分治 从级值中间分成两个区间 从区间短的那 ...
- Codeforces Round #651 (Div. 2) A. Maximum GCD(数论)
题目链接:https://codeforces.com/contest/1370/problem/A 题意 有 $n$ 个数大小分别为 $1$ 到 $n$,找出两个数间最大的 $gcd$ . 题解 若 ...
- [POJ 2585] Window Pains 拓朴排序
题意:你现在有9个2*2的窗口在4*4的屏幕上面,由于这9这小窗口叠放顺序不固定,所以在4*4屏幕上有些窗口只会露出来一部分. 如果电脑坏了的话,那么那个屏幕上的各小窗口叠放会出现错误.你的任务就是判 ...
- Redis 管理命令
INFO 命令 # 查看redis相关信息 127.0.0.1:6379> info # 服务端信息 # Server # 版本号 redis_version:3.2.12 # redis版本控 ...