静态网页选择服务器,【杂谈】一个简易的静态网页服务器

前言

上一篇随笔

注:此项目不需要用tomcat,纯Java底层代码写就可以了。

概述

程序有三个类HttpServer,Request,Response。

HttpServer  => 负责监听socket连接,创建Request、Response对象

Request => 用于获取请求信息的URI(利用Socket的InputStream),这里URI就是静态网页文件的相对路径

Response => 用于发送响应数据报(利用Request获取请求信息,利用OutputStream写出数据)

程序包图:

12b29e66c044d1a4d598a15564a11b19.png

完整代码

由于贴完整代码都会使篇幅略显过长,所以下面都折叠起来了,看客可以逐个展开查看。

HttpServer.java

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.wze.ex01.pyrmont;importjava.io.File;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.InetAddress;importjava.net.ServerSocket;importjava.net.Socket;public classHttpServer {public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";private boolean shutdown = false;public static voidmain(String[] args) {

System.out.println(WEB_ROOT);

HttpServer server= newHttpServer();

server.await();

}public voidawait() {

ServerSocket serverSocket= null;int port = 8080;try{//之所以要绑定监听的IP地址,是因为一个电脑可能有多个网卡

serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));

}catch(IOException e) {

e.printStackTrace();//如果绑定失败,那么这个程序也就没有运行下去的必要了。

System.exit(1);

}while(!shutdown) {

Socket socket= null;

InputStream input= null;

OutputStream output= null;try{//接收一个请求,处理完毕后关闭连接

socket =serverSocket.accept();

input=socket.getInputStream();

output=socket.getOutputStream();

Request request= newRequest(input);

request.parse();

Response response= newResponse(output);

response.setRequest(request);

response.sendStaticResource();

socket.close();

shutdown=request.getUri().equals(SHUTDOWN_COMMAND);

}catch(Exception e) {

e.printStackTrace();continue;

}

}

}

}

View Code

Request.java

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.wze.ex01.pyrmont;importjava.io.IOException;importjava.io.InputStream;public classRequest {privateInputStream input;privateString uri;publicRequest(InputStream input) {this.input =input;

}public voidparse() {//之所以是大小是2048,是因为请求行的大小一般就是2048

StringBuffer request = new StringBuffer(2048);inti;byte[] buffer = new byte[2048];try{

i= input.read(buffer); //读入数据到buffer,并返回请求行的实际长度

} catch(IOException e) {

e.printStackTrace();

i= -1;

}for(int j = 0; j < i; j++) {

request.append((char)buffer[j]);

}

System.out.println(request.toString());

uri= parseUri(request.toString()); //从请求行中把uri取出来

System.out.println(uri);

}/*** 获取请求行中的uri

*

* 请求行格式:Method URI Version

* 用空格做分隔符

*@paramrequestString

*@return

*/

privateString parseUri(String requestString) {intindex1, index2;

index1= requestString.indexOf(' ');if(index1 != -1) {

index2= requestString.indexOf(' ', index1+1);

System.out.println(index1+ " " +index2);if(index2 >index1)return requestString.substring(index1 + 1, index2);

}return null;

}publicString getUri() {returnuri;

}

}

View Code

Response.java

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

packagecom.wze.ex01.pyrmont;importjava.io.File;importjava.io.FileInputStream;importjava.io.IOException;importjava.io.OutputStream;public classResponse {private static final int BUFFER_SIZE = 1024;

Request request;

OutputStream output;publicResponse(OutputStream output) {this.output =output;

}public voidsetRequest(Request request) {this.request =request;

}public void sendStaticResource() throwsIOException {byte[] bytes = new byte[BUFFER_SIZE];

FileInputStream fis= null;try{//获取用户请求文件的实际路径

File file = new File(HttpServer.WEB_ROOT +request.getUri());

System.out.println(file);if(file.exists()) { //如果文件存在,则读取到缓冲数组,再利用socket的outputstream写出数据

long contentLength =file.length();

String successMessage= "HTTP/1.1 200 success

" +

"Content-Type:text/html

" +

"Content-Length:"+contentLength +"

" +

"

";

output.write(successMessage.getBytes());

fis= newFileInputStream(file);//每次最多读写1024字节,直到全部读完

int ch = fis.read(bytes, 0, BUFFER_SIZE);

System.out.println(ch);while(ch != -1) {

output.write(bytes,0, ch);

ch= fis.read(bytes, 0, BUFFER_SIZE);

}

}else{

String errorMessage= "HTTP/1.1 404 File Not Found

" +

"Content-Type:text/html

" +

"Content-Length:23

" +

"

" +

"

File Not Found

";

output.write(errorMessage.getBytes());

}

}catch(Exception e) {

System.out.println(e.toString());

}finally{if(fis != null)

fis.close();

}

}

}

View Code

运行效果

运行HttpServer的主方法,然后在浏览器地址栏键入localhost:8080/index.html,你就可以在浏览器看见网页内容了。到这一步就相当于实现了一个apache服务器。

07c8ecf6a6382f48ca3019b30b921428.png

注意:index.html是你自己创建的,你随便写点内容。我是只在body里面写了hello。

代码解析

Request对象中缓冲大小为什么是2048?

因为大多数浏览器请求行最大长度就是2048字节,所以读取2048字节,里面必然完全包含了请求行的数据。这也是parameter传参长度限制的原因,因为parameter在URI中,而URI又是组成请求行的元素之一。

注:HTTP请求报文的请求行由三部分组成,请求方法,URI,协议版本,且这三个参数用空格隔开。

前面说的例子有问题在哪里?

上面的例子是正常的,不过书本里面少了一部分,那就是响应头的编写,如果没有发送响应头给浏览器,它无法识别发送给它的数据是什么。

Content-Length在上文中起什么作用?

细心的朋友会发现,我在响应头中添加了Content-Length的头信息,指明了文件的长度,也就是字节数。有了这个头信息,浏览器就可以知道什么时候数据接收完成。这跟浏览器的加载提示有关。

怎么让别人也能访问到这个网页?

如果你的电脑有公网IP的话,那你要做的只是把程序跑起来挂着,然后开放端口。开放端口是什么意思?默认情况下,防火墙会为了安全,其他电脑是不能随便访问本机的端口(例外,80端口是默认开启的)。开启的方法就是进入防火墙设置进站规则,开放8080端口。

感悟

其实涉及到网络通信,底层传递的就是一堆字节,而"协议"从一个角度来说,其实就是双方共同遵守的数据格式,它指明从哪里到哪里的字节数据表示的是什么,应用程序根据这些进行处理。想来,其实这些东西在上《计算机网络》的时候都讲到了,只是当时没有现在这种感觉吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值