所谓计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
计算机网络是现代通信技术与计算机技术相结合的产物,计算机网络可以提供以下一些主要功能.
–资源共享。
–信息传输与集中处理。
–均衡负荷与分布处理。
–综合信息服务。
网络按规模的三种分类:
局域网(LAN):指在一个较小地理范围内的各种计算机网络设备互连在一起的通信网络,可以包含一个或多个子网,通常局限在几千米的范围之内。
城域网(MAN):主要是由城域范围内的各局域网之间互连而构成的,现在很少提起这个概念。
广域网(WAN):是由相距较远的局域网或城域网互连而成,通常是除了计算机设备以外,还要涉及一些电信通讯方式。
TCP/IP分层和OSI分层
TCP/IP协议集
IP地址
IP地址用于标识网络中的一个通信实体,这个通信实体可以是一台主机,也可以是一台打印机,或者是路由器的某一个端口。而在基于IP协议网络中传输的数据包,都必须使用IP地址来进行标识。
IP地址是数字型的,IP地址是一个32位(32bit)整数,但通常为了更加便于记忆,通常也把它分成4个8位的二进制数组成,每8位之间用圆点隔开,每个8位整数可以转换成一个0~255的十进制整数,因此我们看到的IP地址常常是如下形式:202.9.128.88。
IP与DNS
IP 地址
–连接至网络的每台计算机都是唯一的
–32 位数字,四个用点号分隔的数字
–包括网络 ID 和主机 ID
–网络的类包括 A、B、C和 D 类E
–0~126
–128~191
–192~223
–127.0.0.1
–192.168.10.179
–10.11.0.1
域名系统
–将特定 IP 地址映射至字符串
–映射由域名服务器系统维护
端口
端口是一个16位的整数,用于表示数据交给哪个通信程序处理。因此,端口是应用程序与外界交流的出入口,它是一种抽象的软件结构,包括一些数据结构和I/O(基本输入/输出)缓冲区。
不同的应用程序处理不同端口上的数据,同一台机器上不能有两个程序使用同一个端口,端口号可以从0到65535,通常将它分为三类:
–公认端口(Well Known Ports):从0到1023,它们紧密绑定(Binding)一些服务。
–注册端口(Registered Ports):从1024到49151。它们松散地绑定一些服务。
-动态和/或私有端口(Dynamic and/or Private Ports):从49152到65535,这些端口是应用程序使用的动态端口,应用程序一般不会主动使用这些端口。
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地址字符串(以字符串形式)。
–String getHostName():获取此 IP 地址的主机名。
URLDecoder和URLEncoder
URLDecoder类包含一个decode(String s,String enc)静态方法,它可以将看上去是乱码的特殊字符串转转成普通字符串。
URLEncoder类包含一个encode(String s,String enc)静态方法,它可以将普通字符串转换成application/x-www-form-urlencoded MIME字符串。
URL
URL(Uniform Resource Locator)对象代表统一资源定位器,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更复杂的对象引用,例如对数据库或搜索引擎的查询。通常情况而言,URL可以由协议名、主机、端口和资源组成。即满足如下格式:
protocol://host:port/resourceName
URLConnection
程序可以通过URLConnection实例向该URL发送请求、读取URL引用的资源。
通常创建一个和 URL 的连接,并发送请求、读取此 URL 引用的资源需要如下几个步骤:
–(1)通过调用URL对象openConnection()方法来创建URLConnection对象。
–(2)设置URLConnection的参数和普通请求属性。
–(3)如果只是发送GET方式请求,使用connect方法建立和远程资源之间的实际连接即可;如果需要发送POST方式的请求,需要获取URLConnection实例对应的输出流来发送请求参数。
–(4)远程资源变为可用,程序可以访问远程资源的头字段、或通过输入流读取远程资源的数据。
IP协议
IP协议是Internet上使用的一个关键协议,它的全称是Internet Protocol,即Internet协议,通常简称IP协议。通过使用IP协议,从而使Internet成为一个允许连接不同类型的计算机和不同操作系统的网络。
IP协议只保证计算机能发送和接收分组数据。IP协议负责将消息从一个主机传送到另一个主机,消息在传送的过程中被分割成一个个的小包。
Java对TCP/IP协议的支持
TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信接口,并通过Socket产生IO流来进行网络通信。
TCP协议
TCP协议被称作一种端对端协议。这是因为它为两台计算机之间的连接起了重要作用:当一台计算机需要与另一台远程计算机连接时,TCP协议会让它们建立一个连接:用于发送和接收数据的虚拟链路。
TCP协议负责收集这些信息包,并将其按适当的次序放好传送,在接收端收到后再将其正确地还原。TCP协议保证了数据包在传送中准确无误。TCP协议使用重发机制:当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体确认信息,如果没有收到另一个通信实体的确认信息,则会再次重发刚才发送的信息。
通过这种重发机制,TCP协议向应用程序提供可靠的通信连接,使它能够自动适应网上的各种变化。即使在 Internet 暂时出现堵塞的情况下,TCP也能够保证通信的可靠。
ServerSocket
ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。ServerSocket包含一个监听来自客户端连接请求的方法:
Socket accept():如果接收到一个客户端Socket的连接请求,该方法将返回一个与连客户端Socket对应的Socket(如图17.4所示每个TCP连接有两个Socket);否则该方法将一直处于等待状态,线程也被阻塞。
为了创建ServerSocket对象,ServerSocket类提供了如下几个构造器:
–ServerSocket(int port):用指定的端口port来创建一个ServerSocket。该端口应该是有一个有效的端口整数值:0~65535。
–ServerSocket(int port,int backlog):增加一个用来改变连接队列长度的参数backlog。
–ServerSocket(int port,int backlog,InetAddress localAddr):在机器存在多个 IP地址的情况下,允许通过localAddr这个参数来指定将ServerSocket绑定到指定的IP地址。
Socket
客户端通常可使用Socket的构造器来连接到指定服务器,Socket通常可使用如下两个构造器:
–Socket(InetAddress/String remoteAddress, int port):创建连接到指定远程主机、远程端口的Socket,该构造器没有指定本地地址、本地端口,默认使用本地主机的默认IP地址,默认使用系统动态指定的IP地址。
–Socket(InetAddress/String remoteAddress, int port, InetAddress localAddr, int localPort)::创建连接到指定远程主机、远程端口的Socket,并指定本地IP地址和本地端口号,适用于本地主机有多个IP地址的情形。
网络通信
当客户端、服务器端产生了对应的Socket之后,此时就到了如图17.4所示的通信示意图,程序无需再区分服务器、客户端,而是通过各自的Socket进行通信,Socket提供如下两个方法来获取输入流和输出流:
–InputStream getInputStream():返回该Socket对象对应的输入流,让程序通过该输入流从Socket中取出数据。
–OutputStream getOutputStream():返回该Socket对象对应的输出流,让程序通过该输出流向Socket中输出数据。
客户端
通过Socket建立对象并指定要连接的服务端主机以及端口。
Socket s = new Socket(“192.168.1.1”,9999);
OutputStream out = s.getOutputStream();
out.write(“hello”.getBytes());
s.close();
服务端
建立服务端需要监听一个端口
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();
思考
对于Web服务器而言,当有多个客户端同时访问服务器时,服务端又如何提供服务呢?
加入多线程支持
实际应用中的客户端则可能需要和服务器端保持长时间通信,即服务器需要不断地读取客户端数据,并向客户端写入数据;客户端也需要不断地读取服务器数据,并向服务器写入数据。
使用传统BufferedReader的readLine()方法读取数据时,当该方法成功返回之前,线程被阻塞,程序无法继续执行。考虑到这个原因,因此服务器应该每个Socket单独启动一条线程,每条线程负责与一个客户端进行通信。
客户端读取服务器数据的线程同样会被阻塞,所以系统应该单独启动一条线程,该线程专门负责读取服务器数据。
NIO实现非阻塞通信
Java的NIO为非阻塞式的Socket通信提供了如下几个特殊类:
–Selector:它是SelectableChannel对象的多路复用器,所有希望采用非阻塞方式进行通信的Channel都应该注册到Selector对象。可通过调用此类的静态open()方法来创建Selector实例,该方法将使用系统默认的Selector来返回新的Selector。
–SelectableChannel:它代表可以支持非阻塞IO操作的Channel对象,可以将其注册到Selector上,这种注册的关系由SelectionKey实例表示。Selector对象提供了一个select()方法,该方法允许应用程序同时监控多个IO Channel。
–SelectionKey:该对象代表SelectableChannel和Selector之间的注册关系。
–ServerSocketChannel:支持非阻塞操作,对应于java.net.ServerSocket这个类,提供了TCP协议IO接口,只支持OP_ACCEPT操作。该类也提供了accept()方法,功能相当于ServerSocket提供的accept()方法。
–SocketChannel:支持非阻塞操作,对应于java.net.Socket这个类,提供了TCP协议IO接口,支持OP_CONNECT,OP_READ和OP_WRITE操作。这个类还实现了ByteChannel接口、ScatteringByteChannel接口和GatheringByteChannel接口,所以可以直接通过SocketChannel来读写ByteBuffer对象。
服务器上所有Channel(包括ServerSocketChannel和SocketChannel)都需要向Selector注册,而该Selector则负责监视这些Socket的IO状态,当其中任意一个或多个Channel具有可用的IO操作时,该Selector的select()方法将会返回大于0的整数,该整数值就表示该Selector上有多少个Channel具有可用的IO操作,并提供了selectedKeys()方法来返回这些Channel对应的SelectionKey集合。正是通过Selector,使得服务器端只需要不断地调用Selector实例的select()方法即可知道当前所有Channel是否有需要处理的IO操作。
代理服务器
代理服务器的功能就是代理网络用户去取得网络信息。我们使用网络浏览器直接连接其他Internet站点取得网络信息时,通常需要发送Request请求来等待响应。代理服务器是介于浏览器和Web服务器之间的一台服务器,有了它之后,浏览器不是直接到Web服务器去取得网页数据,而是向代理服务器发出请求,Request请求会先送到代理服务器,由代理服务器来取回浏览器所需要的信息并送回给网络浏览器。
使用Proxy
Proxy有如下一个构造器:Proxy(Proxy.Type type, SocketAddress sa):创建表示代理服务器的Proxy对象。而sa参数指定代理服务器的地址,其中type是该代理服务器的类型,该服务器类型有如下三种:
–Proxy.Type.DIRECT:表示直接连接或代理不存在。
–Proxy.Type.HTTP:表示高级协议的代理,如 HTTP 或 FTP。
–Proxy.Type.SOCKS:表示 SOCKS(V4 或 V5)代理。
一旦创建了Proxy对象之后,程序就可以在使用URLConnection打开连接时,或创建Socket连接时传入一个Proxy对象,作为本次连接所使用的代理服务器。
其中URL包含了一个URLConnection openConnection(Proxy proxy)方法,该方法使用指定的代理服务器来打开连接;而Socket则提供了一个Socket(Proxy proxy)构造器,该构造器使用指定的代理服务器创建一个没有连接的Socket对象。
使用ProxySelector
ProxySelector可以它根据不同的连接使用不同的代理服务器。
系统默认的ProxySelector会检测各种系统属性和URL协议,然后决定怎样连接不同的主机。当然,程序也可以调用ProxySelector类的setDefault()静态方法来设置默认代理服务器,也可以调用getDefault()方法获得系统当前默认的代理服务器。
程序可以通过System类来设置系统的代理服务器属性,关于代理服务器常用的属性名有如下三个:
–http.proxyHost:设置HTTP访问所使用的代理服务器地址。该属性名的前缀可以改为https、ftp等,分别用于设置HTTP访问、安全HTTP访问和FTP访问所用的代理服务器地址。
–http.proxyPort:设置HTTP访问所使用的代理服务器端口。该属性名的前缀可以改为https、ftp等,分别用于设置HTTP访问、安全HTTP访问和FTP访问所用的代理服务器端口。
–http.nonProxyHosts:设置HTTP访问中不需要使用代理服务器的远程主机,可以使用*通配符,如果有多个地址,多个地址用竖线(|)分隔。
自定义ProxySelector
系统提供了默认的ProxySelector子类作为代理选择器,开发者可以实现自己的代理选择器,程序可以通过继承ProxySelector来实现自己的代理选择器。继承ProxySelector需要重写两个方法:
–List<Proxy> select(URI uri):实现该方法让代理选择器根据不同的URI来使用不同的代理服务器,该方法就是代理选择器管理网络连接使用代理服务器的关键。
–connectFailed(URI uri, SocketAddress sa, IOException ioe):当系统通过默认的代理服务器建立连接失败后,代理选择器将会自动调用该方法。通过重写该方法可以对连接代理服务器失败的情形进行处理。