一个web服务器也叫做HTTP服务器,因为它使用HTTP协议同客户端(即浏览器)通信。一个基于Java的web服务器用到的两个重要类:java.net.Socket和java.net.ServerSocket,通信协议采用HTTP。因此,很自然的接下来我们就以HTTP和java的这两个类来谈谈web服务器。随后我们再介绍一个简单的web服务器应用。

一、HTTP(The Hypertext Transfer Protocol):

Http是允许web服务端和浏览器之间通过Internet发送/接收的协议,它是一个请求/响应的协议。浏览器请求一个文件,服务器会响应这个请求。Http用Tcp连接方式----默认端口是80.Http的第一个发布版本是Http/0.9,目前一般用的是Http1.1.

通过Http协议,通常是浏览器通过建立连接并且发送请求来发起一个会话事务,服务器端会响应或者给浏览器一个响应的连接,浏览器端或者服务器端可以在会话中提前终止一个连接。例如,当用一个浏览器作为客户端,可以点击停止按钮就可以终止正在下载的文件,从而有效的关闭与web服务器端的Http连接。

1.HTTP请求:

一个Http请求包含以下3个部分:

·Method-URI-Protocal/Version

·Request headers

·Entity body

一个HTTP请求的例子如下:

POST /examples/default.jsp HTTP/1.1

Accept:text/plain;text/html

Accept-Language:en-gb

Connection:Keep-Alive

Host:localhost

User-Agent:Mozilla/4.0(compatible;MSIE 4.01;windows 98)

Content-Length:33

Content-Type:application/x-www-form-urlencoded

Accept-Encoding:gzip,deflate

lastName=Franks&firstName=Michael

Method-URI-Protocal/Version出现在请求的第一行,即:

POST /examples/default.jsp HTTP/1.1

其中POST表示是请求的方法,/examples/default.jsp代表URI,HTTP/1.1表示Protocol/Version.

HTTP1.1支持七种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TRACE。其中GET和POST是最常用的。

URI完全地说明了源文件类型,一个URI通常是相对于服务器端的根目录。这样一来,URI的路径通常是这样的/*。URL统一资源定位符通常就是URI。协议版本代表所用的HTTP版本。

请求的文件头request header可以体现出请求的浏览器信息和实体等重要信息。例如,它包含浏览器所用的编码方式,实体的长度等等。每一个header被CRLF(carriage return/linefeed)分开,CRLF即回车换行。

在headers和entity实体之间,会有一个CRLF来分隔,这个对于HTTP请求格式非常重要。

CRLF告诉HTTP服务器请求的内容从哪里开始。

在上面的HTTP请求中,请求实体如下:

lastName=Franks&firstName=Michael

2.HTTP响应:

与Http请求相似,一个Http响应也由以下三部分组成:

·Protocol-Status code-Description

·Response headers

·Entity body

下面是一个HTTP响应的例子:

HTTP/1.1 200 OK

Server:Microsoft-IIS/4.0

Date:Mon,5 Jan 2004 13:13:33 GMT

Content-Type:text/html

Last-Modified:Mon,5 Jan 2004 13:13:12 GMT

Content-Length:112

HTTP Response Example

Welcome to Brainy Software

响应的第一行与请求的第一行格式有些相似。它告诉协议是HTTP1.1,请求成功标志200.并且一切正常OK。响应的报文头与请求的报文头相似,也包含了一些环境参数。同样响应报文也以CRLF来分隔开。

二、Socket类:

Socket是网络连接的一个端口。Socket可以使应用程序在网络中读/写到数据。分别位于不同计算机的两款应用软件可以依靠Socket相互进行接收/读取数据,为使一台计算机上的应用软件发送信息给另一台电脑,需要知道其IP地址和端口号。在Java中,socket类即是java.net.Socket类。

创建socket对象,可以用该类众多构造方法中的一种来构造对象,其中一个是这样的,需要host名字和端口号:public Socket (java.lang.String host,int port) .例如要连接端口号为80的yahoo.com,可以这样来写:new Socket(“yahoo.com”,80)。

下面的代码片断创建了一个socket类,不过是同本机127.0.0.1通信。

Socket socket = new Socket(“127.0.0.1”,”8080”);

OutputStream os = socket.getOutputStream();

boolean autoflush = true;

PrintWriter out = new PrintWriter(socket.getOutputStream(),autoflush);

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

//发送一个HTTP请求到web 服务器端

out.println(“GET /index.jsp HTTP/1.1”);

out.println(“Host:localhost:8080”);

outprintln(“Connection:Close”);

out.println();

//读取响应

boolean loop = true;

StringBuffer sb = new StringBuffer(8096);

while(loop){

if(in.ready){

int i=0;

while(i!=-1){

i = in.read();

sb.append((char)i);

}

}

loop = false;

}

Thread.currentThread().sleep(50);

//响应结果到显示平台

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

Socket.close();

三、ServerSocket类

Socket类代表“客户端”的socket,也就是说无论什么时候要连接远端服务器时,创建一个socket对象即可。现在,如果要想创建一个服务器应用,比如HTTP server或者FTP server,则需要用不同的方式。这是因为server端要实时接收客户端的请求。

ServerSocket与Socket不同。ServerSocket的角色是一直在等待客户端的请求。一旦ServerSocket接收到客户端的请求,则会创建一个Socket对象来进行通信。

要创建一个服务器套接字,你需要使用ServerSocket类提供的四个构造方法中的一个。你需要指定IP地址和服务器套接字将要进行监听的端口号。通常,IP地址将会是127.0.0.1,也就是说,服务器套接字将会监听本地机器。服务器套接字正在监听的IP地址被称为是绑定地址。服务器套接字的另一个重要的属性是backlog,这是服务器套接字开始拒绝传入的请求之前,传入的连接请求的****队列长度。

其中一个ServerSocket类的构造方法如下所示:

public ServerSocket(int port, int backLog, InetAddress bindingAddress);

对于这个构造方法,绑定地址必须是java.net.InetAddress的一个实例。一种构造InetAddress对象的简单的方法是调用它的静态方法getByName,传入一个包含主机名称的字符串,就像下面的代码一样。

InetAddress.getByName("127.0.0.1");

下面一行代码构造了一个监听的本地机器8080端口的ServerSocket,它的backlog为1。

new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));

一旦你有一个ServerSocket实例,你可以让它在绑定地址和服务器套接字正在监听的端口上等待传入的连接请求。你可以通过调用ServerSocket类的accept方法做到这点。这个方法只会在有连接请求时才会返回,并且返回值是一个Socket类的实例。Socket对象接下去可以发送字节流并从客户端应用中接受字节流,就像前一节"Socket类"解释的那样。实际上,这章附带的程序中,accept方法是****用到的方法

四、实例运用

我们这个应用包含如下三个类:

·HttpServer

·Request

·Response

该应用程序的入口(即main方法)在HttpServer类中,main方法创建一个HttpServer对象,然后调用其await方法,该方法正如其名,一直在监听给定的端口,等待HTTP请求,一旦有请求,则进行接收,然后返回response对象。这个方法一直在监听客户端的请求,直到有shutdown命令关闭之。

这个应用不仅仅能发送静态资源,例如HTML文件、image图片以及某一文件夹下的文件,而且能处理动态传送而来的字节。但是它不传送任何的header,比如dates、cookies等。

下面我们来详细的看看这三个类。

HttpServer类

HttpServer类是服务器端代码,如清单1.1。清单1.2详细展现await方法,在清单1.1中不再显示。

list1.1:HttpServer类

public class HttpServer{

/**

*WEB_ROOT这个路径下放置HTML文件和其他一些文件。

*在这个包中,WEB_ROOT就是工作路径”webroot”。

*工作路径是文件系统中java命令所调用的位置。

*/

public static final String WEB_ROOT =

System.getProperty(“user.dir”)+File.separator + “webroot”;

//关闭命令

private static final String SHUTDOWN_COMMAND = “/SHUTDOWN”;

//接收shutdown命令

private boolean shutdown = false;

public static void main(String[] args){

HttpServer server = new HttpServer();

server.await();

}

public void await(){

}

}

await方法详细如下:

public void await(){

ServerSocket serverSocket = null;

int port = 8080;

try{

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对象,并且parse

Request request = new Request(input);

request.parse();

//创建Response对象

Response response = new Response(output);

response.setRequest(request);

response.sendStaticResource();

//关闭socket

socket.close();

//查看URI是否为关闭uri

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

}catch(Exception e){

e.printStackTrace();

continue;

}

}

}

这个web服务器可以访问到所有WEB_ROOT目录以及子目录下的静态资源,WEB_ROOT的初始化如下:public static final String WEB_ROOT =

System.getProperty(“user.dir”)+File.separator +”webroot”;

代码包含了一个叫做webroot的目录,该目录下有一些静态资源。要访问服务器下的静态资源,URL可以这样写:http://machineName:port/staticResource.如果是跨机器访问,那么machineName就是计算机的名字或者IP地址,如果是同一台机器,则为localhost或着计算机名,端口就是8080,staticResource就是你将要访问的文件名,但是该文件必须在WEB_ROOT目录下。

例如,如果你在同一机器通过服务器访问,要访问该服务的index.html文件,URL为:http://localhost:8080/index.html.

如果要关闭服务,则可以在浏览器上输入预先在程序中设置好的url路径。比如现在要停止当前正在运行的服务,我们这个例子中的关闭命令是通过HttpServer类中静态常量SHUTDOWN来控制,private static final String SHUTDOWN_COMMAND = “/SHUTDOWN”,因此我们要关闭该例子的服务,url可以这样来写:http://localhost:8080/SHUTDOWN.

现在,让我们来看看await方法。这个方法名是await而不是wait,主要是因为java.lang.Object这个超类中有个关于多线程的方法名叫做wait。await方法一开始就创建了一个ServerSocket对象,然后进行一个while循环。

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

//循环等待request请求

while (!shutdown) { ... }

循环代码中当在端口8080上有HTTP的请求时,serverSocket便会通过accept方法返回一个socket对象,即:socket = serverSocket.accpet().

Request类:

该类代表一个HTTP request对象。该request对象实例化需要传递一个socket对象的inputstream对象。可以调用InputStream对象的read方法来获取HTTP request对象所传输的数据。

该类的具体内容如下listing 1.3.该类含有两个方法,parse方法(listing 1.4)和getUri(listing 1.5)方法。

listing 1.3:

public class Request {

private InputStream input;

private String uri;

public Request(InputStream input) {

this.input = input;

}

public String getUri() {

return uri;

}

public void parse() {

……

}

private String parseUri(String requestString) {

……

}

}

Listing 1.4:

public void parse() {

// Read a set of characters from the socket

StringBuffer request = new StringBuffer(2048);

int i;

byte[] buffer = new byte[2048];

try {

i = input.read(buffer);

}

catch (IOException e) {

e.printStackTrace();

i = -1;

}

for (int j=0; j

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

}

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

uri = parseUri(request.toString());

}

Parse方法解析了request对象传输而来的数据。这个方法别无它途,仅仅要获取该http请求中的url路径。下面的getUri方法则返回了该url路径。

Listing 1.5:

private String parseUri(String requestString) {

int index1, index2;

index1 = requestString.indexOf(' ');

if (index1 != -1) {

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

if (index2 > index1)

return requestString.substring(index1 + 1, index2);

}

return null;

}

Response类:

该类如下:

public class Response {

private static final int BUFFER_SIZE = 1024;

Request request;

OutputStream output;

public Response(OutputStream output) {

this.output = output;

}

public void setRequest(Request request) {

this.request = request;

}

public void sendStaticResource() throws IOException {

byte[] bytes = new byte[BUFFER_SIZE];

FileInputStream fis = null;

try {

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

if (file.exists()) {

fis = new FileInputStream(file);

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

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\r\n" +

"Content-Type: text/html\r\n" +

"Content-Length: 23\r\n" +

"\r\n" +

"

File Not Found

";

output.write(errorMessage.getBytes());

}

}catch (Exception e) {

// 如果不能实例化File对象,则会抛出一个异常

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

}finally {

if (fis!=null)

fis.close();

}

}

}

首先注意到的是,通过构造函数获取到java.io.OutputStream对象,如下:

public Response(OutputStream output) {

this.output = output;

}

运行这个应用:

在浏览器地址栏上输入地址:http://localhost:8080/index.html,可以看到测试页面。

如果输入一个不存在的文件地址,http://localhost:8080/indexs.html 如下,则返回404错误。

java tomcat原理图,浅谈tomcat工作原理相关推荐

  1. 观察者模式(浅谈监听器工作原理)

    从某种角度来说,我们总是处于两种生活状态:观察者与被观察者.当处于观察者状态时,被观察的对象会向我们发出某种信息,使我们产生某种心理活动或行为状态的改变.当我们处于被观察者状态时,我们的行为活动又可以 ...

  2. 浅谈搜索引擎工作原理

    做为网页开发者,仅仅会编写代码完成业务功能是远远不够的,你做的网站最后需要搜索引擎这个公共入口来呈现给用户.所以搜索引擎优化是及其重要的,而要了解如何优化自己的网站从而适应搜索引擎,我们需要先了解搜索 ...

  3. 浅谈tomcat中间件的优化【转】

    今天来总结一下tomcat的一些优化的方案,由于本人才疏学浅,写的不好,勿喷! tomcat对于大多数从事开发工作的童鞋应该不会很陌生,通常做为默认的开发环境来为大家服务,不过tomcat默认的一些配 ...

  4. 浅谈tomcat优化

    前言 对于JavaWeb开发人员而言,Tomcat已成为默认的web服务器,但是在生产环境下使用Tomcat部署应用,我们如果采用Tomcat默认的配置,尤其是内存和线程的配置,其配置都很低,容易成为 ...

  5. 浅谈代理服务器工作的原理

    浅谈代理服务器工作的原理 (1) 代理服务原理 代理服务器有很多种,大体来说有http,ftp,socks代理三种,其中又分透明代理和不透明代理.其中透明代理一般是网关,是硬件.所以这里讨论不透明代理 ...

  6. 浅谈:Spring Boot原理分析,切换内置web服务器,SpringBoot监听项目(使用springboot-admin),将springboot的项目打成war包

    浅谈:Spring Boot原理分析(更多细节解释在代码注释中) 通过@EnableAutoConfiguration注解加载Springboot内置的自动初始化类(加载什么类是配置在spring.f ...

  7. 浅谈“三层结构”原理与用意(转帖)

    浅谈"三层结构"原理与用意 序 在刚刚步入"多层结构"Web应用程序开发的时候,我阅读过几篇关于"asp.net三层结构开发"的文章.但其多 ...

  8. svc的参考文献_浅谈SVC的原理及作用

    浅谈 SVC 的原理及作用 超(特)高压运行检修公司 自贡中心 涂洪骏 摘要: 介绍了静止补偿器 (SVC) 的工作特性.基本原理.运行方式,重点针对 SVC 的作用进行了分析. 关键词 :静止补偿器 ...

  9. 浅谈Wi-Fi渗透--原理篇

    浅谈Wi-Fi渗透–原理篇 在这个手机比人多的移动时代,无线网络Wi-Fi遍布每个角落,殊不知隐藏其中的风云涌动 广义上无线网络应用类型如下,今天的文章就聚焦于 WLAN的一种无线局域网技术--Wi- ...

最新文章

  1. 3项目里面全局用less变量 cli vue_VUE CLI3 less 全局变量引用
  2. C#自定义控件在添加引用后不显示在工具箱的解决方法
  3. SciSharpCube:容器中的SciSharp,.NET机器学习开箱即用
  4. 智能雷达物位计说明书_?浅谈人工检尺法和雷达液位计在油罐液位测量中的应用...
  5. python评估不平衡数据集_Python Pandas:平衡不平衡的数据集(用于面板分析)
  6. php字符长度函数漏洞 ctf,CTF中常见php-MD5()函数漏洞
  7. 数据流技术在GPU和大数据处理中的应用
  8. java-集合(三)
  9. OpenHarmony 1.1.0 LTS 版本发布,十六大性能全面提升
  10. c语言 键盘输入结构体,C语言结构体问题
  11. 张小龙 4 小时演讲没时间看?看这一篇就够了!「附赠张小龙历年演讲实录 PDF」...
  12. Oracle Instant Client环境配置
  13. linux下安装百度云音乐,linux 安装网易云音乐
  14. MySQL知识总结 (六) MySQL调优
  15. GlobalMapper 脚本应用(持续更新)
  16. sqlyog导入数据的两种方式
  17. Resize a VMWare disk (zz)
  18. linux Ubuntu10.04操作系统不显示顶部和底部菜单
  19. 小技巧!如何把小图拼接成长图,将长图切成小图
  20. 支付宝支付 Django

热门文章

  1. 2003年以来网页尺寸增长3倍
  2. C/C++报错:全局变量重定义或是多次定义
  3. Android 曲线图绘制
  4. C语言程序设计及上机指导,《C语言程序设计上机指导》全套教案
  5. 机器人出魔切还是三相_哇!电站凝汽器清洗居然可以用机器人啦
  6. tf.slice解析
  7. mysql特有语法_MySQL详细的基础语法
  8. Android与Js进行交互
  9. 外服封号_外服大主播Diss原神:因为吐槽氪金体验差,米哈游把我号封了
  10. springboot调用python脚本_Springboot实现上传文件接口,使用python的requests进行组装报文上传文件的方法...