66.JAVA编程思想——网络编程

66.JAVA编程思想——网络编程

历史上的网络编程都倾向于困难、复杂,而且极易出错。

程序员必须掌握与网络有关的大量细节,有时甚至要对硬件有深刻的认识。一般地,我们需要理解连网协议中不同的“层”(Layer)。而且对于每个连网库,一般都包含了数量众多的函数,分别涉及信息块的连接、打包和拆包;这些块的来回运输;以及握手等等。这是一项令人痛苦的工作。但是,连网本身的概念并不是很难。我们想获得位于其他地方某台机器上的信息,并把它们移到这儿;或者相反。这与读写文件非常相似,只是文件存在于远程机器上,而且远程机器有权决定如何处理我们请求或者发送的数据。

Java 最出色的一个地方就是它的“无痛苦连网”概念。有关连网的基层细节已被尽可能地提取出去,并隐藏在JVM 以及Java 的本机安装系统里进行控制。我们使用的编程模型是一个文件的模型;事实上,网络连接(一个“套接字”)已被封装到系统对象里,所以可象对其他数据流那样采用同样的方法调用。除此以外,在我们处理另一个连网问题——同时控制多个网络连接——的时候,Java 内建的多线程机制也是十分方便的。

1     机器的标识

为了分辨来自别处的一台机器,以及为了保证自己连接的是希望的那台机器,必须有一种机制能独一无二地标识出网络内的每台机器。早期网络只解决了如何在本地网络环境中为机器提供唯一的名字。但Java面向的是整个因特网,这要求用一种机制对来自世界各地的机器进行标识。为达到这个目的,我们采用了IP(互联网地址)的概念。IP 以两种形式存在着:

(1) 大家最熟悉的DNS(域名服务)形式。

(2) 此外,亦可采用“四点”格式,亦即由点号(.)分隔的四组数字,比如202.98.32.111。

不管哪种情况,IP 地址在内部都表达成一个由32 个二进制位(bit)构成的数字,所以IP 地址

的每一组数字都不能超过255。利用由java.net 提供的static InetAddress.getByName(),我们可以让一个特定的Java 对象表达上述任何一种形式的数字。结果是类型为InetAddress 的一个对象,可用它构成一个“套接字”(Socket),大家在后面会见到这一点。

IPV4最多只能得到40 亿左右的数字组合,全世界的人很快就会把它用光。但根据目前正在研究的新IP 编址方案,它将采用128 bit 的数字,这样得到的唯一性IP 地址也许在几百年的时间里都不会用完。

作为运用InetAddress.getByName()一个简单的例子,请考虑假设自己有一家拨号连接因特网服务提供者(ISP),那么会发生什么情况。每次拨号连接的时候,都会分配得到一个临时IP 地址。但在连接期间,那个IP 地址拥有与因特网上其他IP 地址一样的有效性。如果有人按照你的IP 地址连接你的机器,他们就有可能使用在你机器上运行的Web 或者FTP 服务器程序。当然这有个前提,对方必须准确地知道你目前分配到的IP。由于每次拨号连接获得的IP 都是随机的,怎样才能准确地掌握你的IP 呢?

1.1     代码

import java.net.*;

 

public class WhoAmI {

    public staticvoidmain(String[] args)throwsException {

        if (args.length!= 1) {

            System.err.println("Usage: WhoAmI MachineName");

            System.exit(1);

        }

        InetAddress a = InetAddress.getByName(args[0]);

        System.out.println(a);

    }

} /// :~

需要输入机器的主机名作为参数。

2     服务器和客户机

网络最基本的精神就是让两台机器连接到一起,并相互“交谈”或者“沟通”。一旦两台机器都发现了对方,就可以展开一次令人愉快的双向对话。但它们怎样才能“发现”对方呢?这就象在游乐园里那样:一台机器不得不停留在一个地方,侦听其他机器说:“嘿,你在哪里呢?”

“停留在一个地方”的机器叫作“服务器”(Server);到处“找人”的机器则叫作“客户机”(Client)或者“客户”。它们之间的区别只有在客户机试图同服务器连接的时候才显得非常明显。一旦连通,就变成了一种双向通信,谁来扮演服务器或者客户机便显得不那么重要了。

所以服务器的主要任务是侦听建立连接的请求,这是由我们创建的特定服务器对象完成的。而客户机的任务是试着与一台服务器建立连接,这是由我们创建的特定客户机对象完成的。一旦连接建好,那么无论在服务器端还是客户机端,连接只是魔术般地变成了一个IO 数据流对象。从这时开始,我们可以象读写一个普通的文件那样对待连接。所以一旦建好连接,我们只需象使用自己熟悉的IO 命令即可。这正是Java连网最方便的一个地方。

2.1     在没有网络的前提下测试程序

由于多种潜在的原因,我们可能没有一台客户机、服务器以及一个网络来测试自己做好的程序。IP 的设计者注意到了这个问题,并建立了一个特殊的地址——localhost——来满足非网络环境中的测试要求。在Java 中产生这个地址最一般的做法是:

InetAddress addr =InetAddress.getByName(null);

如果向getByName()传递一个null(空)值,就默认为使用localhost。我们用InetAddress 对特定的机器进行索引,而且必须在进行进一步的操作之前得到这个InetAddress(互联网地址)。我们不可以操纵一个InetAddress 的内容(但可把它打印出来,就象下一个例子要演示的那样)。创建InetAddress的唯一途径就是那个类的static(静态)成员方法getByName()(这是最常用的)、getAllByName()或者getLocalHost()。

为得到本地主机地址,亦可向其直接传递字串"localhost":

InetAddress.getByName("localhost");

或者使用它的保留IP 地址(四点形式),就象下面这样:

InetAddress.getByName("127.0.0.1");

这三种方法得到的结果是一样的。

2.2     端口

有些时候,一个IP 地址并不足以完整标识一个服务器。这是由于在一台物理性的机器中,往往运行着多个服务器(程序)。由IP 表达的每台机器也包含了“端口”(Port )。我们设置一个客户机或者服务器的时候,必须选择一个无论客户机还是服务器都认可连接的端口。就象我们去拜会某人时,IP 地址是他居住的房子,而端口是他在的那个房间。

注意端口并不是机器上一个物理上存在的场所,而是一种软件抽象(主要是为了表述的方便)。客户程序知道如何通过机器的IP 地址同它连接,但怎样才能同自己真正需要的那种服务连接呢(一般每个端口都运行着一种服务,一台机器可能提供了多种服务,比如HTTP 和FTP 等等)?端口编号在这里扮演了重要的角色,它是必需的一种二级定址措施。也就是说,我们请求一个特定的端口,便相当于请求与那个端口编号关联的服务。“报时”便是服务的一个典型例子。通常,每个服务都同一台特定服务器机器上的一个独一无二的端口编号关联在一起。客户程序必须事先知道自己要求的那项服务的运行端口号。

系统服务保留了使用端口1 到端口1024 的权力,所以不应让自己设计的服务占用这些以及其他任何已知正在使用的端口。

你可能感兴趣的:(java开发)