友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第177章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




器,客户必须确切知道自己把数据报发到哪个地址。尽管有一个端口号,但没有为它分配因特网地址,因为 

它就驻留在“这”台机器内,所以知道自己的因特网地址是什么(目前是默认的 localhost)。在无限while 

循环中,套接字被告知接收数据(receive())。然后暂时挂起,直到一个数据报出现,再把它反馈回我们希 

望的接收人——DatagramPacket dp——里面。数据包(Packet)会被转换成一个字串,同时插入的还有数据 

包的起源因特网地址及套接字。这些信息会显示出来,然后添加一个额外的字串,指出自己已从服务器反馈 

回来了。  

大家可能会觉得有点儿迷惑。正如大家会看到的那样,许多不同的因特网地址和端口号都可能是消息的起源 



                                                                                          549 


…………………………………………………………Page 551……………………………………………………………

地——换言之,客户程序可能驻留在任何一台机器里(就这一次演示来说,它们都驻留在 localhost 里,但 

每个客户使用的端口编号是不同的)。为了将一条消息送回它真正的始发客户,需要知道那个客户的因特网 

地址以及端口号。幸运的是,所有这些资料均已非常周到地封装到发出消息的DatagramPacket 内部,所以我 

们要做的全部事情就是用getAddress()和getPort()把它们取出来。利用这些资料,可以构建 

DatagramPacket echo——它通过与接收用的相同的套接字发送回来。除此以外,一旦套接字发出数据报,就 

会添加“这”台机器的因特网地址及端口信息,所以当客户接收消息时,它可以利用 getAddress()和 

getPort()了解数据报来自何处。事实上,getAddress()和 getPort()唯一不能告诉我们数据报来自何处的前 

提是:我们创建一个待发送的数据报,并在正式发出之前调用了getAddress()和getPort()。到数据报正式 

发送的时候,这台机器的地址以及端口才会写入数据报。所以我们得到了运用数据报时一项重要的原则:不 

必跟踪一条消息的来源地!因为它肯定保存在数据报里。事实上,对程序来说,最可靠的做法是我们不要试 

图跟踪,而是无论如何都从目标数据报里提取出地址以及端口信息(就象这里做的那样)。  

为测试服务器的运转是否正常,下面这程序将创建大量客户(线程),它们都会将数据报包发给服务器,并 

等候服务器把它们原样反馈回来。  

  

//: ChatterClient。java  

// Tests the ChatterServer by starting multiple   

// clients; each of which sends datagrams。  

import java。lang。Thread;  

import java。*;  

import java。io。*;  

  

public class ChatterClient extends Thread {  

  // Can listen & send on the same socket:  

  private DatagramSocket s;  

  private InetAddress hostAddress;  

  private byte'' buf = new byte'1000';  

  private DatagramPacket dp =   

    new DatagramPacket(buf; buf。length);  

  private int id;  

  

  public ChatterClient(int identifier) {  

    id = identifier;  

    try {  

      // Auto…assign port number:  

      s = new DatagramSocket();  

      hostAddress =   

        InetAddress。getByName(〃localhost〃);  

    } catch(UnknownHostException e) {  

      System。err。println(〃Cannot find host〃);  

      System。exit(1);  

    } catch(SocketException e) {  

      System。err。println(〃Can't open socket〃);  

      e。printStackTrace();  

      System。exit(1);  

    }   

    System。out。println(〃ChatterClient starting〃);  

  }  

  public void run() {  

    try {  

      for(int i = 0; i 《 25; i++) {  

        String outMessage = 〃Client #〃 +  

          id + 〃; message #〃 + i;  



                                                                                          550 


…………………………………………………………Page 552……………………………………………………………

        // Make and send a datagram:  

        s。send(Dgram。toDatagram(outMessage;  

          hostAddress;   

          ChatterServer。INPORT));  

        // Block until it echoes back:  

        s。receive(dp);  

        // Print out the echoed contents:  

        String rcvd = 〃Client #〃 + id +  

          〃; rcvd from 〃 +   

          dp。getAddress() + 〃; 〃 +   

          dp。getPort() + 〃: 〃 +  

          Dgram。toString(dp);  

        System。out。println(rcvd);  

      }  

    } catch(IOException e) {  

      e。printStackTrace();  

      System。exit(1);  

    }  

  }  

  public static void main(String'' args) {  

    for(int i = 0; i 《 10; i++)  

      new ChatterClient(i)。start();  

  }  

} ///:~  

  

ChatterClient 被创建成一个线程(Thread),所以可以用多个客户来“骚扰”服务器。从中可以看到,用 

于接收的DatagramPacket 和用于 ChatterServer 的那个是相似的。在构建器中,创建DatagramPacket 时没 

有附带任何参数(自变量),因为它不需要明确指出自己位于哪个特定编号的端口里。用于这个套接字的因 

特网地址将成为“这台机器”(比如 localhost),而且会自动分配端口编号,这从输出结果即可看出。同 

用于服务器的那个一样,这个 DatagramPacket 将同时用于发送和接收。  

hostAddress 是我们想与之通信的那台机器的因特网地址。在程序中,如果需要创建一个准备传出去的 

DatagramPacket,那么必须知道一个准确的因特网地址和端口号。可以肯定的是,主机必须位于一个已知的 

地址和端口号上,使客户能启动与主机的“会话”。  

每个线程都有自己独一无二的标识号(尽管自动分配给线程的端口号是也会提供一个唯一的标识符)。在 

run()中,我们创建了一个String 消息,其中包含了线程的标识编号以及该线程准备发送的消息编号。我们 

用这个字串创建一个数据报,发到主机上的指定地址;端口编号则直接从 ChatterServer 内的一个常数取 

得。一旦消息发出,receive()就会暂时被“堵塞”起来,直到服务器回复了这条消息。与消息附在一起的所 

有信息使我们知道回到这个特定线程的东西正是从始发消息中投递出去的。在这个例子中,尽管是一种“不 

可靠”协议,但仍然能够检查数据报是否到去过了它们该去的地方(这在 localhost 和LAN 环境中是成立 

的,但在非本地连接中却可能出现一些错误)。  

运行该程序时,大家会发现每个线程都会结束。这意味着发送到服务器的每个数据报包都会回转,并反馈回 

正确的接收者。如果不是这样,一个或更多的线程就会挂起并进入“堵塞”状态,直到它们的输入被显露出 

来。  

大家或许认为将文件从一台机器传到另一台的唯一正确方式是通过TCP 套接字,因为它们是“可靠”的。然 

而,由于数据报的速度非常快,所以它才是一种更好的选择。我们只需将文件分割成多个数据报,并为每个 

包编号。接收机器会取得这些数据包,并重新“组装”它们;一个“标题包”会告诉机器应该接收多少个 

包,以及组装所需的另一些重要信息。如果一个包在半路“走丢”了,接收机器会返回一个数据报,告诉发 

送者重传。  



15。5 一个 Web 应用  



现在让我们想想如何创建一个应用,令其在真实的 Web 环境中运行,它将把Java 的优势表现得淋漓尽致。这 

个应用的一部分是在Web 服务器上运行的一个 Java 程序,另一部分则是一个“程序片”或“小应用程序” 



                                                                                  551 


…………………………………………………………Page 553……………………………………………………………

 (Applet),从服务器下载至浏览器(即“客户”)。这个程序片从用户那里收集信息,并将其传回Web 服 

务器上运行的应用程序。程序的任务非常简单:程序片会询问用户的 E…mail 地址,并在验证这个地址合格后 

 (没有包含空格,而且有一个@符号),将该 E…mail 发送给 Web 服务器。服务器上运行的程序则会捕获传回 

的数据,检查一个包含了所有E…mail 地址的数据文件。如果那个地址已包含在文件里,则向浏览器反馈一条 

消息,说明这一情况。该消息由程序片负责显示。若是一个新地址,则将其置入列表,并通知程序片已成功 

添加了电子函件地址。  

若采用传统方式来解决这个问题,我们要创建一个包含了文本字段及一个“提交”(Submit)按钮的HTML 

页。用户可在文本字段里键入自己喜欢的任何内容,并毫无阻碍地提交给服务器(在客户端不进行任何检 

查)。提交数据的同时,Web 页也会告诉服务器应对数据采取什么样的操作——知会“通用网关接口” 

 (CGI)程序,收到这些数据后立即运行服务器。这种CGI 程序通常是用 Perl 或C 写的(有时也用C++,但 

要求服务器支持),而且必须能控制一切可能出现的情况。它首先会检查数据,判断是否采用了正确的格 

式。若答案是否定的,则CGI 程序必须创建一个 HTML 页,对遇到的问题进行描述。这个页会转交给服务器, 

再由服务器反馈回用户。用户看到出错提示后,必须再试一遍提交,直到通过为止。若数据正确,CGI 程序 

会打开数据文件,要么把电子函件地址加入文件,要么指出该地址已在数据文件里了。无论哪种情况,都必 

须格式化一个恰当的HTML 页,以便服务器返回给用户。  

作为Java 程序员,上述解决问题的方法显得非常笨拙。而且很自然地,我们希望一切工作都用Java 完成。 

首先,我们会用一个Java 程序片负责客户端的数据有效性校验,避免数据在服务器和客户之间传来传去,浪 

费时间和带宽,同时减轻服务器额外构建HTML 页的负担。然后跳过Perl CGI 脚本,换成在服务器上运行一 

个Java 应用。事实上,我们在这儿已完全跳过了 Web 服务器,仅仅
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!