软件项目实训及课程设计指导——如何正确地创建和销毁软件应用系统中网络通讯中的Socket类的对象实例
1、基于TCP/IP协议的Socket通信相关的基础知识
(1)TCP/IP(Transmission Control Protocol传输控制协议/Internet Protocol网间协议)
TCP/IP是目前Internet网络中的主要协议,它定义了计算机和外设进行通信所使用的规则;TCP/IP网络参考模型包括五个层次:应用层、传输层、网络层、链路层、物理层。而ISO/OSI网络参考模型则包括七个层次:应用层、表示层、会话层、传输层、网络层、链路层、物理层。
(2)网络通讯中数据传输的模式
面向连接(TCP)——比如生活中的打电话
无连接(UDP,User Datagram Protocol)——比如发邮件、短信等
(3)端口号
在网络通讯中应用端口号达到区分服务的类别(该服务也就是服务器端相关程序所提供的功能),从而允许服务器端程序在同一时间内能够进行多个不同的网络会话,因为不同的网络会话都有对应的服务端口号,彼此相互区分。
如:WWW服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23,smtp服务的端口号为25,pop3服务的端口号为110等。
在程序中应用端口号时需要注意端口号的范围,端口号其实是一个16位无符号整数,数值范围是0-65535;低于256的端口号保留给标准的应用程序使用,比如pop3的端口号就是110。
2、Java语言中提供基于TCP/IP协议的Socket通信的技术支持
(1)了解什么是Socket(套接字)
网络中双向通讯程序中的某一端称为一个Socket,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。
(2)Java提供客户端套接字Socket类和服务器端套接字ServerSocket类
在JDK系统API库java.net包中提供有客户端套接字Socket类和服务器端套接字ServerSocket类,软件应用系统的开发人员可以应用在网络通信的客户端程序中构建出Socket类的对象实例,也就是当客户端程序需要与服务器端程序进行通讯时,客户端程序需要在客户机程序中创建一个Socket对象,Socket类有多种不同形式的构造方法。
在JDK API中与Socket通信有关的各个接口和功能类都定义在java.net包中,而在对应的服务器端程序中则需要创建出ServerSocket类的对象实例。
在Java程序中构建Socket类的对象实例需要提供服务器端主机的IP地址以及服务器端相关程序所提供的对外服务的端口号,从而使得不同的客户端程序应用IP地址可以区分不同的服务器主机,同时再通过端口号进一步区分在同一服务器主机中可能提供不同的功能服务。
(3)基于Socket通讯的主要过程
通讯的主要过程包括Socket的建立、监听、连接、发送数据和接收数据等环节。
3、Socket通讯实现中客户/服务器端通讯的主要方式及工作原理
(1)客户/服务器端通讯的主要方式
在网络通讯中的客户端和服务器端之间的通讯方式主要有如下的两种方式:
"一对一",也就是一个服务器对应一个客户机,此种通讯方式由于通讯的效率低下,目前已经不再应用;
"一对多",也就是一个服务器对应多个连接的客户机。为此,在服务器相关的程序中一般都要应用多线程的技术才能使得服务器程序可以同时响应不同的客户端程序的请求——也就是需要采用一个服务器端程序进程但构建出多个不同的线程。
(2)客户/服务器端通讯的工作原理如下示图所示
4、正确地应用Socket类的构造方法创建出Socket类的对象实例
如下示图为JDK API帮助文档中所提供的Socket类的各种构造方法的定义,但在Socket类中两个常用的构造方法分别是 Socket(InetAddress addr, int port) 和 Socket(String host, int port),它们都能够创建出一个基于Socket的连接服务器端流的套接字对象。
在程序中可以通过构造方法Socket(InetAddress addr, int port)内的第1个参数InetAddress类对象实例addr获得服务器端主机的IP地址,而在程序中通过构造方法Socket(String host, int port)中的第1个字符串类型参数host可以获得代表服务器端主机的名称字符串。
这两个构造方法都声明有抛出UnknownHostException异常类对象。下面为构造Socket类对象实例的代码片段示例:
try {Socket oneSocketObject =new Socket("服务器IP地址","端口号");}catch(UnknownHostException e){}catch (Ioexception e){}
5、如何获得服务器端主机及客户端主机的IP地址
由于Socket类对象实例可以出现在网络通讯中的客户端程序和服务器端程序中,因此在客户端和服务器端不同的程序中通过调用Socket类对象实例中的同一个方法但获得的结果却是不同的,读者在程序中应用时需要加以区分。
(1)在客户端程序代码中通过调用Socket类对象实例中的getInetAddress()方法可以获得远程服务器端主机的IP地址,而通过调用Socket类对象实例中的getPort()可以获得远程服务器端主机的端口号。
(2)在服务器端程序代码中通过调用Socket类对象实例中的getInetAddress()方法可以获得远程客户端主机的IP地址。
(3)在服务器端程序代码中通过调用Socket类中的getLocalAddress()可以获得服务器端本机的IP地址,而通过调用Socket类中的getLocalPort()可以获得服务器端本机的端口号。
(4)在客户端程序代码中通过调用Socket类中的getLocalAddress()可以获得客户端本机的IP地址,而通过调用Socket类中的getLocalPort()可以获得客户端本机的端口号(此端口号其实就是连接到服务器的端口号)。
6、获得服务器的主机名和对应的IP地址的程序代码示例
(1)依据服务器的主机名获得服务器的IP地址
InetAddress类存储远程系统的IP地址,如下代码片段示例是根据所提供的服务器端主机名称字符串"WWW-XUMIF9WPKIP "(可以为其它主机名称字符串或者包括本机"localhost"字符串等)获得对应的IP地址。
try{ InetAddress serverInetAddress = InetAddress.getByName("WWW-XUMIF9WPKIP "); System.out.println("服务器主机IP地址为:"+ serverInetAddress.toString());}catch (UnknownHostException e){}
对其中的serverInetAddress.toString()的调用代码也可以改用如下的语句System.out.println("服务器主机IP地址为:"+ serverInetAddress.getHostAddress());显示出服务器主机IP地址。程序代码执行的结果参见如下示图所示。
(2)依据服务器主机的IP地址获得服务器的主机名
在InetAddress类中提供有static 静态类型的public static InetAddress getByAddress(byte[] addr) throws UnknownHostException方法,该方法根据给定服务器主机IP 地址(由4字节的数组参数表示)返回 InetAddress 类型的对象实例。
如下代码示例是假定服务器主机的IP地址为192.168.0.1,然后再依据该IP地址获得对应的主机名称字符串的代码片段示例。
try{ byte[] serverHostIPByteArray = new byte[] { (byte) 192, (byte) 168, 0, 1 }; InetAddress serverInetAddress = InetAddress.getByAddress(serverHostIPByteArray); System.out.println("服务器主机名称为:"+ serverInetAddress.getHostName ());}catch (UnknownHostException e){}
7、如何正确地关闭Socket类的对象实例以及时释放所占的系统资源
当构造出Socket类的对象实例时,Java虚拟机JVM系统同样也需要为它分配一定的系统资源。因此,在软件应用系统程序中如果不再需要Socket类的对象实例时,应该及时地将它关闭掉以释放所占用的系统资源。
关闭Socket类的对象实例分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况。前者是指由本地客户端主机主动发起的关闭行为;而后者则是指本地主机检测到远程服务器主机发起的关闭行为之后,相应地也作出对应关闭的回应,从而关闭整个通讯中的连接。
下面为关闭Socket类对象实例的代码片段示例,并且要将这些程序代码放在finally 关键字所标识的语句块中以保证在程序中即使出现异常也能够正常地被执行:
finally { try{ /** 下面代码中的oneSocketInputObject对象代表Socket输入流对象实例 */ oneSocketInputObject.close(); /** 下面代码中的oneSocketOutputObject对象代表Socket输出流对象实例 */ oneSocketOutputObject.close(); /** 下面代码中的oneSocketObject对象代表Socket流对象实例 */ oneSocketObject.close(); } catch(IOException e){ }}
由于在通讯过程中需要发送和接收数据,因此在客户端还需要构造出Socket输入流对象实例(如示例代码中的oneSocketInputObject)和Socket输出流对象实例(如示例代码中的oneSocketOutputObject),在关闭Socket类对象实例之前首先应该分别关闭与Socket相关的所有输入/输出流对象实例,以释放所有的资源。
而且还要注意关闭代码的顺序——与Socket相关的所有输入/输出IO流对象实例应该要先关闭,然后再关闭Socket类对象实例本身。
尽管Java语言中提供有垃圾自动回收的机制,Socket通讯过程中所占用的各种网络资源最终是会被Java虚拟机JVM系统释放的。但是为了能够有效地利用系统的资源,建议读者还是按照合理的顺序在程序代码中主动释放这些资源,以避免出现内存泄漏现象。
在程序中如何正确地创建和销毁软件应用系统中文件IO流对象实例
如何应用Java反射技术灵活地创建程序类的对象实例
如何应用GOF设计模式中的构建者模式创建复合对象实例
如何合理地创建对象实例以降低程序类之间关系的耦合度
如何正确应用对象/关系映射技术实现系统持久层中各个DAO组件