本章将主要介绍 Java 网络通信的支持,通过这些网络支持类,Java 程序可以非常方便地访问互 联网上的 HTTP 服务,FTP 服务等,并可以直接取得互联网上的远程资源,还可以向远程资源发送 GET,POST 请求. 本章将先简要介绍计算机网络的基础知识,包括 IP 地址和端口等概念,这些知识是网络编程的 基础.本章将会详细介绍 InetAddress,URLDecoder,URLEncoder,URL 和 URLConnection 等网络 工具类,并会深入介绍通过 URLConnection 发送请求,访问远程资源等操作. 本章重点将会介绍 Java 提供的 TCP 网络通信支持,包括如何利用 ServerSocket 建立 TCP 服务 器,利用 Socket 建立 TCP 客户端.实际上 Java 的网络通信非常简单,服务器端通过 ServerSocket 建立监听,客户端通过 Socket 连接到指定服务器后,通信双方就可以通过 IO 流进行通信.本章将 以逐步迭代的方式开发一个 C/S 结构多人网络聊天工具为例, 向读者介绍基于 TCP 协议的网络编程. 本章也将重点介绍 Java 提供的 UDP 网络通信支持, 主要介绍如何使用 DatagramSocket 来发送, 接收数据报(DatagramPacket) ,并讲解如何使用 MulticastSocket 来实现多点广播通信.本章也将以 开发局域网通信程序为例来介绍 MulticastSocket 和 DatagramSocket 的实际用法. 本章最后还会介绍利用 Proxy 和 ProxySelector 在 Java 程序中通过代理服务器访问远程资源. 17.1 网络编程的基础知识时至今日,计算机网络缩短了人们之间的距离,把"地球村"变成现实.网络应用已经成为计 算机领域最广泛的应用. 17.1.1 网络基础知识所谓计算机网络, 就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规 模大,功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件,软件,数据信息 等资源. 计算机网络是现代通信技术与计算机技术相结合的产物, 计算机网络可以提供以下一些主要功能. 资源共享. 信息传输与集中
处理. 均衡负荷与分布处理. 综合信息服务. 通过计算机网络可以向全社会提供各种经济信息,科研情报和咨询服务.其中,国际互联网 Internet 上的全球信息网(WWW—World Wide Web)服务就是一个最典型也是最成功的例子.实际 上,今天的网络承载绝大部分大型企业的运转,一个大型的,全球性的企业或组织的日常工作流程 都是建立在互联网基础之上的. 计算机网络的品种很多,根据各种不同的分类原则,可以得到各种不同类型的计算机网络.计 算机网络通常是按照规模大小和延伸范围来分类的, 常见的划分为: 局域网 (LAN) 城域网 , (MAN) , 广域网(WAN) .INTERNET 可以视为世界上最大的广域网. 如果按照网络的拓扑结构来划分,可以分为星型网络,总线网络,环线网络,树型网络,星型环 线网络等;如
果按照网络的传输介质来划分,可以分为双绞线网,同轴电缆网,光纤网和卫星网等. 下面简单介绍按规模来分类 局域网(LAN):指在一个较小地理范围内的各种计算机网络设备互连在一起的通信网络, 可以包含一个或多个子网,通常局限在几千米的范围之内. 城域网(MAN):主要是由城域范围内的各局域网之间互连而构成的. 广域网(WAN):是由相距较远的局域网或城域网互联而成,通常是除了计算机设备以外, 794 疯狂 Java 联盟独家提供试读 还要涉及一些电信通信方式. 计算机网络中实现通信必须有一些约定,即通信协议.对速率,传输代码,代码结构,传输控 制步骤,出错控制等制定标准.为了使两个结点之间能进行对话,必须在它们之间建立通信工具, 使彼此之间能进行信息交换. 接口包括两部分:一是硬件装置,功能是实现结点之间的信息传送;二是软件装置,功能是规 定双方进行通信的约
定协议. 计算机网络协议通常由三部分组成:一是语义部分,用于决定双方对话的类型;二是语法部分, 用于决定双方对话的格式;三是变换规则,用于决定通信双方的应答关系. 由于世界上大型计算机厂商推出各自不同的网络体系结构,影响了网络通信的统一性.因此国 际标准化组织 ISO 于 l978 年提出"开放系统互连参考模型" ,即著名的 OSI(Open System Interconnection) . 开放系统互连参考模型力求将网络简化,并以模块化的方式来设计网络. 开放系统互连参考模型把计算机网络分成物理层,数据链路层,网络层,传输层,会话层,表 示层,应用层等七层,受到计算机界和通信业的极大关注.通过十多年的发展和推进,OSI 模式已 成为各种计算机网络结构的参考标准. 图 17.1 显示了 OSI 参考模型的推荐分层. 前面介绍到网络通信之间必须有硬件和软件方面的支持,通信协议是网络通信的基础,而 IP 协 议是一种非常重要的协议.IP(InternetProtocol)协议又称互联网协议,是支持网间互联的数据报协 议.它提供网间连接的完善功能,包括 IP 数据报规定互联网络范围内的地址格式. 经常与 IP 协议放在一起的还有 TCP(Transmission Control Protocol)协议,即传输控制协议, 它规定一种可靠的数据信息传递服务.虽然 IP 和 TCP 这两个协议功能不尽相同,也可以分开单独 使用,但它们是在同一个时期作为一个协议来设计的,并且在功能上也是互补的.因此实际使用中 常常把这两个协议统称为 TCP/IP 协议,TCP/IP 协议最早出现在 UNIX 操作系统中,现在几乎所有 的操作系统都支持 TCP/IP 协议,因此 TCP/IP 协议也是 Internet 中最常用的基础协议. 按 TCP/IP 协议模型,网络通常被分为一个四层模型,这个四层模型和前面的 OSI 七层模型有 大致的对应关系,图 10.2 显示了 TCP/IP 分层
java学习资源
模型和 OSI 分层模型之间的对应关系. 图 17.1 OSI 参考模型的推荐分层 图 17.2 OSI 分层模型和 TCP/IP 分层模型的对应关系 17.1.2 IP 地址和端口号 IP 地址用于标志网络中的一个通信实体,这个通信实体可以是一台主机,也可以是一台打印机, 或者是路由器的某一个端口. 而在基于 IP 协议网络中传输的数据包, 都必须使用 IP 地址来进行标识. 如同我们写一封信,要标明收信人的通信地址和发信人的地址,而邮政工作人员则通过该地址 来决定邮件的去向.类似的过程也发生在计算机网络里,每个被传输的数据包也要包括一个源 IP 地 795 疯狂 Java 联盟独家提供试读 疯狂 讲义 址和一个目的 IP 地址,当该数据包在网络中进行传输时,这两个地址要保持不变,以确保网络设备 总能根据确定的 IP 地址,将数据包从源通信实体送往指定的目的通信实体. IP 地址是数字型的,IP 地址是一个 32 位(32bit)整数,但通常为了更加便于记忆,通常也把 它分成 4 个 8 位的二进制数组成,每 8 位之间用圆点隔开,每个 8 位整数可以转换成一个 0~255 的十进制整数,因此我们看到的 IP 地址常常是如下形式:202.9.128.88. NIC(Internet Network Information Center)统一负责全球 Internet IP 地址的规划,管理,而 Inter NIC,APNIC,RIPE 三大网络信息中心具体负责美国及其他地区的 IP 地址分配.其中 APNIC 负责 亚太地区的 IP 管理,我国申请 IP 地址也要通过 APNIC,APNIC 的总部设在日本东京大学. IP 地址被分成了 A,B,C,D,E 五类,每个类别的网络标识和主机标识各有规则. A 类:10.0.0.0 ~ 10.255.255.255 B 类:172.16.0.0 ~ 172.31.255.255 C 类:192.168.0.0 ~192.168.255.255 IP 地址可以唯一地确定网络上的一个通信实体,但一个通信实体可以有多个通信程序同时提供 网络服务,此时还需要使用端口. 端口是一个 16 位的整数,用于表示数据交给哪个通信程序处理.因此,端口就是应用
程序与外 界交流的出入口,它是一种抽象的软件结构,包括一些数据结构和 I/O(基本输入/输出)缓冲区. 不同的应用程序处理不同端口上的数据,同一台机器上不能有两个程序使用同一个端口,端口 号可以从 0 到 65535,通常将它分为三类: 公认端口(Well Known Ports):从 0 到 1023,它们紧密绑定(Binding)一些服务. 注册端口(Registered Ports):从 1024 到 49151.它们松散地绑定一些服务. 动态和/或私有端口(Dynamic and/or Private Ports):从 49152 到 65535,这些端口是应 用程序使用的动态端口,应用程序一般不会主动使用这些端口. 如果我们把 IP 地址理解为某个人所在地方的地址(包括街道和门牌号) ,但仅有地址还是不 到这个人,还需要知道他所在的房号才可以到这个人.因此如果我们认为应用程序是人,而计算 机网络充当类似邮递员的
角,当一个程序需要发送数据时,需要指定目的地的 IP 地址和端口,如 果指定了正确的 IP 地址和端口号,计算机网络就可以将数据送给该 IP 地址和端口所对应的程序. 17.2 Java 的基本网络支持 Java 为网络支持提供了 java 包, 该包下的 URL 和 URLConnection 等类提供了以编程方式访 问 Web 服务的功能,而 URLDecoder 和 URLEncoder 则提供普通字符串和 application/x-www-formurlencoded MIME 字符串相互转换的静态方法. 17.2.1 使用 InetAddress Java 提供了 InetAddress 类来代表 IP 地址, InetAddress 下还有 2 个子类: Inet4Address, Inet6Address, 它们分别代表 Internet Protocol version 4(IPv4)地址和 Internet Protocol version 6(IPv6)地址. InetAddress 类没有提供构造器,而是提供了如下两个静态方法来获取 InetAddress 实例: getByName(String host):根据主机获取
对应的 InetAddress 对象. getByAddress(byte[] addr):根据原始 IP 地址来获取对应的 InetAddress 对象. InetAddress 还提供了如下三个方法来获取 InetAddress 实例对应的 IP 地址和主机名: String getCanonicalHostName():获取此 IP 地址的全限定域名. String getHostAddress(): 返回该 InetAddress 实例对应的 IP 地址字符串 (以字符串形式) . 796 疯狂 Java 联盟独家提供试读 String getHostName():获取此 IP 地址的主机名. 除此之外,InetAddress 类还提供了一个 getLocalHost()方法来获取本机 IP 地址对应的 InetAddress 实例. InetAddress 类还提供了一个 isReachable()方法,用于测试是否可以到达该地址.该方法的实现 将尽最大努力试图到达主机,但防火墙和服务器配置可能阻塞请求,使其在某些特定的端口可以访 问时处于不可达的状态.如果可以获得权限,则典型实现将使用 ICMP ECHO REQUEST;否则它 将试图在目标主机的端口 7 Echo)上建立 TCP 连接.下面程序测试了 InetAddress 的简单用法: 程序清单:codes/17/17-2/InetAddressTest.java public class InetAddressTest { public static void main(String[] args) throws Exception { //根据主机名来获取对应的 InetAddress 实例 InetAddress ip = ByName("du"); //判断是否可达 System.out.println("oneedu 是否可达:" + ip.isReachable(2000)); //获取该 InetAddress 实例的 IP 字符串 System.out.HostAddress()); //根据原始 IP 地址来获取对应的 InetAddress 实例 InetAddress local = ByAddress(new byte[] {127,0,0,1}); System.out.println("本机是否可达:" + local.isReachable(5000)); //获取该 InetAddress 实例对应的全限定域名 System.out.CanonicalHostName()); } } 上面程序简单地示范了 InetAddress 类几个方
法用法,InetAddress 类本身并没有提供太多功能, 它代表一个 IP 地址对象,是网络通信的基础,在后面介绍中将大量使用该类. 17.2.2 使用 URLDecoder 和 URLEncoder URLDecoder 和 URLEncoder 用
于完成普通字符串和 application/x-www-form-urlencoded MIME 字符 串之间的相互转换. 可能有读者觉得后一个字符串非常专业, 以为又是什么特别高深的知识, 其实不是. 在介绍 application/x-www-form-urlencoded MIME 字符串之前,先使用 le 搜索关 键字"李刚 j2ee" ,将看到如图 17.3 所示的界面: 图 17.3 搜索关键字包含中文 从图 17.3 中可以看出:当我们搜索的关键字包含中文时,这些关键字就会变成如图 17.3 所示 797 疯狂 Java 联盟独家提供试读 疯狂 讲义 的 "乱码" ——实际上这不是乱码, 这就是所谓的 application/x-www-form-urlencoded MIME 字符串. 当 URL 地址里包含非西欧字符的字符串时,系统会将这些非西欧字符串转换成如图 17.3 所示 的特殊字符串.那么编程过程中可能涉及将普通字符串和这种特殊字符串的相关转换,这就需要使 用 URLDecoder 和 URLEncoder 类,其中: URLDecoder 类包含一个 decode(String s,String enc)静态方法,它可以将看上去是乱码的 特殊字符串转转成普通字符串. URLEncoder 类包含一个 encode(String s,String enc)静态方法,它可以将普通字符串转换 成 application/x-www-form-urlencoded MIME 字符串. 下面程序示范了如何将图 17.3 所示地址栏中"乱码"转换成普通字符串,并示范了如何将普通 字符串转换成 application/x-www-form-urlencoded MIME 字符串. 程序清单:codes/17/17-2/URLDecoderTest.java public class URLDecoderTest { public static void main(String[] args) throws Exception { //将 application/x-www-for
m-urlencoded 字符串 //转换成普通字符串 //其中的字符串直接从图 17.3 所示窗口复制过来 String keyWord = URLDecoder.decode( "%E6%9D%8E%E5%88%9A+j2ee", "UTF-8"); System.out.println(keyWord); //将普通字符串转换成 //application/x-www-form-urlencoded 字符串 String urlStr = de( "ROR 敏捷开发最佳指南 System.out.println(urlStr); } } 上面程序中粗体字代码用于完成普通字符串和 application/x-www-form-urlencoded MIME 字符 串之间的转换.运行上面程序将看到如下输出: 李刚 j2ee ROR%C3%F4%BD%DD%BF%AA%B7%A2%D7%EE%BC%D1%D6%B8%C4%CF 提 示: 仅包含西欧字符的普通字符串和 application/x-www-form-urlencoded MIME 字符 串无须转换,而包含中文字符的普通字符串则需要转换,转换的方法是每个中文字符 占 2 个字节,每个字节可以转换成 2 个十六进制的数字,所以每个中文字符将转换成 "%XX%XX"的形式.当然,采用不同的字符集时,每个中文字符对应的字节数并 不完全相同,所以使用 URLEncoder 和 URLDecoder 进行转换时也需要指定字符集. 17.2.3 使用 URL 和 URLConnection URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网"资源"的指针. 资源可以是简单的文件或目录, 也可以是对更为复杂的对象引用, 例如对数据库或搜索引擎
的查询. 通常情况而言,URL 可以由协议名,主机,端口和资源组成.即满足如下格式: protocol://host:port/resourceName 例如如下的 URL 地址: du/Index.htm 798 疯狂
Java 联盟独家提供试读 提 示: JDK 中还提供了一个 URI(Uniform Resource Identifiers)类,其实例代表一个统 一资源标识符,Java 的 URI 不能用于定位任何资源,它的唯一作用就是解析.与此对 应的是,URL 则包含一个可打开到达该资源的输入流,因此我们可以将 URL 理解成 URI 的特例. URL 类提供了多个构造器用于创建 URL 对象,一旦获得了 URL 对象之后,可以调用如下方法 来访问该 URL 对应的资源: String getFile():获取此 URL 的资源名. String getHost():获取此 URL 的主机名. String getPath():获取此 URL 的路径部分. int getPort():获取此 URL 的端口号. String getProtocol():获取此 URL 的协议名称. String getQuery():获取此 URL 的查询字符串部分. URLConnection openConnection():返回一个 URLConnection 对象,它表示到 URL 所引 用的远程对象的连接. InputStream openStream():打开与此 URL 的连接,并返回一个用于读取该 URL 资源的 InputStream. URL 对象中前面几个方法都非常容易理解, 而该对象提供的 openStream()可以读取该 URL 资源 的 InputStream, 通过该方法可以非常方便地读取远程资源——甚至实现多线程下载. 如下程序所示: 程序清单:codes/17/17-2/MutilDown.java //定义下载从 start 到 end 的内容的线程 class DownThread extends Thread { //定义字节数组(取水的竹筒)的长度 private final int BUFF_LEN = 32; //定义下载的起始点 private long start; //定义下载的结束点 private long end; //下载资源对应的输入流 private InputStream is; //将下载到的字节输出到 raf 中 private RandomAccessFile raf ; //构造器,传入输入流,输出流和下载起始点,结束点 public DownThread(long start , long end , InputStream is , RandomAccessFile raf) { //输出该线程负责下载的字节位置 System.out.println(start + "---->" + end); t
his.start = start; d = end; this.is = is; this.raf = raf; } public void run() { try { is.skip(start); raf.seek(start); 799 疯狂 Java 联盟独家提供试读 疯狂 讲义 //定义读取输入流内容的缓存数组(竹筒) byte[] buff = new byte[BUFF_LEN]; //本线程负责下载资源的大小 long contentLen = end - start; //定义最多需要读取几次就可以完成本线程的下载 long times = contentLen / BUFF_LEN + 4; //实际读取的字节数 int hasRead = 0; for (int i = 0; i < times ; i++) { hasRead = is.read(buff); //如果读取的字节数小于 0,则退出循环! if (hasRead < 0) { break; } raf.write(buff , 0 , hasRead); } } catch (Exception ex) { ex.printStackTrace(); } //使用 finally 块来关闭当前线程的输入流,输出流 finally { try { if (is != null) { is.close(); } if (raf != null) { raf.close(); } } catch (Except
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论