asp 做购物网站,网站外链是什么意思,做照片的网站有哪些,php做视频网站有哪些优质博文#xff1a;IT-BLOG-CN
目的#xff1a; 了解Java Web服务器是如何运行的。Web服务器使用HTTP与其客户端#xff0c;也就是Web浏览器进行通信。基于Java的Web服务器会使用两个重要类#xff1a;java.net.Socket类和java.net.ServerSocket类#xff0c;并通过发送…优质博文IT-BLOG-CN
目的 了解Java Web服务器是如何运行的。Web服务器使用HTTP与其客户端也就是Web浏览器进行通信。基于Java的Web服务器会使用两个重要类java.net.Socket类和java.net.ServerSocket类并通过发送HTTP消息进行通信。
一、HTTP
超文本传输协议Hypertext Transfer ProtocolHTTP是一个简单的请求-响应协议它通常运行在TCP之上。运行Web服务器和浏览器通过Internet发送并接收数据。请求和响应消息的头以ASCII形式给出这个简单模型是早期Web成功的有功之臣因为它使开发和部署非常地直截了当。HTTP使用可靠的TCP连接默认使用TCP的80端口。
在HTTP中总是由客户端通过建立连接并发送HTTP请求来初始化一个事物的。Web服务器端并不负责联系客户端或建立一个到客户端的回调链接。客户端或服务器端可提前关闭连接, 例如, 当使用Web浏览器浏览网页时, 可以单击浏览器上的stop按钮来停止下载文件, 这样就有效的关闭了一个 Web服务器的http连接。
一个HTTP请求包含以下三部分: 【1】请求方法统一资源标识符Uniform Resource Identifier, URI协议/版本 【2】请求头 【3】实体
// 请求方式 - URL - 协议/版本
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 HTTP 1.1支持7种类型的请 求GET, POST, HEAD, OPTIONS, PUT, DELETE和TRACE。GET和POST在互联网中最常用的两种请求。
一个HTTP响应包含以下三部分 【1】协议、状态码、描述 【2】响应头 【3】响应实体段
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
html head titleHTTP Response Example/title /head body Welcome to Brainy Software /body
/html响应头部的第一行类似于请求头部的第一行。第一行告诉你该协议使用HTTP 1.1请求成功200表示一切都运行良好。 响应头部和请求头部类似也包括很多有用的信息。响应的主体内容是响应本身的HTML内容。
二、Socket类
Socket为网络通信提供了一组丰富的方法和属性。 Socket允许使用枚举中列出的ProtocolType任何通信协议执行同步和异步数据传输。套接字是网络连接的一个端点。套接字使得一个应用可以从网络中读取和写入数据。放在两个不同计算机上的两个应用可以通过连接发送和接受字节流。为了从你的应用发送一条信息到另一个应用你需要知道另一个应用的IP地址和套接字端口。
// host远程主机的地址port远程端口
public Socket (java.lang.String host, int port) 一旦你成功创建了一个Socket类的实例你可以使用它来发送和接受字节流。要发送字节流你首先必须调用Socket类的getOutputStream方法来获取一个java.io.OutputStream对象。要发送文本到一个远程应用你经常要从返回的OutputStream对象中构造一个java.io.PrintWriter对象。要从连接的另一端接受字节流你可以调用Socket类的getInputStream方法用来返回一个java.io.InputStream对象。 以下的代码片段创建了一个套接字可以和本地HTTP服务器(127.0.0.1是指本地主机)进行通讯发送一个HTTP请求并从服务器接受响应。它创建了一个StringBuffer对象来保存响应并在控制台上打印出来。
public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {Socket socket new Socket(127.0.0.1, 80); //想要发送自己流你需要的得到socket类返回的一个OutputStream对象OutputStream os socket.getOutputStream(); boolean autoflush true; //通过现有的OutputStream构建一个PrintWriter对象来向输出流中写数据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); out.println(Connection: Close); out.println(); // 读取返回值 boolean loop true; StringBuffer sb new StringBuffer(8096); while (loop) {// 告知是否准备读取此流if ( in.ready() ) {int i0; while (i!-1) { // 读取单个字符i in.read();sb.append((char) i);} loop false;}Thread.currentThread().sleep(50);
} //关闭 socket
socket.close(); ServerSocket类
Socket类代表一个客户端套接字即任何时候你想连接到一个远程服务器应用的时候你构造的套接字现在假如你想实施一个服务器应用例如一个HTTP服务器或者FTP服务器你需要一种不同的做法。这是因为你的服务器必须随时待命因为它不知道一个客户端应用什么时候会尝试去连接它。为了让你的应用能随时待命你需要使用java.net.ServerSocket类。这是服务器套接字的实现。
ServerSocket和Socket不同服务器套接字的角色是等待来自客户端的连接请求。一旦服务器套接字获得一个连接请求它创建一个Socket实例来与客户端进行通信。 要创建一个服务器套接字你需要使用ServerSocket类提供的四个构造方法中的一个。你需要指定IP地址和服务器套接字将要进行监听的端口号。通常IP地址将会是127.0.0.1也就是说服务器套接字将会监听本地机器。服务器套接字正在监听的IP地址被称为是绑定地址。服务器套接字的另一个重要的属性是backlog这是服务器套接字开始拒绝传入的请求之前传入的连接请求的最大队列长度。 其中一个ServerSocket类的构造方法如下所示:
// 创建绑定到特定端口的服务器套接字。
public ServerSocket(int port) throws IOException
// 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
public ServerSocket(int port, int backlog) throws IOException
// 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
public ServerSocket(int port, int backlog, InetAddress address) throws IOException
// 创建非绑定服务器套接字。使用此构造方法时 如果没有抛出异常就意味着应用程序已经成功绑定到指定的端口并且侦听客户端请求。
public ServerSocket() throws IOException通过ServerSocket创建实例后可以让它在绑定地址和服务器套接字正在监听的端口上等待传入的连接请求。你可以通过调用ServerSocket类的accept方法做到这点。这个方法只会在有连接请求时才会返回并且返回值是一个Socket类的实例。Socket对象接下去可以发送字节流并从客户端应用中接受字节流就像上述的Socket类解释的那样。
//ServerSocketDemo
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;public class ServerSocketDemo extends Thread {private ServerSocket serverSocket;private int i 1;public ServerSocketDemo(int port) throws IOException {serverSocket new ServerSocket(port);//设置20s内无客户端连接则抛出SocketTimeoutException异常serverSocket.setSoTimeout(20000);}public void run(){while(true) {System.out.println(服务端第i次启动中...对应的端口号为 serverSocket.getLocalPort());i;try {Socket server serverSocket.accept();//彩蛋//server.setSoTimeout(5);//彩蛋//当服务端监听到客户端的连接后才会执行以下代码System.out.println(服务端打印的远程主机地址为server.getRemoteSocketAddress());//监听来自客户端的消息DataInputStream dis new DataInputStream(server.getInputStream());System.out.println(服务端接收到的来自于客户端的信息为dis.readUTF());//通过socket向客户端发送信息DataOutputStream dos new DataOutputStream(server.getOutputStream());dos.writeUTF(我是服务端您已连接到server.getLocalSocketAddress());server.close();}catch (SocketTimeoutException e){System.out.println(20s内无客户端连接正在关闭服务端监听服务);continue;}catch (IOException e) {e.printStackTrace();break;}}}public static void main(String[] args) {try {Thread t1 new ServerSocketDemo(8089);t1.run();}catch(IOException e){e.printStackTrace();return;}}}三、HttpServer类
HttpServer类表示一个Web服务器具体实现如代码如下
public class HttpServer {public static void main(String[] args) {HttpServer server new HttpServer();server.await();}public void await() {}
}这个Web服务器可以处理对指定目录中的静态资源的请求该目录包括由公有静态变量final WEB_ROOT指明的目录及其所有子目录。WEB_ROOT的初始值为
public static final String WEB_ROOT System.getProperty(user.dir) File.separator webroot;该代码清单包含一个名为webroot的目录用于测试该应用程序的一些静态资源都位于该目录下。在该目录下还可以找到用于测试后续章节中应用程序的几个servlet程序。若要请求静态资源可以在浏览器的地址栏或URL框中输入如下的URL
http://machineName:port/staticResource若从另一台机器不是运行应用程序的那台机器上向该应用程序发出请求则machineName是应用程序所在计算机的名称或IP地址若在同一台机器上发出的请求则可以将machineName替换为localhost此外连接请求使用的端口为8080。staticResource是请求的文件的名字该文件必须位于WEB_ROOT指向的目录下。
例如如果你正使用同一台机器来测试该应用程序你想让HttpServer对象发送index.html文件就可以使用如下的URL
http://localhost:8080/index.html若要关闭服务器可以通过Web浏览器的地址栏或URL框在URL的host:port部分后面输入预先定义好的字符串从Web浏览器发送一条关闭命令这样服务器就会收到关闭命令了。关闭命令定义在HttpServer类的SHUTDOWN静态final变量中
private static final String SHUTDOWN_COMMAND /SHUTDOWN;因此若要关闭服务器需要使用如下的URL
private static final String SHUTDOWN_COMMAND /SHUTDOWN;因此若要关闭服务器需要使用如下的URL
http://localhost:8080/SHUTDOWN在应用程序的入口点也就是静态main函数中创建一个HttpServer实例然后调用其await()方法。顾名思义await方法会在制定的端口上等待http请求并对其进行处理然后发送相应的消息回客户端。在接收到命令之前它会一直保持等待的状态。 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);}// Loop waiting for a requestwhile (!shutdown) {Socket socket null;InputStream input null;OutputStream output null;try {socket serverSocket.accept();input socket.getInputStream();output socket.getOutputStream();// create Request object and parseRequest request new Request(input);request.parse();// create Response objectResponse response new Response(output);response.setRequest(request);response.sendStaticResource();// Close the socketsocket.close();//check if the previous URI is a shutdown commandshutdown request.getUri().equals(SHUTDOWN_COMMAND);}catch (Exception e) {e.printStackTrace();continue;}}
}该方法名之所以称为await()而不是wait()是因为wait()方法是java.lang.Object类中与使用线程相关的重要方法。await()方法会先创建一个ServerSocket实例然后进入一个while循环
serverSocket new ServerSocket(port, 1, InetAddress.getByName(127.0.0.1));
......
// Loop waiting for a request
while(!shtdown) {...
}当从8080端口接收到HTTP请求后ServerSocket类的accept()方法返回等待结束
socket serverSocket.accept();接收到请求后await()方法会从accept()方法返回的Socket实例中获取java.io.InputStream对象和java.io.OutputStream对象
input socket.getInputStream();
output socket.getOutputStream();然后await()方法会创建一个ex01.pyrmont.Request对象并调用其parse()方法来解析HTTP请求的原始数据
// create Request object and parse
Request request new Request(input);
request.parse();然后await()方法会创建一个Response对象并分别调用其setRequest()方法和sendStaticResource()方法
// create Response object
Response response new Response(output);
response.setRequest(request);
response.sendStaticResource();最后await()方法关闭套接字调用Request类的getUri()方法来测试HTTP请求的URI是否是关闭命令。若是则将变量shutdown设置为true程序退出while循环。
// Close the socket
socket.close (); //check if the previous URI is a shutdown command
shutdown request.getUri().equals(SHUTDOWN_COMMAND);四、Request类
Request类表示一个HTTP请求。可以传递InputStream对象从通过处理与客户端通信的Socket对象中获取的来创建Request对象。可以调用InputStream对象中的read()方法来读取HTTP请求的原始数据。
package ex01.pyrmont;import java.io.InputStream;
import java.io.IOException;public class Request {private InputStream input;private String uri;public Request(InputStream input) {this.input input;}// 解析input输入流这里只是获取请求行的URI// 实际的解析过程远不止这些public void parse() {//下面是用最常见的read()方法获取输入流的内容也是为什么要传入输入流的原因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 i; j) {request.append((char) buffer[j]);}System.out.print(request.toString());uri parseUri(request.toString());}//获取URI通过对字符串进行简单的查询和切割获得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;}public String getUri() {return uri;}
}parse()方法用于解析HTTP请求中的原始数据。parse()方法会调用私有方法parseUri()来解析HTTP请求的URI除此之外并没有做太多的工作。parseUri()方法将URI存储在变量uri中。调用公共方法getUri()会返回HTTP请求的URI。parse()方法从传入到Request对象中的套接字的InputStream对象中读取整个字节流并将字节数组存储在缓冲区中。然后它使用缓冲区字节数组中的数组填充StringBuffer对象request并将StringBuffer的String表示传递给parseUri()方法。
parseUri()方法从请求行中获取URI。parseUri()方法在请求中搜索第一个和第二个空格从中找出URI。
五、Respose类
对目标文件存在与否进行两种不同的处理 如果存在就将文件的内容写入浏览器否则返回404页面到浏览器 从这个类可以看出这个类只是简单的文件作为静态资源将文件的内容写到浏览器中没有加载servlet的代码
package ex01.pyrmont;import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;/*HTTP Response Status-Line*(( general-header | response-header | entity-header ) CRLF)CRLF[ message-body ]Status-Line HTTP-Version SP Status-Code SP Reason-Phrase CRLF*/public class CopyOfResponse {private static final int BUFFER_SIZE 1024;Request request;OutputStream output;public CopyOfResponse(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 {//获取URI对应磁盘下的文件对象因为需要用到URI所以传入request参数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 {//文件不存在返回404页面String errorMessage HTTP/1.1 404 File Not Found\r\n Content-Type: text/html\r\n Content-Length: 23\r\n \r\n h1File Not Found/h1;output.write(errorMessage.getBytes());}} catch (Exception e) {System.out.println(e.toString());} finally {if (fis ! null)fis.close();}}
}response对象是通过传递由套接字获得的OutputStream对象给HttpServer类的await方法来构造的。Response类有两个公共方法setRequest和sendStaticResource。setRequest方法用来传递一个Request对象给Response对象sendStaticResource方法是用来发送一个静态资源例如一个HTML文件。它首先通过传递上一级目录的路径和子路径给File累的构造方法来实例化java.io.File类。File file new File(HttpServer.WEB_ROOT, request.getUri())。然后它检查该文件是否存在。假如存在的话通过传递File对象让sendStaticResource构造一个java.io.FileInputStream对象。然后它调用FileInputStream的read方法并把字节数组写入OutputStream对象。请注意这种情况下静态资源是作为原始数据发送给浏览器的。假如文件并不存在sendStaticResource方法发送一个错误信息到浏览器。