视频课堂:https://edu.csdn.net/course/play/8222
1969年,KenThompson和Dennis Ritchie在MurrayHill,New Jersey的贝尔电话实验室开发了与C语言一致的UNIX。很多年来,UNIX的发展停留在贝尔实验室和一些大学及研究机构,用特意设计的DEC PDP机器运行。到了1978 年,Bill Joy在Cal Berkeley领导了一个项目,给UNIX增添新的特性,例如虚拟内存和全屏显示功能。到了1984年早期,当Bill正准备建立Sun Microsystems,它发明了4.2BSD,即众所周知的Berkeley UNIX。
4.2BSD带有快速文件系统、可靠信号处理、进程间通信以及最重要的网络功能。最先在4.2中发现的网络支持后来成为了实际的Internet标准。Berkeley的TCP/IP实现保留了在Internet内通信的最初的标准。进程间和网络通信的套接字范型被Berkeley以外的系统广泛采用。甚至Window和Macintosh在20世纪80年代晚期也开始和“Berkeley 套接字”谈话。
|
网络套接字(network socket)有一点像电源插座。网络周围的各式插头有一个标准方法传输它们的有效负载。理解标准协议的任何东西都能够插入套接字并进行通信。对于电源插座,不论你插入一个电灯或是烤箱,只要它们使用60HZ,115伏电压,设备将会工作。
思考一下你的用电账单是怎样生成的。在你的房子和电网支架间可能有1米的距离,经过这一米的每千瓦电都将列入账单。账单到达你的“地址”。所以,虽然电流在电源插座周围是自由流动的,你房子的所有插头都是有特定的地址的。
除了我们谈论的是TCP/IP包和IP地址而不是电器和街道地址外,同样的思想被应用到网络套接字。Internet Protocol(IP)是一种低级路由协议。该协议将数据分解成小包然后通过网络传到一个地址,它并不确保传输的信息包一定到达目的。传输控制协议(TCP)是一种较高级的协议,它把这些信息包有力的捆绑在一起,在必要的时候,排序和重传这些信息包以获得可靠的数据传输。第三种协议,用户数据报协议(UDP)几乎与TCP协议相当,并能够直接用来支持快速的、无连接的、不可靠的信息包传输。
|
你经常在与网络有关的话题中听说客户/服务器(client/server)这个术语。在一些产品说明中,这个概念似乎非常复杂,其实它的含义很简单。 服务器(server)就是能够提供共享资源的任何东西。现在有计算服务器,提供计算功能;打印服务器,管理多个打印机;磁盘服务器,提供联网的磁盘空间;以及Web服务器,用来存储网页。客户(client)是简单的任何有权访问特定服务器的实体。客户和服务器之间的连接就像电灯和电源插头的连接。房间的电源插座是服务器,电灯是客户。服务器是永久的资源,在访问过服务器之后,客户可以自由的“拔去插头”。
在Berkeley套接字中,套接字的概念允许单个计算机同时服务于很多不同的客户,并能够提供不同类型信息的服务。该种技术由引入的端口(port)处理,此端口是一个特定机器上的被编号的套接字。服务器进程是在“监听”端口直到客户连到它。尽管每个客户部分是独特的,一个服务器允许在同样端口接受多个客户。为管理多个客户连接,服务器进程必须是多线程的,或者有同步输入/输出处理多路复用技术的其他方法。
|
一旦连接成功,一个高级的协议跟着生效,该协议与所使用的端口有关。TCP/IP 为特定协议保留了低端的1024 个端口。如果你在网络上冲浪有一些时间了,那么这中间的很多你已经很熟悉了。 端口21是为FTP的, 23是Telnet, 25是为e-mail, 79是为finger的, 80是HTTP,119是为网络新闻的——等等。下面该轮到讲述每个协议决定客户如何与端口交互了。
举例来说,HTTP是网络浏览器及服务器用来传输超文本网页和图像的协议。它是基本网页浏览服务器的一个非常简单的协议。下面是它的工作原理。当一个客户向一个HTTP服务器请求一个文件时,即一个点击动作,它仅仅以一种特定格式向预先指定的端口打印文件名然后读回文件的内容。服务器同样对状态代码编号反应,告诉客户请求是否被执行以及原因。下面是一个例子。客户请求单个文件/index.html,服务器回应它已成功找到该文件并且把文件传输到客户:
很明显,HTTP协议比该例显示的要复杂的多,但这是一个实际的和附近Web服务器的传输。
|
一个代理服务器(proxy server)以客户端协议与其他服务器通信。这在客户与服务器连接受到某些限制的情况下经常是必需的。这样,客户可以连接代理服务器,代理服务器没有这些限制并且代理服务器也会依次和客户通信。代理服务器具有过滤某些请求或缓存一些这样的请求的结果以备后用的额外功能。一个缓冲代理HTTP 服务器可用来减小局域网连向Internet的带宽要求。若一个流行网站的网址被成百上千个用户点击,代理服务器可以一次获得该网络服务器的流行网页,节省昂贵的Internet网络传输,同时为用户快速提供对这些网页的访问。
本章的后面部分,我们将实际建立一个完整的缓冲代理HTTP服务器。这个程序有趣的是它既是客户又是服务器。为服务于某些网页,它必须像客户那样向其他服务器获取被请求内容的一个拷贝。
|
Internet上的每一台计算机都有一个地址。Internet地址是网络上标识每台计算机的惟一定义的数。IP地址有32位,我们通常把它们分成4个从0到255的,有点号(.)隔开的序列。
这使它们易于记忆,因为它们不是随机指派的——它们是按层次结构指派的。最开始的字节定义了网络属于A、B、C、D或E哪个等级。多数Internet用户使用C级,因为有多于两百万的网络是在C类。C类网的开始字节从192到224,最后字节实际上标识了256之中可以上单个C类网的独立计算机。这种安排允许在C类网中可以有5亿的设备。
|
如果每台机器必须用数字作为它们的地址,Internet不是一个漫游的友好场所。例如,设想在广告的底部看见“http://192.9.9.1/”,这一定使人头昏脑胀。感谢上帝,存在一个与所有这些数字相伴的一个平行层次的名称的交换所,叫做域名服务(DNS)。就像IP地址中从左到右描绘网络层次的四个数字一样,Internet 地址的名称,域名,在名称空间从右到左描述了机器的地址。例如,www.osborne.com是在COM域(为美国商业企业保留)中的,叫做osborne(公司名称)的,www是Osborne的Web服务器的特定计算机的名称。www在意义上相应于IP地址的最右边的数字。
|
现在准备工作已经完成,让我们看看Java怎样和所有的这些网络概念相联系。Java 通过扩展第17章介绍的已有的流式输入/输出接口和增加在网络上建立输入/输出对象特性这两个方法支持TCP/IP。Java支持TCP和UDP协议族。TCP用于网络的可靠的流式输入/输出。UDP支持更简单的、快速的、点对点的数据报模式。
|
java.net 包所包含的类如下:
|
无论你是在打电话、发送邮件或建立与Internet的连接,地址是基础。InetAddress 类用来封装我们前面讨论的数字式的IP地址和该地址的域名。你通过一个IP主机名与这个类发生作用,IP主机名比它的IP地址用起来更简便更容易理解。InetAddress 类内部隐藏了地址数字。
|
InetAddress 类没有明显的构造函数。为生成一个InetAddress对象,必须运用一个可用的工厂方法。工厂方法(factory method)仅是一个类中静态方法返回一个该类实例的约定。
这是在一个带有各种参数列表的重载构造函数完成的,当持有惟一方法名时可使结果更清晰。对于InetAddress,三个方法 getLocalHost( )、getByName( )以及getAllByName( )可以用来创建InetAddress的实例。三个方法显示如下:
static InetAddress getLocalHost( )
throwsUnknownHostException
static InetAddress getByName(String hostName)
throwsUnknownHostException
static InetAddress[ ] getAllByName(String hostName)
throwsUnknownHostException
getLocalHost( )仅返回象征本地主机的InetAddress 对象。getByName( )方法返回一个传给它的主机名的InetAddress。如果这些方法不能解决主机名,它们引发一个UnknownHostException异常。
在Internet上,用一个名称来代表多个机器是很常有的事。Web服务器中,也有方法提供一定程度的缩放。getAllByName( )工厂方法返回代表由一个特殊名称分解的所有地址的InetAddresses类数组。在不能把名称分解成至少一个地址时,它将引发一个UnknownHostException异常。
下面的例子打印了本地机的地址和名称以及两个著名的Internet网址:
// Demonstrate InetAddress. import java.net.*; class InetAddressTest { publicstatic void main(String args[]) throws UnknownHostException { InetAddress Address = InetAddress.getLocalHost(); System.out.println(Address); Address =InetAddress.getByName("osborne.com"); System.out.println(Address); InetAddress SW[] = InetAddress.getAllByName("www.nba.com"); for (inti=0; i System.out.println(SW[i]); } }
下面是该程序的输出(当然,你所看到的结果可能有一些不同):
default/206.148.209.138
osborne.com/198.45.24.130
www.nba.com/204.202.130.223
|
InetAddress 类也有一些非静态的方法,列于下面,它们可以用于讨论过的方法返回的对象:
boolean equals(Object other)
如果对象具有和other相同的Internet地址则返回true。
byte[ ] getAddress( )
返回代表对象的Internert地址的以网络字节为顺序的有四个元素的字节数组。
String getHostAddress( )
返回代表与InetAddress对象相关的主机地址的字符串。
String getHostName( )
返回代表与InetAddress对象相关的主机名的字符串。
int hashCode( )
返回调用对象的散列码。
boolean isMulticastAddress( )
如果Internet地址是一个多播地址返回true;否则返回false。
String toString( ) 返回主机名字符串和IP地址。
Internet地址在分层的缓存服务器系列中被找到。这意味着你的本地机可能像知道它自己和附近的服务器一样知道一个名称-IP地址的自动映射。对于其他名称,它可能向一个本地DNS服务器询问IP地址信息。如果那个服务器不含一个指定的地址,它可以到一个远程的站点去询问。这可以一路通到名为InterNIC(internic.net)的根服务器。该过程可能需要比较长的时间,所以结构化你的代码以使你在本地存储IP地址信息而不是重复向上查找信息是一个明智之举。
|
TCP/IP 套接字用于在主机和Internet之间建立可靠的、双向的、持续的、点对点的流式连接。一个套接字可以用来建立Java 的输入/输出系统到其他的驻留在本地机或Internet上的任何机器的程序的连接。
注意:小应用程序只建立回到下载它的主机的套接字连接。存在这个限制的原因是:穿过防火墙的小应用程序有权使用任何机器是很危险的事情。
Java中有两类TCP套接字。一种是服务器端的,另一种是客户端的。ServerSocket类设计成在等待客户建立连接之前不做任何事的“监听器”。Socket类为建立连向服务器套接字以及启动协议交换而设计。
一个Socket对象的创建隐式建立了一个客户和服务器的连接。没有显式的说明建立连接细节的方法或构造函数。下面是用来生成客户套接字的两个构造函数:
Socket(String hostName, int port)
创建一个本地主机与给定名称的主机和端口的套接字连接,可以引发一个UnknownHostException异常或IOException异常。
Socket(InetAddress ipAddress, int port)
用一个预先存在的InetAddress对象和端口创建一个套接字,可以引发IOException异常。
使用下面的方法,可以在任何时候检查套接字的地址和与之有关的端口信息:
InetAddress getInetAddress( )
返回和Socket对象相关的InetAddress。
Int getPort( )
返回与该Socket对象连接的远程端口。
Int getLocalPort( )
返回与该Socket连接的本地端口。
一旦Socket对象被创建,同样可以检查它获得访问与之相连的输入和输出流的权力。
如果套接字因为网络的连接中断而失效,这些方法都能够引发一个IOException异常。
InputStream getInputStream( )
返回与调用套接字有关的InputStream类。
OutputStream getOutputStream( )
返回与调用套接字有关的OutputStream类。
void close( )
关闭InputStream和OutputStream。
|
下面的例子打开了一个InterNIC服务器上“whois”端口的连接,传输命令行语句到套接字,然后打印返回的数据。InterNIC将努力寻找作为已注册的Internet域名的参数,然后传输回IP地址和该地点的联系信息。
//Demonstrate Sockets. import java.net.*; import java.io.*; class Whois { publicstatic void main(String args[]) throws Exception { int c; Socket s= new Socket("internic.net", 43); InputStream in = s.getInputStream(); OutputStream out = s.getOutputStream(); String str = (args.length == 0 ?"osborne.com" : args[0]) + "\n"; bytebuf[] = str.getBytes(); out.write(buf); while ((c= in.read()) != -1) { System.out.print((char) c); } s.close(); } }
例如,如果你在命令行键入osborne.com,你将会获得下面相似的结果:
Whois Server Version 1.3
Domain names in the .com, .net, and .org domainscan now be registered
with many different competing registrars. Go tohttp://www.internic.net
for detailed information.
DomainName: OSBORNE.COM
Registrar:NETWORK SOLUTIONS, INC.
WhoisServer: whois.networksolutions.com
ReferralURL: www.networksolutions.com
NameServer: NS1.EPPG.COM
NameServer: NS2.EPPG.COM
UpdatedDate: 07-apr-2000
>>> Last update of whois database: Fri, 6Oct 2000 10:03:36 EDT <<<
The Registry database contains ONLY .COM, .NET,.ORG, .EDU domains and
Registrars.
|
最后的例子有一点含糊,因为现代Internet不是围绕老的协议,如whois、finger及FTP的,而是关于WWW(万维网)的。Web是一个由Web浏览器统一的高级协议和文件格式的松散集合。Web中最重要的一个方面是Tim Berners-Lee设计了一个定位所有网络资源的弹性方法。一旦你能够可靠的命名一个事物,它将成为一个功能强大的范型,统一资源定位(URL)就做这些。
URL提供了一个相当容易理解的形式来惟一确定或对Internet上的信息进行编址。URL是无所不在的; 每一个浏览器用它们来识别Web上的信息。 实际上, Web是用URL和HTML 为所有资源编址的同样陈旧的Internet 。在Java的网络类库中,URL 类为用URL在Internet 上获取信息提供了一个简单的、简洁的用户编程接口(API)。
|
http://www.osborne.com/ 和 http://www.osborne.com:80/index.htm是URL 的两个例子。
一个URL规范以四个元素为基础。第一个是所用到的协议,用冒号(:)来将它与定位符的其他部分相隔离。尽管现在所有的事情都通过HTTP(实际上,如果你在URL规范中不用“http://”,大多数浏览器都能正确执行)完成,但它不是惟一的协议,常见的协议有http、ftp、gopher和文件。第二个元素是主机名或所用主机的IP地址,这由左边的双斜线(//)和右边的单斜线(/)或可选冒号(:)限制。第三个成分,端口号,是可选的参数,由主机名左边的冒号(:)和右边的斜线(/)限制(它的默认端口为80,它是预定义的HTTP 端口;所以“:80”是多余的)。第四部分是实际的文件路径。多数HTTP服务器将给URL附加一个与目录资源相关的index.html或 index.htm 文件。所以,http://www.osborne.com/ 与http://www.osborne.com/index.htm是相同的。
Java的URL类有多个构造函数,每个都能引发一个MalformedURLException异常。一个常见形式是用与你在浏览器中看到的相同的字符串指定URL:
URL(String urlSpecifier)
下面的两个构造函数形式允许你把URL分裂成它的组成部分:
URL(String protocolName, String hostName, int port,String path)
URL(String protocolName, String hostName, String path)
另一个经常用到的构造函数允许你用一个已经存在的URL作为引用上下文,然后从该上下文中创建一个新的URL。尽管这听起来有些别扭,它实际上是很简单而有用的。
URL(URL urlObj, String urlSpecifier)
下面的例子中,我们为Osborne的下载页面创建一个URL,然后检查它的属性:
// Demonstrate URL. import java.net.*; class URLDemo { publicstatic void main(String args[]) throws MalformedURLException { URL hp =new URL("http://www.osborne.com/download"); System.out.println("Protocol:" + hp.getProtocol()); System.out.println("Port: " +hp.getPort()); System.out.println("Host: " +hp.getHost()); System.out.println("File:" + hp.getFile()); System.out.println("Ext:" + hp.toExternalForm()); } }
运行该程序,你将获得下面输出:
Protocol: http
Port: -1
Host: www.osborne.com
File: /download
Ext:http://www.osborne.com/download
注意端口是-1,这意味着该端口没有被明确设置。现在我们已经创建了一个URL对象,我们希望找回与之相连的数据。 为获得URL的实际比特或内容信息,用它的openConnection( )方法从它创建一个URLConnection对象,如下:
url.openConnection()
openConnection( )有下面的常用形式:
URLConnectionopenConnection( )
与调用URL对象相关,它返回一个URLConnection对象。它可能引发IOException异常。
|
URLConnection是访问远程资源属性的一般用途的类。如果你建立了与远程服务器之间的连接,你可以在传输它到本地之前用URLConnection来检察远程对象的属性。这些属性由HTTP协议规范定义并且仅对用HTTP协议的URL对象有意义。我们这里将验证URLConnection最有用的原理。
下面的例子中我们用一个URL对象的openConnection( )方法创建了一个URLConnection类,然后用它来检查文件的属性和内容:
// Demonstrate URLConnection. import java.net.*; import java.io.*; import java.util.Date; class UCDemo { publicstatic void main(String args[]) throws Exception { int c; URL hp =new URL("http://www.osborne.com"); URLConnection hpCon = hp.openConnection(); System.out.println("Date: " + new Date(hpCon.getDate())); System.out.println("Content-Type: " + hpCon.getContentType()); System.out.println("Expires: " + hpCon.getExpiration()); System.out.println("Last-Modified: " + newDate(hpCon.getLastModified())); int len =hpCon.getContentLength(); System.out.println("Content-Length:" + len); if (len> 0) { System.out.println("=== Content ==="); InputStream input = hpCon.getInputStream(); int i =len; while(((c = input.read()) != -1) && (--i > 0)) { System.out.print((char) c); } input.close(); } else { System.out.println("No Content Available"); } } }
该程序建立了一个经过端口80通向www.osborne.com的HTTP 连接。然后列出了标头值并检索内容。下面是输出的前几行:
Date: Tue Jul 21 16:37:44 CST 2009
Content-Type: text/html
Expires: 375007920000
Last-Modified: Thu Jan 01 08:00:00 CST 1970
Content-Length: -1
No Content Available
URL和URLConnection类对于希望建立与HTTP服务器的连接来获取信息的简单程序来说是非常好的。对于更复杂的应用程序,你会发现学习HTTP协议规范,实现你自己的包装程序是比较好的。