摘要:WWW的工作基于客户机/服务器计算模型,由Web 浏览器(客户机)和Web服务器(服务器)构成,两者之间采用超文本传送协议(HTTP)进行通信,HTTP协议的作用原理包括四个步骤:连接,请求,应答。根据上述HTTP协议的作用原理,本文实现了GET请求的Web服务器程序的方法,通过创建ServerSocket类对象,监听端口8080;等待、接受客户机连接到端口8080;创建与socket字相关联的输入流和输出流;然后,读取客户机的请求信息,若请求类型是GET,则从请求信息中获取所访问的HTML文件名,如果 HTML文件存在,则打开HTML文件,把HTTP头信息和HTML文件内容通过socket传回给Web浏览器,然后关闭文件。否则发送错误信息给 Web浏览器。最后,关闭与相应Web浏览器连接的socket字。
一、HTTP协议的作用原理
WWW是以Internet作为传输媒介的一个应用系统,WWW网上最基本的传输单位是Web网页。WWW的工作基于客户机/服务器计算模型,由Web 浏览器(客户机)和Web服务器(服务器)构成,两者之间采用超文本传送协议(HTTP)进行通信。HTTP协议是基于TCP/IP协议之上的协议,是 Web浏览器和Web服务器之间的应用层协议,是通用的、无状态的、面向对象的协议。HTTP协议的作用原理包括四个步骤:
(1) 连接:Web浏览器与Web服务器建立连接,打开一个称为socket(套接字)的虚拟文件,此文件的建立标志着连接建立成功。
(2) 请求:Web浏览器通过socket向Web服务器提交请求。HTTP的请求一般是GET或POST命令(POST用于FORM参数的传递)。GET命令的格式为:
GET 路径/文件名 HTTP/1.0
文件名指出所访问的文件,HTTP/1.0指出Web浏览器使用的HTTP版本。
(3) 应答:Web浏览器提交请求后,通过HTTP协议传送给Web服务器。Web服务器接到后,进行事务处理,处理结果又通过HTTP传回给Web浏览器,从而在Web浏览器上显示出所请求的页面。
例:假设客户机与www.mycompany.com:8080/mydir/index.html建立了连接,就会发送GET命令:GET /mydir/index.html HTTP/1.0。主机名为www.mycompany.com的Web服务器从它的文档空间中搜索子目录mydir的文件index.html。如果找到该文件,Web服务器把该文件内容传送给相应的Web浏览器。
为了告知 Web浏览器传送内容的类型,Web服务器首先传送一些HTTP头信息,然后传送具体内容(即HTTP体信息),HTTP头信息和HTTP体信息之间用一个空行分开。
常用的HTTP头信息有:
① HTTP 1.0 200 OK
这是Web服务器应答的第一行,列出服务器正在运行的HTTP版本号和应答代码。代码“200 OK”表示请求完成。
② MIME_Version:1.0
它指示MIME类型的版本。
③ content_type:类型
这个头信息非常重要,它指示HTTP体信息的MIME类型。如:content_type:text/html指示传送的数据是HTML文档。
④ content_length:长度值
它指示HTTP体信息的长度(字节)。
(4) 关闭连接:当应答结束后,Web浏览器与Web服务器必须断开,以保证其它Web浏览器能够与Web服务器建立连接。
二、Java实现Web服务器功能的程序设计
根据上述HTTP协议的作用原理,实现GET请求的Web服务器程序的方法如下:
(1) 创建ServerSocket类对象,监听端口8080。这是为了区别于HTTP的标准TCP/IP端口80而取的;
(2) 等待、接受客户机连接到端口8080,得到与客户机连接的socket;
(3) 创建与socket字相关联的输入流instream和输出流outstream;
(4) 从与socket关联的输入流instream中读取一行客户机提交的请求信息,请求信息的格式为:GET 路径/文件名 HTTP/1.0
(5) 从请求信息中获取请求类型。如果请求类型是GET,则从请求信息中获取所访问的HTML文件名。没有HTML文件名时,则以index.html作为文件名;
(6) 如果HTML文件存在,则打开HTML文件,把HTTP头信息和HTML文件内容通过socket传回给Web浏览器,然后关闭文件。否则发送错误信息给Web浏览器;
(7) 关闭与相应Web浏览器连接的socket字。
下面的程序是根据上述方法编写的、可实现多线程的Web服务器,以保证多个客户机能同时与该Web服务器连接。
程序1:WebServer.java文件
// WebServer.java 用JAVA编写Web服务器
import java.io.*;
import java.net.*;
public class WebServer {
public static void main(String args[]) {
int i=1, PORT=8080;
ServerSocket server=null;
Socket client=null;
try {
server=new ServerSocket(PORT);
System.out.println("Web Server is listening on port "+server.getLocalPort());
for (;;) {
client=server.accept(); // 接受客户机的连接请求
new ConnectionThread(client,i).start();
i++;
}
} catch (Exception e) {System.out.println(e);}
}
}
/* ConnnectionThread类完成与一个Web浏览器的通信 */
class ConnectionThread extends Thread {
Socket client; // 连接Web浏览器的socket字
int counter; // 计数器
public ConnectionThread(Socket cl,int c) {
client=cl;
counter=c;
}
public void run() // 线程体
{
try {
String destIP=client.getInetAddress().toString(); // 客户机IP地址
int destport=client.getPort(); // 客户机端口号
System.out.println("Connection "+counter+":connected to "+destIP+" on port "+destport+".");
PrintStream outstream=new PrintStream(client.getOutputStream());
DataInputStream instream=new DataInputStream(client.getInputStream());
String inline=instream.readLine(); // 读取Web浏览器提交的请求信息
System.out.println("Received:"+inline);
if (getrequest(inline)) { // 如果是GET请求
String filename=getfilename(inline);
File file=new File(filename);
if (file.exists()) { // 若文件存在,则将文件送给Web浏览器
System.out.println(filename+" requested.");
outstream.println("HTTP/1.0 200 OK");
outstream.println("MIME_version:1.0");
outstream.println("Content_Type:text/html");
int len=(int)file.length();
outstream.println("Content_Length:"+len);
outstream.println("");
sendfile(outstream,file); // 发送文件
outstream.flush();
} else { // 文件不存在时
String notfound="<html><head><title>Not Found</title></head>
<body><h1>Error 404-file not found</h1></body></html>";
outstream.println("HTTP/1.0 404 no found");
outstream.println("Content_Type:text/html");
outstream.println("Content_Length:"+notfound.length()+2);
outstream.println("");
outstream.println(notfound);
outstream.flush();
}
}
long m1=1;
while (m1<11100000) {m1++;} // 延时
client.close();
} catch (IOException e) {
System.out.println("Exception:"+e);
}
}
/* 获取请求类型是否为“GET” */
boolean getrequest(String s) {
if (s.length()>0)
{
if (s.substring(0,3).equalsIgnoreCase("GET")) return true;
}
return false;
}
/* 获取要访问的文件名 */
String getfilename(String s) {
String f=s.substring(s.indexOf(' ')+1);
f=f.substring(0,f.indexOf(' '));
try {
if (f.charAt(0)=='/')
f=f.substring(1);
} catch (StringIndexOutOfBoundsException e) {
System.out.println("Exception:"+e);
}
if (f.equals("")) f="index.html";
return f;
}
/*把指定文件发送给Web浏览器 */
void sendfile(PrintStream outs,File file) {
try {
DataInputStream in=new DataInputStream(new FileInputStream(file));
int len=(int)file.length();
byte buf[]=new byte[len];
in.readFully(buf);
outs.write(buf,0,len);
outs.flush();
in.close();
} catch (Exception e) {
System.out.println("Error retrieving file.");
System.exit(1);
}
}
}
程序中的ConnectionThread线程子类用来分析一个Web浏览器提交的请求,并将应答信息传回给Web浏览器。其中, getrequest()方法用来检测客户的请求是否为“GET”;getfilename(s)方法是从客户请求信息s中获取要访问的HTML文件名; sendfile()方法把指定文件内容通过socket传回给Web浏览器。
对上述程序的getrequest()方法和相关部分作修改,也能对POST请求进行处理。
三、运行实例
为了测试上述程序的正确性,将编译后的WebServer.class、ConnectionThread.class和下面的index.html文件置于网络的某台主机的同一目录中(如:主机NT40SRV的C:\JWEB目录)。
程序2:index.html文件
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" content="text/html; charset=gb_2312-80">
<TITLE>Java Web服务器</TITLE>
</HEAD>
<BODY>
<h3>这是用JAVA写出的WEB服务器主页</h3>
1998年8月28日
<hr>
</BODY>
</HTML>
首先在该主机上用java命令运行WebServer.class:
C:\jweb>java webserver
然后在客户机运行浏览器软件,在URL处输入WebServer程序所属的URL地址(如:http://nt40srv:8080/index.html),就在浏览器窗口显示出指定的HTML文档。
注意,不能缺省端口号8080,如缺省,则运行该主机的正常WEB服务器。
说明,不具备网络条件的可在安装了Windows 95的单机上进行测试,方法是用localhost或127.0.0.1代替URL地址的域名部分,即URL地址为http://localhost:8080。
系统购建在一个Server上,客户端是在手机上通过HTTP访问服务器上的数据,用户的个性数据通过加密的方式保存在RMS中,服务器将用户访问的数据进行处理,使传输的数据最少。同时服务器上也保存了用户的个性数据,以及一些配置信息,使用户请求的时候仅仅通过简单的数字参数请求就可以完成操作。
系统购建在一个Server上,客户端是在手机上通过HTTP访问服务器上的数据,用户的个性数据通过加密的方式保存在RMS中,服务器将用户访问的数据进行处理,使传输的数据最少。同时服务器上也保存了用户的个性数据,以及一些配置信息,使用户请求的时候仅仅通过简单的数字参数请求就可以完成操作。
1. 连接设置
首先是考虑客户端与服务器如何进行通信。CLDC 提供了通用的连接框架,我们可以用来从 Web 发送和检索数据。此外,MIDP 提供了 HttpConnection 接口,而 MIDP 规范要求实现至少支持 HTTP 1.1 协议。因此,我们可以使用 HttpConnection 查询 Web 服务器,获得我们需要的数据。
HTTP 是一种“请求-响应”协议,在该协议中,必须在发送请求之前设置请求参数。
整个连接和获得数据的代码如下:
HttpConnection http = null;
InputStream is = null;
boolean ret = false;
try{
http = (HttpConnection) Connector.open(URL);
http.setRequestMethod(HttpConnection.GET);
if(hasListener)
dl.setProgress(1, 10);
is = http.openInputStream();
if(hasListener)
dl.setProgress(2, 10);
String str;
int length = (int) http.getLength();
if(hasListener)
dl.setProgress(3, 10);
if(length != -1) { // Length 有效.
byte data[] = new byte[length];
is.read(data);
str = new String(data);
}else{ // Length无效
ByteArrayOutputStream bs = new ByteArrayOutputStream();
int ch;
while((ch = is.read()) != -1)
bs.write(ch);
str = new String(bs.toByteArray());
bs.close();
}
//其他操作
if(hasListener)//完成HTTP请求
dl.setProgress(10, 10);
} catch (IOException e) {
//异常打出
} finally {
//关闭连接等操作
if(is != null)
is.close();
if(http != null)
http.close();
if(dl != null)
dl.onFinish();
}
连接应处于三种状态之一:
- Setting state:在这个状态中,还未建立到服务器的连接。http = (HttpConnection) Connector.open(URL) 只是创建了一个尚未连接的 HttpConnection。
- Connectioned state:在设置了所有必需的头之后,调用 http.openInputStream() 会产生服务器连接。发送请求参数,并等待响应。应用程序下载后,连接将转入下一个状态。
- Closed state:连接已被关闭,并且如果调用了方法,则方法将会抛出 IOException。在我们的示例中,在退出定制的下载方法之前我们关闭了所有数据流和连接。
2. 服务器设置
系统采用客户机/服务器的体系结构,我们先讨论服务器和连接的问题,是因为服务器端的设置我们比较熟悉,因此我们首先讨论这两部分。
服务器端计算的一些优点是:
- 强大的处理器能力 ,目前我们使用的处理器基本上都是2G以上的。
- 高速连接 ,高速网卡提供100M甚至1000M的接入带宽。
- 灵活的计算选择(语言、平台等) ,服务器端的开发可以采用你所熟悉的,高效的变成语言。
服务器端程序可以完成的工作:
- 将繁重的计算部分转移到服务器端
- 在服务器端进行浮点运算
- 在服务器端预处理信息以提高性能
- 克服其它 J2ME 限制,如创建图象
- 降低升级成本:服务器端上的一次升级 vs. 对客户机端上的每个软件套件的升级
服务器端可以为我们提供足够的信息处理,将信息进行精简,减少客户端的处理压力。
3. 客户端基本开发
无论怎样还是要说一下MIDlet,这是J2ME程序运行的关键程序主题。
J2ME 平台由几层组成,最上层是 MIDP。我们在 MIDP 上面开发 J2ME 应用程序;因此,将应用程序命名为 MIDlet。每个 J2ME 应用程序都必须继承 MIDlet 类,以便应用程序管理软件可以控制它。
以下是我们 MIDlet 的蓝图:
public class MyMIDlet extends MIDlet implements CommandListener
{
Display display;
private List menu;
private Command commandSelect;
private Command commandExit;
...
public MyMIDlet () { // The constructor.
...
// 数据初始化
// 从RMS中读取数据.
//创建UI的第一个界面.
}
public void startApp() { // Enter the active state.
// Display the first screen.
display.setCurrent(menu);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
...
// Clean up data streams, network, etc.
...
}
public void commandAction(Command c, Displayable s) {
notifyDestroyed();
}
// Other customized methods.
...
}
当应用程序管理软件调用 startApp()、pauseApp() 或 destroyApp() 方法时,MIDlet 的状态将发生改变。例如,在调用 pauseApp() 时,MIDlet 从活动状态变为暂停状态。
因为那些方法会通知状态的改变,所以它们必需是轻量级的以便迅速返回。正如您在上面代码清单中看到的,我们将大多数初始化进程放在 <init> 和构造器中,而不是放在 startApp() 中。
4. 高级用户界面设计
GUI API 是在 MIDP 而不是在 CLDC 中定义的。高级用户界面 API (如 Alert、Form 以及象 Command 这样的独有组件)以及低级用户界面 API(如 Canvas)。
Screen 是所有高级界面 API 的超类。
5. 低级用户界面
低级用户界面主要是在Canvas上进行绘图。
第四第五个将在接下来进行详细的描述。跟别进行。
1003

被折叠的 条评论
为什么被折叠?



