01.什么是Java的Socket技术?
Socket技术属于TCP(Transfer Control Protocol),是一种面向连接传输(Connection- oriented transmission)通信协议。这种通信协议首先建立计算机间的通信连接,然后再进行数据的传送和交换。Java的Socket编程技术以及API类Socket、ServerSocket,就是面向连接传输的具体体现。发送方(Socket对象),或者用户,和服务方(ServerSocket对象),或者服务器,必须首先建立连接,以便在TCP协议基础上实现通信。当一个用户端程序中的Socket对象发出连接请求,服务器端程序中的ServerSocket对象,可接受或者拒绝这个请求。一旦这两个Sockets对象实现连接,它们则可以进行双向数据传输,双方都可以进行数据发送和接收操作。这个解释也同样适用于应用Socket技术设计的多用户-服务器编程和多层次-多用户-服务器编程。
简言之,Sockets,即IP地址+端口号,也称套接字,是利用软件技术虚拟通信设备,通过端口进行计算机间的通信。它形象化地描述这个通信过程,如同把设备(用户端计算机)插入指定插座(服务器)一样容易。虽然Socket属于底层通信技术,但通过Java提供的API类,可实现不必了解底层通信详情,通过创建对象和调用适当的方法,进行用户-服务器应用软件开发和编程。Socket技术也是用户-服务器编程的基础,JSP(Java Server Pages)、RMI、Java EE以及其他Java网络编程技术,都基于Socket概念和技术。
与Sockets/TCP通讯技术相对应的是UDP(User Datagram Protocol),则是一种面向传输,即Transmission-oriented connection通信协议。UDP在数据通信过程中并不要求、也不保持计算机间的通信连接。用户计算机通过数据报表datagram,或邮包packet形式,发送请求给另外一台计算机后。双方的连接并不继续保持。每个邮包都有其字节长度、独立的发送方地址、接收方地址以及通信端口。它在网络上可能以任何路径进行传递并到达目的地。
02.、Sockets单用户-服务器编程
这个小节讨论基本Java网络编程技术。首先,我们将在Java程序中利用Socket与服务器进行通信,请求、获取并显示服务器发回的数据。还将进一步讨论HTTP通信协议和技术,学习Java在URL编程方面的应用实例。
Sockets单用户-服务器编程步骤
1. 设计、编写用户端程序。首先利用Socket编写用户端程序。创建Socket对象并且请求连接指定的服务器、调用Socket的方法,利用数据流技术发送对服务器的请求信息、并提取服务器传送回来的数据。
2. 设计、编写服务器端对应的服务程序。利用ServerSocket创建对象、调用其方法,利用数据流技术接受用户端的连接请求、得到用户端的请求信息、并发送所请求的数据。
代码实例:Sockets单用户-服务器编程
首先解释常用的利用Socket以及ServerSocket进行用户-服务器编程的API类。表1列出了java.net包中提供的常用Socket和ServerSocket的构造方法和方法。
表1常用Socket和ServerSocket构造方法和其他方法
注意:Socket和ServerSocket抛出检查性异常,程序中必须提供处理这些异常的代码。具体实例见下面的讨论。
下面的例子利用Socket和ServerSocket,模拟用户-服务器通信,将用户的英文输入,发送到服务器端程序,转换为大写字母,并将结果传回到用户屏幕。如下显示了这个例子的一个典型运行结果。第一行显示了服务器端程序运行、连接用户的成功。下方为用户端程序运行、输入请求、得到回答以及停止程序运行的对话通信过程:
Server: Welcome! The server is running.... Server: Type quit to STOP Client: java programming Server: JAVA PROGRAMMING Client: socket and sockectServer client-server programming Server: SOCKET AND SOCKECTSERVER CLIENT-SERVER PROGRAMMING Client: quit Server: Bye! Client: Now is disconnected...
如下是用户端的程序代码:
//这个程序存在本文压缩附件中名为SocketClientTest.java源代码 //Socket simple client application: connect to server to convert entries to upper case import java.io.*; import java.net.*; import java.util.*; public class SocketClientTest { public static void main(String[] args) { try { Socket clientSocket = new Socket("localhost", 1688); //本地计算机模拟;使用端口1688 InputStream inData = clientSocket.getInputStream(); //得到服务器输入流 OutputStream outData = clientSocket.getOutputStream(); //建立输出流至服务器 PrintWriter toServer = new PrintWriter(outData, true); //发送输出流 Scanner sc = new Scanner(System.in); //键盘输入扫描 Scanner data = new Scanner(inData); //服务器输入扫描 String heding = data.nextLine(); //得到服务器第一行输入信息 System.out.println(heading); //打印这行信息 while (sc.hasNextLine()) { //键盘输入循环 String line = sc.nextLine(); //得到键盘输入 toServer.println(line); //传送到服务器 String fromServer = data.nextLine(); //得到服务器回答 System.out.println(fromServer); //打印 if (fromServer.equals("Bye!")) { //如果传回结果为停止运行 System.out.println("Now is disconnected..."); break; } } clientSocket.close(); } catch (IOException e) { //处理检查性异常 e.printStackTrace(); } } }
这个用户端程序利用localhost和端口1688(可以是1024~65535之间的任何一个端口),进行用户-服务器之间的数据交流。在把用户从键盘输入的数据传送给服务器时,使用PrintWriter创建一个封装有Socket输出流至服务器的对象outData,并利用true作为选项,实现对输入流缓冲器的实时刷新。程序中还利用Scanner创建了封装有服务器端输入流inData的对象data,用来扫描从服务器传送过来的数据,并将其显示到屏幕上。
如同文件I/O,Socket抛出的异常为检查性异常。如果连接失败,Socket将抛出UnknownHostException以及IOException;其他Socket方法将抛出IOException。因为UnknownHostException是IOException的子类,代码中利用IOException来捕获所有的异常。
创建Socket对象将执行与指定服务器通过规定端口连接操作。这时,其他代码将暂停运行,直到连接完毕,或抛出连接异常。为了防止无终止等待,或控制等待时间,可以在代码中创建了clientSocket后,利用setSoTimeOut()方法,加入如下控制用户与服务器通信时间的语句:
clientSocket.setSoTimeout(1000); //设置连接时间为1秒
如下程序为服务器端代码:
//这个程序存在本文结尾压缩文件中名为SocketServerTest.java源代码 //Socket simple server application: convert client's entries to upper case //SocketServer code import java.io.*; import java.net.*; import java.util.*; public class SocketServerTest { public static void main(String[] args) { System.out.println("Welcome! The server is running..."); try { ServerSocket server = new ServerSocket(1688); //监控端口为1688 Socket fromClient = server.accept(); //接受用户的连接请求 InputStream inData = fromClient.getInputStream(); //得到用户输入流 OutputStream outData = fromClient.getOutputStream(); //得到用户输出流 PrintWriter toClient = new PrintWriter(outData, true); //创建输出流 toClient.println("Type quit to STOP"); //发送信息到用户 Scanner data = new Scanner(inData); //用户输入扫描 while (data.hasNextLine()) { String line = data.nextLine(); //得到用户输入数据 if (line.equalsIgnoreCase("quit")) { //如果是停止运行 server.close(); //关闭连接 toClient.println("Bye!"); //发送信息 break; } toClient.println(line.toUpperCase());//否则发送转换为大写字母信息 } } catch (IOException e) { e.printStackTrace(); } } }
服务器端程序首先利用ServerSocket创建有指定监控端口的对象,并调用accept()方法来接受任何从这个端口试图连接这个服务器的请求。代码其他部分与用户端程序相似,这里不再一一赘述。如果用户传送来的信息为quit,程序将关闭连接,停止运行。程序中调用String的toUpperCase()方法,把用户传送过来的信息,转换成大写字母,并将其传回至用户端。
注意:测试时,首先运行服务器端程序,再运行用户端代码。
Socket单用户-服务器程序测试运行步骤
建议你先在本地计算机模拟运行上节讨论的单用户-服务器程序,连接并调试程序的运行。其步骤如下:
1.在Eclipse中首先运行服务器端程序,如SocketServerTest。
2.在Eclipse中运行用户端程序,如SocketClientTest。
3.为更好模拟运行,可将用户端程序拷贝(注意不包括包名ch23)到一个本机文件夹中,如C:\Temp。打开一个操作系统窗口,打入如下编译指令:
javac SocketClientTest.java
再打入如下运行指令:
java SocketClientTest
以上步骤适合于本书所讨论的所有单用户-服务器程序测试。
你可以在本地计算机运行成功后,在用户端代码中将localhost(或127.0.0.1)改为作为服务器的计算机IP 地址,例如192.168.15.101,这样,就可在任何两个有网络连接的计算机上运行服务器端程序以及用户端程序。计算机的IP地址可用前面介绍过的指令ipconfig在操作系统窗口中获得。注意由于网络安全、联网设置和访问规范问题,需要对联网的计算机进行设置上的调整和更新。
如下是在有网络连接的计算机上测试的方法。其运行步骤如下:
1.在作为服务器的计算机的操作系统窗口中输入:
ipconfig指令,获得其IP地址,如:192.168.15.101。
2.将用户端代码中的localhost修改为作为服务器端计算机的IP地址。
3.运行服务器端程序。
4.在联网的另外一个计算机上运行用户端程序。