- 浏览: 68552 次
- 性别:
- 来自: 成都
最新评论
-
qiaohhgz:
public static void String chang ...
一图和几句话解决java参数传值问题 -
ocaicai:
虽然我看懂了你的意思,但是我依然觉得表达得有些欠妥!
一图和几句话解决java参数传值问题 -
ql0722:
特别赞同第二条第三条
如果我再年轻几岁 -
zzc_zj:
很好的知识点,学习了
数据库查询select原理 -
dai03070609:
[url][/url][flash=200,200][/fla ...
数据库查询select原理
第一章 一个简单的web server
这章分析java web servers怎么工作。一个web 服务器也被称作一个超文本传输协议(HTTP)服务器,因为它使用HTTP来与客户端通信,客户端通常是web浏览器。一个基于java的web服务器使用2个重要的类:java.net.Socket和java.net.ServerSocket.它们之间的通信是通过HTTP消息完成的。很自然我们这章由讨论HTTP和这两个类为开始。最后,继续分析这个简单web服务器应用结束本章内容。
The Hypertext Transfer Protocol (HTTP)
HTTP是种允许web服务器和浏览器之间通过Internet互相发送和接收数据的协议。是一种基于请求/相应的协议。客户端向服务器发出请求来请求一个文件,服务器相应地对这个请求作出回应。HTTP使用可靠地TCP连接—其默认为TCP80端口号。第一个HTTP版本号是HTTP/0.9,然后被HTTP/1.0重新修改。代替HTTP/1.0版本的是现在的HTTP/1.1版本,1.1版本被定义为Request for Comments(RFC)2616 。
提示:这包含HTTP 1.1的部分只是简单的帮助你理解web服务器应用传递消息。如果想了解跟多,可以阅读 RFC 2016。
HTTP总是客户端通过建立连接和发送一个HTTP请求来初始化一个事务。Web服务器是不能够去连接一个客户端或者发出一个(callback connection)回调连接给客户端。客户端和服务器都可以提前结束一个连接。例如,当使用一个web浏览器你可以按浏览器上的stop按钮来停止下载一个文件,有效地关闭与web服务器的HTTP连接。
HTTP Requests
一个HTTP请求包含3个组成部分:
方法-统一资源定位符(uri)-协议/版本Method-Uniform ResourceIdentifer-Protocol/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
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
//这行是CRLF
lastName=Franks&firstName=Michael
POST是请求方法,/example/defaul.jsp 是请求的URI HTTP/1.1是协议的和版本部分。
每一个HTTP请求可以使用许多请求方法的一种作为HTTP标准。在HTTP1.1支持7中类型的请求:GET.POST,HEAD,OPTIONS,PUT,DELETE和TRACE。
GET和POST是在Internet应用中使用最普遍的。
URI完整地指定了Intetnet上的资源。一个URI通常被解析为服务器相关的根目录。这样,它总是可以使用一个前斜杠‘/’开始。一个URL(Uniform Resource Locator)本质上是URI的一种类型。
协议版本表示被使用的HTTP的协议版本号。
请求头部包含关于客户端环境和实体正文的有用的信息。例如,它包含浏览器设置的语言,正文的长度等等。每一个头部都被一个回车换行符(CRLF)序列分开。
在头部和实体正文有一个空行(CRLF),这个空行对HTTP求个格式尤为重要。CRLF告诉HTTP服务器正文从哪里开始。在一些Internet编程书籍中介绍这个换行符(CRLF)是HTTP请求的第四组成部分。
在前面的HTTP请求中,实体正文只是简单的一行内容:
lastName=Franks&firstName=Michael
在一个电影的HTTP请求中,实体内容可以很容易地变得更长
HTTP Responses
与HTTP请求类似,一个HTTP 响应也包含3个组成部分:
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
//(CRLF)
<html>
<head>
<title>HTTP Response Example</title>
</head>
<body> Welcome to Brainy Software </body>
</html>
响应头的第一行与请求头的第一行很相似。第一行告诉我们协议是使用的HTTP1.1版本,请求成功(200表示请求成功),所有的请求都完成(ok)。
响应头报班的有用信息和请求头相似。在响应的实体正文是响应自己的HTML内容。头部和正文实体同样被一个(CRLF)序列隔开。
The Socket Class
一个socket是一个网络连接的终端。一个socket运行一个应用在网络上能够读和写。两个软件应用安装在2台不同的计算机上能够通过一个连接发送和接收字节流来相互通信。从你的应用发送一条消息到另一个应用,你需要了解另一个应用的IP地址和socket端口号。在java中,一个socket用java.net.Socket类表示。
你可以使用Socket类提供的诸多构造方法中的一种来创建一个socket。
其中一个构造方法接收一个主机名和端口号:
Public Socket (java.lang.String host , int port)
host是远程机器的名字或ip地址,port是远程应用的端口号。例如:在80端口连接yahoo.com
你可以象下面来构造一个Socket对象:
new Socket(”yahoo.com”,80);
一旦你成功地创建好一个Socket类的实例,你就可以使用它来发送可接收字节流。发送字节流,第一步你必须调用Socket类的 getOutputStream方法来获得一个java.io.OutPutStream对象。发送文本到远程应用,你通常想要创建一个从OutputStream对象返回的java.io.PrintWriter对象。从连接的另一端接收字节流,你可以调用Socket类的 getInputStream方法来返回一个java.io.InputStream对象。
下面的代码创建了一个socket,它可以连接本地 HTTP服务器 (127.0.0.1被分配给本地主机),发送一个HTTP请求和接收来自服务器的响应。它创建了一个StringBuffer对象可以接收响应并把它打印在控制台(console)上。
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() ));
// send an HTTP request to the web server
out.println("GET /index.jsp HTTP/1.1");
out.println("Host: localhost:8080");
out.println("Connection: Close");
out.println();
// read the response
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);
}
// display the response to the out console
System.out.println(sb.toString());
socket.close();
Socket类代表的是一个客户端socket,例如:一个你想要在任何时候连接到一个远程服务器应用的socket。现在,如果你想要事项一个服务器应用,就像HTTP服务器或FTP服务器,你需要一种不同的途径来实现。这是因为你的服务器必须在任何时刻都处于工作(监听)中,而且它不知道什么时候会有客户端应用来连接它。为了是你的应用能够处于时刻地工作(监听状态)中,你需要使用java.net.ServerSocket类。这是一个服务器socket的实现。
ServerSocket是与Socket有区别的。服务器socket的作用是等待客户端的连接请求。一旦服务器socket得到一个连接请求,它就会创建一个Socket实例来处理与客户端之间的通信。
你需要使用ServerSocket类提供的4个构造函数的其中一个来创建一个服务器Socket。你需要明确IP地址和服务器socket将要监听的端口号。典型地,IP地址为127.0.0.1,意味着服务器socket将要监听本地机器。IP地址是服务器socket正在监听的作为绑定地址。作为服务器的另一重要特性是它的backlog。在服务器socket开始拒绝接收客户端到来的请求的最大连接请求队列的长度。
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实例,你就可以通知它在绑定的地址和服务器socket正在监听的端口号等待客户端的链接请求。即调用ServerSocket类的accept方法。这个方法将只在当有一个连接请求和它的返回值是一个Socket类的实例的时返回。这个Socket对象就可以用来发送和接收客户端的字节流。
The Application
我的web服务器应用包括下面3个类:
HttpServer
Request
Response
应用的入口(静态主方法 main )可以再HttpServlet类中找到。主方法创建一个HttpServer的实例然后调用它的等待方法。顾名思义,就是在指定的端口等待HTTP请求、处理请求和发送响应给客户端。一直到它接收到shutdown命名后才停止等待。
这个应用仅仅能发送像HTML文件和图像文件类似的位于某个目录下的静态资源。它也把收到HTTP请求字节流显示在控制台(console)上。不管怎样,它不能发送任何像日期或cookies这样的头信息到浏览器。
下面让我们来看看这3个类
The HttpServer Class
Listing 1.1: The HttpServer class
package ex01.pyrmont;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File;
public class HttpServer {
/** WEB_ROOT is the directory where our HTML and other files reside.
* For this package, WEB_ROOT is the "webroot" directory under the
* working directory. * The working directory is the location in the file system
* from where the java command was invoked.
*/
public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+ "webroot";
// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
// the shutdown command received
private boolean shutdown = false;
public static void main(String[] args) {
HttpServer server = new HttpServer();
server.await();
}
public void await()
{ ... } }
Listing 1.2: The HttpServer class's await method
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 request
while (!shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
// create Request object and parse
Request request = new Request(input);
request.parse();
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
} catch (Exception e) {
e.printStackTrace ();
continue;
}
}
}
web服务器可以找到在public static final WEB_ROOT和它下面所有的子目录下的静态资源。
WEB_ROOT的初始化工作:
Public static final String WEB_ROOT =
System System.getProperty("user.dir") +File.separator + "webroot";
代码中有一个叫做webroot的一个目录。这个目录里面包含一些静态资源你可以使用它们来测试这个应用。
获取静态资源。你可以在浏览器URL中输入:
http://machineName:port/staticResource
如果你发送一个请求道另一台运行这个应用的计算机上,machineName是主机名或IP地址。如果是本机运行,你可以使用localhost在当做machineName使用。Port端口号是8080,staticResource是你请求的文件名,它必须位于WEB_ROOT目录下。
例如:如果你你使用本机来测试应用程序,你想要HttpServer对象发送index.html文件给你,你使用下面的地址:
http://localhost:8080/index.html
停止服务器,你从浏览器端URL地址上发送一个事先预定义好的字符串shutdown命令。这个命令位于host:port部分的后面。
例如:
http://localhost:8080/SHUTDOWN
await的方法是用来代替wait方法的,因为在java.lang.Object中有一个处理线程的wait方法。
await方法在ServerSocket创建好一个实例的时候就进入一个while循环(死循环)。
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
...
// Loop waiting for a request
while (!shutdown) {
...
}
这个while循环在ServerSocket的accept方法返回只有接收到在8080端口的HTTP请求后结束。
socket = serverSocket.accept();
一旦接收到一个请求,await方法就会通过accept方法,从Socket实例获得java.io.InputStream和java.io.OututStream对象。
input = socket.getInputStream();
output = socket.getOutputStream();
然后await方法会创建一个Request对象调用它的parse方法来解析HTTP请求未处理的数据。
// create Request object and parse Request request = new Request(input);
request.parse ();
然后,await方法创建一个Response对象,把Requset对象传给Response。随之调用sendStaticResource方法。
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
最后,await方法关闭Socket和调用Request的getUri方法来检查HTTP请求是否一个shutdown命令。如果是,shutdown变量被设置成为true,程序退出while循环。
// Close the socket
socket.close ();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
The Request
Listing 1.3: The Request class
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;
}
public void parse() {
...
}
private String parseUri(String requestString) {
...
}
public String getUri() {
return uri;
}
}
Listing 1.4: The Request class's parse method
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<i; j++) {
request.append((char) buffer[j]);
}
System.out.print(request.toString());
uri = parseUri(request.toString());
}
Listing 1.5: the Request class's parseUri method
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;
}
parse方法是解析HTTP请求的未处理的的数据。这个方法并没有做太多事情。HTTP请求的URI通过调用private的parseUri方法。ParseUri方法在uri变量里保存了URI。Public的getUri方法被调用用来返回HTTP请求的URI。
The Response Class
Listing 1.6: The Response class
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 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 {
// file not found
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
} catch (Exception e) {
// thrown if cannot instantiate a File object
System.out.println(e.toString() );
} finally {
if (fis!=null)
fis.close();
}
}
}
构造函数接收一个java.io.OutputStream对象:
public Response(OutputStream output) {
this.output = output;
}
HttpServer类的await方法,这个await方法传递了一个从socket里获得的OutputStream对象来创建Resqonse对象。
Response类有2个public方法:setRequest和sendStaticResource方法。setRequest方法用来传递一个Request对象到Response对象中。
SendStaticResource方法用来发送像HTML文件一样的静态资源。java.io.File类通过传递父路径和子路径到文件类构造器来实例化。
File file = new File(HttpServer.WEB_ROOT, request.getUri());
然后,文件是否存在会被检查。如果存在,sendStaticResource通过传递一个File对象来创建一个java.io.FileInputStream对象。之后,调用FileInputStream的read方法把字节数组写入到OutputStream的output。注意在这个例子的静态资源内容是以原始数据发送到浏览器
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);
}
}
如果文件不存在的话,sendStaticResource方法发送错误信息到浏览器。
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
Running the Application
从工作目录下运行这个程序,输入下面:
java ex01.pyrmont.HttpServer
测试程序,打开浏览器输入URL:
http://localhost:8080/index.html
在控制台会得到像下面的HTTP请求信息:
GET /index.html HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
application/vnd.ms-excel, application/msword,
application/vnd.ms-powerpoint, application/x-shockwave-flash, application/pdf, */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Host: localhost:8080
Connection: Keep-Alive
GET /images/logo.gif HTTP/1.1
Accept: */*
Referer: http://localhost:8080/index.html
Accept-Language: en-us Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Host: localhost:8080
Connection: Keep-Alive
第一章 完
这章分析java web servers怎么工作。一个web 服务器也被称作一个超文本传输协议(HTTP)服务器,因为它使用HTTP来与客户端通信,客户端通常是web浏览器。一个基于java的web服务器使用2个重要的类:java.net.Socket和java.net.ServerSocket.它们之间的通信是通过HTTP消息完成的。很自然我们这章由讨论HTTP和这两个类为开始。最后,继续分析这个简单web服务器应用结束本章内容。
The Hypertext Transfer Protocol (HTTP)
HTTP是种允许web服务器和浏览器之间通过Internet互相发送和接收数据的协议。是一种基于请求/相应的协议。客户端向服务器发出请求来请求一个文件,服务器相应地对这个请求作出回应。HTTP使用可靠地TCP连接—其默认为TCP80端口号。第一个HTTP版本号是HTTP/0.9,然后被HTTP/1.0重新修改。代替HTTP/1.0版本的是现在的HTTP/1.1版本,1.1版本被定义为Request for Comments(RFC)2616 。
提示:这包含HTTP 1.1的部分只是简单的帮助你理解web服务器应用传递消息。如果想了解跟多,可以阅读 RFC 2016。
HTTP总是客户端通过建立连接和发送一个HTTP请求来初始化一个事务。Web服务器是不能够去连接一个客户端或者发出一个(callback connection)回调连接给客户端。客户端和服务器都可以提前结束一个连接。例如,当使用一个web浏览器你可以按浏览器上的stop按钮来停止下载一个文件,有效地关闭与web服务器的HTTP连接。
HTTP Requests
一个HTTP请求包含3个组成部分:
方法-统一资源定位符(uri)-协议/版本Method-Uniform ResourceIdentifer-Protocol/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
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
//这行是CRLF
lastName=Franks&firstName=Michael
POST是请求方法,/example/defaul.jsp 是请求的URI HTTP/1.1是协议的和版本部分。
每一个HTTP请求可以使用许多请求方法的一种作为HTTP标准。在HTTP1.1支持7中类型的请求:GET.POST,HEAD,OPTIONS,PUT,DELETE和TRACE。
GET和POST是在Internet应用中使用最普遍的。
URI完整地指定了Intetnet上的资源。一个URI通常被解析为服务器相关的根目录。这样,它总是可以使用一个前斜杠‘/’开始。一个URL(Uniform Resource Locator)本质上是URI的一种类型。
协议版本表示被使用的HTTP的协议版本号。
请求头部包含关于客户端环境和实体正文的有用的信息。例如,它包含浏览器设置的语言,正文的长度等等。每一个头部都被一个回车换行符(CRLF)序列分开。
在头部和实体正文有一个空行(CRLF),这个空行对HTTP求个格式尤为重要。CRLF告诉HTTP服务器正文从哪里开始。在一些Internet编程书籍中介绍这个换行符(CRLF)是HTTP请求的第四组成部分。
在前面的HTTP请求中,实体正文只是简单的一行内容:
lastName=Franks&firstName=Michael
在一个电影的HTTP请求中,实体内容可以很容易地变得更长
HTTP Responses
与HTTP请求类似,一个HTTP 响应也包含3个组成部分:
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
//(CRLF)
<html>
<head>
<title>HTTP Response Example</title>
</head>
<body> Welcome to Brainy Software </body>
</html>
响应头的第一行与请求头的第一行很相似。第一行告诉我们协议是使用的HTTP1.1版本,请求成功(200表示请求成功),所有的请求都完成(ok)。
响应头报班的有用信息和请求头相似。在响应的实体正文是响应自己的HTML内容。头部和正文实体同样被一个(CRLF)序列隔开。
The Socket Class
一个socket是一个网络连接的终端。一个socket运行一个应用在网络上能够读和写。两个软件应用安装在2台不同的计算机上能够通过一个连接发送和接收字节流来相互通信。从你的应用发送一条消息到另一个应用,你需要了解另一个应用的IP地址和socket端口号。在java中,一个socket用java.net.Socket类表示。
你可以使用Socket类提供的诸多构造方法中的一种来创建一个socket。
其中一个构造方法接收一个主机名和端口号:
Public Socket (java.lang.String host , int port)
host是远程机器的名字或ip地址,port是远程应用的端口号。例如:在80端口连接yahoo.com
你可以象下面来构造一个Socket对象:
new Socket(”yahoo.com”,80);
一旦你成功地创建好一个Socket类的实例,你就可以使用它来发送可接收字节流。发送字节流,第一步你必须调用Socket类的 getOutputStream方法来获得一个java.io.OutPutStream对象。发送文本到远程应用,你通常想要创建一个从OutputStream对象返回的java.io.PrintWriter对象。从连接的另一端接收字节流,你可以调用Socket类的 getInputStream方法来返回一个java.io.InputStream对象。
下面的代码创建了一个socket,它可以连接本地 HTTP服务器 (127.0.0.1被分配给本地主机),发送一个HTTP请求和接收来自服务器的响应。它创建了一个StringBuffer对象可以接收响应并把它打印在控制台(console)上。
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() ));
// send an HTTP request to the web server
out.println("GET /index.jsp HTTP/1.1");
out.println("Host: localhost:8080");
out.println("Connection: Close");
out.println();
// read the response
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);
}
// display the response to the out console
System.out.println(sb.toString());
socket.close();
Socket类代表的是一个客户端socket,例如:一个你想要在任何时候连接到一个远程服务器应用的socket。现在,如果你想要事项一个服务器应用,就像HTTP服务器或FTP服务器,你需要一种不同的途径来实现。这是因为你的服务器必须在任何时刻都处于工作(监听)中,而且它不知道什么时候会有客户端应用来连接它。为了是你的应用能够处于时刻地工作(监听状态)中,你需要使用java.net.ServerSocket类。这是一个服务器socket的实现。
ServerSocket是与Socket有区别的。服务器socket的作用是等待客户端的连接请求。一旦服务器socket得到一个连接请求,它就会创建一个Socket实例来处理与客户端之间的通信。
你需要使用ServerSocket类提供的4个构造函数的其中一个来创建一个服务器Socket。你需要明确IP地址和服务器socket将要监听的端口号。典型地,IP地址为127.0.0.1,意味着服务器socket将要监听本地机器。IP地址是服务器socket正在监听的作为绑定地址。作为服务器的另一重要特性是它的backlog。在服务器socket开始拒绝接收客户端到来的请求的最大连接请求队列的长度。
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实例,你就可以通知它在绑定的地址和服务器socket正在监听的端口号等待客户端的链接请求。即调用ServerSocket类的accept方法。这个方法将只在当有一个连接请求和它的返回值是一个Socket类的实例的时返回。这个Socket对象就可以用来发送和接收客户端的字节流。
The Application
我的web服务器应用包括下面3个类:
HttpServer
Request
Response
应用的入口(静态主方法 main )可以再HttpServlet类中找到。主方法创建一个HttpServer的实例然后调用它的等待方法。顾名思义,就是在指定的端口等待HTTP请求、处理请求和发送响应给客户端。一直到它接收到shutdown命名后才停止等待。
这个应用仅仅能发送像HTML文件和图像文件类似的位于某个目录下的静态资源。它也把收到HTTP请求字节流显示在控制台(console)上。不管怎样,它不能发送任何像日期或cookies这样的头信息到浏览器。
下面让我们来看看这3个类
The HttpServer Class
Listing 1.1: The HttpServer class
package ex01.pyrmont;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.File;
public class HttpServer {
/** WEB_ROOT is the directory where our HTML and other files reside.
* For this package, WEB_ROOT is the "webroot" directory under the
* working directory. * The working directory is the location in the file system
* from where the java command was invoked.
*/
public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+ "webroot";
// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
// the shutdown command received
private boolean shutdown = false;
public static void main(String[] args) {
HttpServer server = new HttpServer();
server.await();
}
public void await()
{ ... } }
Listing 1.2: The HttpServer class's await method
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 request
while (!shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
// create Request object and parse
Request request = new Request(input);
request.parse();
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
} catch (Exception e) {
e.printStackTrace ();
continue;
}
}
}
web服务器可以找到在public static final WEB_ROOT和它下面所有的子目录下的静态资源。
WEB_ROOT的初始化工作:
Public static final String WEB_ROOT =
System System.getProperty("user.dir") +File.separator + "webroot";
代码中有一个叫做webroot的一个目录。这个目录里面包含一些静态资源你可以使用它们来测试这个应用。
获取静态资源。你可以在浏览器URL中输入:
http://machineName:port/staticResource
如果你发送一个请求道另一台运行这个应用的计算机上,machineName是主机名或IP地址。如果是本机运行,你可以使用localhost在当做machineName使用。Port端口号是8080,staticResource是你请求的文件名,它必须位于WEB_ROOT目录下。
例如:如果你你使用本机来测试应用程序,你想要HttpServer对象发送index.html文件给你,你使用下面的地址:
http://localhost:8080/index.html
停止服务器,你从浏览器端URL地址上发送一个事先预定义好的字符串shutdown命令。这个命令位于host:port部分的后面。
例如:
http://localhost:8080/SHUTDOWN
await的方法是用来代替wait方法的,因为在java.lang.Object中有一个处理线程的wait方法。
await方法在ServerSocket创建好一个实例的时候就进入一个while循环(死循环)。
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
...
// Loop waiting for a request
while (!shutdown) {
...
}
这个while循环在ServerSocket的accept方法返回只有接收到在8080端口的HTTP请求后结束。
socket = serverSocket.accept();
一旦接收到一个请求,await方法就会通过accept方法,从Socket实例获得java.io.InputStream和java.io.OututStream对象。
input = socket.getInputStream();
output = socket.getOutputStream();
然后await方法会创建一个Request对象调用它的parse方法来解析HTTP请求未处理的数据。
// create Request object and parse Request request = new Request(input);
request.parse ();
然后,await方法创建一个Response对象,把Requset对象传给Response。随之调用sendStaticResource方法。
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
最后,await方法关闭Socket和调用Request的getUri方法来检查HTTP请求是否一个shutdown命令。如果是,shutdown变量被设置成为true,程序退出while循环。
// Close the socket
socket.close ();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
The Request
Listing 1.3: The Request class
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;
}
public void parse() {
...
}
private String parseUri(String requestString) {
...
}
public String getUri() {
return uri;
}
}
Listing 1.4: The Request class's parse method
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<i; j++) {
request.append((char) buffer[j]);
}
System.out.print(request.toString());
uri = parseUri(request.toString());
}
Listing 1.5: the Request class's parseUri method
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;
}
parse方法是解析HTTP请求的未处理的的数据。这个方法并没有做太多事情。HTTP请求的URI通过调用private的parseUri方法。ParseUri方法在uri变量里保存了URI。Public的getUri方法被调用用来返回HTTP请求的URI。
The Response Class
Listing 1.6: The Response class
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 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 {
// file not found
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
} catch (Exception e) {
// thrown if cannot instantiate a File object
System.out.println(e.toString() );
} finally {
if (fis!=null)
fis.close();
}
}
}
构造函数接收一个java.io.OutputStream对象:
public Response(OutputStream output) {
this.output = output;
}
HttpServer类的await方法,这个await方法传递了一个从socket里获得的OutputStream对象来创建Resqonse对象。
Response类有2个public方法:setRequest和sendStaticResource方法。setRequest方法用来传递一个Request对象到Response对象中。
SendStaticResource方法用来发送像HTML文件一样的静态资源。java.io.File类通过传递父路径和子路径到文件类构造器来实例化。
File file = new File(HttpServer.WEB_ROOT, request.getUri());
然后,文件是否存在会被检查。如果存在,sendStaticResource通过传递一个File对象来创建一个java.io.FileInputStream对象。之后,调用FileInputStream的read方法把字节数组写入到OutputStream的output。注意在这个例子的静态资源内容是以原始数据发送到浏览器
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);
}
}
如果文件不存在的话,sendStaticResource方法发送错误信息到浏览器。
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
Running the Application
从工作目录下运行这个程序,输入下面:
java ex01.pyrmont.HttpServer
测试程序,打开浏览器输入URL:
http://localhost:8080/index.html
在控制台会得到像下面的HTTP请求信息:
GET /index.html HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
application/vnd.ms-excel, application/msword,
application/vnd.ms-powerpoint, application/x-shockwave-flash, application/pdf, */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Host: localhost:8080
Connection: Keep-Alive
GET /images/logo.gif HTTP/1.1
Accept: */*
Referer: http://localhost:8080/index.html
Accept-Language: en-us Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Host: localhost:8080
Connection: Keep-Alive
第一章 完
发表评论
-
程序员的乐趣
2012-09-21 15:39 282近来闲的蛋痛,又因为公司里面有日志系统,每天必须写工作日志,少 ... -
2012考研失利 重拾JAVA 准备面试找工作
2012-02-26 23:07 813去年6月辞职考研 今年计算机统考408考起来蛋疼,挂在专业课上 ... -
弱弱的问一下 冰神是谁???
2011-05-10 17:19 18弱弱的问一下 冰神是谁??? -
程序员 爬友 考研
2011-04-26 10:36 409等待我的小球球入住 -
如果我再年轻几岁
2011-02-21 10:22 1155如果我再年轻几岁~(转 ... -
百度 、Google.hk 与 Google.com
2010-12-22 15:24 623今天搜索,发现了了百度、google.hk和google.co ... -
google map v3 GIS功能总结
2010-12-16 11:04 3405在CRM系统中嵌入了地图功能。只要是显示选择客户地 ... -
coder 爱翻译 How Tomcat Works 第二章 第二部分
2010-11-03 21:55 951Application 2 在第一个应用中,有一个严重的问题 ... -
coder 爱翻译 How Tomcat Works 第二章 第一部分
2010-11-03 15:41 1212第二章: A Simple Servlet Con ... -
飞车党 come on
2010-11-02 12:31 382哎,昏昏沉沉的我,今天居然会在上班路上被飞车小毛贼给掏包了。 ... -
ExtJs 21种皮肤打包下载
2010-10-13 13:40 1696直接上rar包 -
我想大声告诉你
2010-10-12 23:27 333夜深了 我还为你不能睡 黎明前的心情 最深的灰 左右为难的你 ... -
json-lib 入门
2010-10-12 13:16 10091)JSON简介 2)JSON/LIST转换 3)JSON/M ... -
B/S结构浏览器兼容问题(一)获知是何种浏览器
2010-10-11 20:37 1299var Sys = {}; var ua = navigat ... -
祝福老一辈
2010-10-04 22:46 885以前的老莫看上去一脸严肃的表情,如今看来是一脸的慈祥。 老莫问 ... -
工作月总结
2010-09-14 15:36 888从找到工作到现在已经一个月时间了 公司很小,研发人员就2个。是 ... -
再看马士兵
2010-09-10 08:26 1020昨晚再次翻出一年半以前下的马士兵的网络部分的视频。 发现以前很 ... -
生日快乐
2010-09-08 12:36 239简简单单 生日快乐
相关推荐
适合读者 1.jsp/servlet 开发人员,想了解 tomcat 内部机制的 coder; 2.想加入 tomcat 开发团队的 coder; 3.web 开发人员,但对软件开发很有兴趣的 coder; 4.想要对 tomcat 进行定制的 coder。
Bad Programming Practices 101 Become a Better Coder by Learning How (Not) to Program 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息请在美国亚马逊官网搜索此书
- **插件扩展**:PHPCoder支持第三方插件,可以通过社区下载安装增强功能。 - **代码格式化**:使用“编辑”>“格式化代码”对代码进行整理,保持良好的编码风格。 - **搜索与替换**:利用查找和替换功能,可以在...
6. 插件扩展:MediaCoder有一个开放的插件架构,用户可以通过安装第三方插件来增加新的编码解码器、滤镜和其他功能,扩展其应用范围。 7. 用户友好:MediaCoder的界面简洁直观,即便是对编码不熟悉的用户也能快速...
mediacoder 5685专业版,无普通版的限制
Embedded Coder用于产生嵌入式处理器、目标快速原型板和大规模生产中使用的微处理器的可读的、紧凑的、快速的C和C++代码。Embedded Coder支持附加的MATLAB Coder™和Simulink Coder™配置选项,以及对生成代码的功能...
Mediacoder是一款强大的多媒体编码工具,专为音频和视频转换而设计,支持多种格式,如MP4、AVI、MKV等。本教程将深入讲解如何利用Mediacoder进行视频压制,优化视频质量,同时合理权衡码率与视频大小的关系。 **1. ...
#### 一、Simulink HDL Coder 简介 Simulink HDL Coder 是 MathWorks 提供的一款强大的工具,它允许用户将 Simulink 模型转换为硬件描述语言(HDL)代码,进而用于 FPGA 和 ASIC 的设计实现。此工具旨在帮助工程师...
Martin, "The Clean Coder: A Code of Conduct for Professional Programmers" Prentice Hall | 2011 | ISBN: 0137081073 | 256 pages | PDF | 6 MB Programmers who endure and succeed amidst swirling ...
本文介绍了使用HDL Coder生成HDL代码的基本流程和技术要点,包括生成代码前的准备工作、不同环境下的代码生成方法以及一个实践案例。通过对这些知识点的学习,读者可以更好地理解和掌握HDL Coder的应用技巧,从而...
综上所述,CoolCoder类生成工具是现代开发环境下的一款实用工具,它通过自动化的方式帮助开发者快速生成实体类和映射文件,减轻了开发负担,提高了开发质量。对于那些使用NHibernate框架的项目,它更是不可或缺的...
MediaCoder行业版一款针对VOD及KTV视频点播行业开发的,用于转换和处理带有多音轨内容的视频节目的软件。它具备业界领先的视频编码引擎,在高性能转码的同时保持高画质,并通过丰富的视频滤镜增强画面视觉效果。作为...
MediaCoder是一款功能强大的多媒体转换工具,它支持广泛的音频和视频编码格式,使用户能够轻松地在不同设备之间转换媒体文件。这款软件适用于个人用户、专业音频和视频制作人员,以及那些希望在各种设备上享受多媒体...
PHPCoder用于快速开发和调试PHP应用程序,它很容易扩展和定制,完全能够符合开发者的个性要求.PHPCoder是一个非常实用的,功能强大的编程环境,而且它是免费的!
Matlab Coder是Mathworks公司推出的一款用于将Matlab代码转换成高效C代码的工具。从2004年开始,Matlab陆续在Simulink中添加了Embeded Matlab Function模块,2007年在Real-Time Workshop中引入了emlc函数(现在的...
MATLAB Coder 是一款能够将 MATLAB 代码转换成独立的 C 或 C++ 代码的强大工具。这一过程对于那些希望在非 MATLAB 环境下部署 MATLAB 代码的应用开发者来说至关重要。通过将 MATLAB 代码转换为 C/C++ 代码,不仅可以...
MediaCoder-Premium-x64 MediaCoder是最早开始使用GPU进行视频编码加速的影音转码软件之一。通过将原本完全依赖CPU的计算工作转移到GPU上进行,H.264和H.265编码所需的时间被大幅缩短。
texasinstrumentsc2000.mlpkginstall 支持TI的C2000系列工具包,要求MATLAB R2017a及其以上版本。 安装方法:打开matlab,调整路径到mlpkginstall文件所在目录;在current folder窗口里双击mlpkginstall文件即可开始...
开源的AI自动生成SQL语句源代码,这款SQLCoder-70B-Alpha在文本到SQL的转换能力上超越了包括GPT-4在内的所有通用模型,它能更准确地理解你的需求,并生成相应的SQL查询。SQLCoder2和SQLCoder-7B模型已经向公众开放,...