4.2.2 Java网络编程
通常,Java网络程序建立在TCP/IP协议基础上,致力于实现应用层。传输层向应用层提供了套接字Socket接口,它封装了下层的数据传输细节;应用层的程序可通过Socket与远程主机建立连接和进行数据传输。
JDK提供了3种套接字类:java.net.Socket、java.net.ServerSocket和java.net.DatagramSocket。其中,java.net.Socket和java.net.ServerSocket类建立在TCP协议基础上,而java.net.DatagramSocket类则建立在UDP协议基础上。Java网络程序均采用客户机/服务器通信模式。下面介绍如何使用java.net.Socket和java.net.ServerSocket编写客户端和服务器端程序。
如图4-2所示,编写一个客户端程序需要以下3个步骤。
图 4-2 Java客户端/服务器端通信模型
步骤1 创建客户端Socket:
Socket soc=new Socket(serverHost, port);
其中,serverHost为服务器端的host, port为服务器端的监听端口号。一旦Socket创建成功,则表示客户端连接服务器成功。
步骤2 创建输出、输入流以向服务器端发送数据和从服务器端接收数据:
//构造数据输入流,用以接收数据
DataInputStream in=new DataInputStream(soc.getInputStream());
//构造数据输出流,用以发送数据
DataOutputStream out=new DataOutputStream(soc.getOutputStream());
……//应用程序发送和接收数据
步骤3 断开连接:
soc.close();
如图4-2所示,编写一个服务器端程序需要以下4个步骤。
步骤1 创建ServerSocket对象:
ServerSocket serverSocket=new ServerSocket(port);
其中,port为服务器端的监听端口号。当客户端向服务器端建立连接时,需要知道该端口号。创建ServerSocket对象成功后,操作系统将把当前进程注册为服务器进程。
步骤2 监听端口号,等待新连接到达:
Socket soc=serverSocket.accept();
运行函数accept()后,ServerSocket对象会一直处于监听状态,等待客户端的连接请求。一旦有客户端请求到达,该函数会返回一个Socket对象,该Socket对象与客户端Socket对象形成一条通信链路。
步骤3 创建输出、输入流以向客户端发送数据和从客户端接收数据。此处的程序和客户端的一样,故不再赘述。
步骤4 断开连接。此处的程序和客户端的一样,故不再赘述。
在Client/Server模型中,Server往往需要同时处理大量来自Client的访问请求,因此Server端需采用支持高并发访问的架构。一种简单而又直接的解决方案是“one-thread-per-connection”。这是一种基于阻塞式I/O的多线程模型,如图4-3所示。在该模型中,Server为每个Client连接创建一个处理线程,每个处理线程阻塞式等待可能到达的数据,一旦数据到达,则立即处理请求、返回处理结果并再次进入等待状态。由于每个Client连接有一个单独的处理线程为其服务,因此可保证良好的响应时间。但当系统负载增大(并发请求增多)时,Server端需要的线程数会增加,这将成为系统扩展的瓶颈所在。
图 4-3 旧I/O模型,多个线程阻塞等待客户端请求