socket详解

AI绘画(可定制) 2016-06-03 05:50:28
套接字(socket)为两台计算机之间的通信提供了一种机制,在 James Gosling 注意到 Java 语言之前,套接字就早已赫赫有名。该语言只是让您不必了解底层操作系统的细节就能有效地使用套接字。多数着重讨论 Java 编码的书或者未涵盖这个主题,或者给读者留下很大的想象空间。

我们将专门讨论以下问题:
• 什么是套接字
• 它位于您可能要写的程序的什么地方
• 能工作的最简单的套接字实现 ―,以帮助您理解基础知识
• 详细剖析另外两个探讨如何在多线程和具有连接池环境中使用套接字的示例
• 简要讨论一个现实世界中的套接字应用程序
如果您能够描述如何使用 java.net 包中的类,那么本教程对您来说也许基础了点,虽然用它来提高一下还是不错的。如果您在 PC 和其它平台上使用套接字已经几年,那么最初的部分也许会使您觉得烦。但如果您不熟悉套接字,而且只是想知道什么是套接字以及如何在 Java 代码中有效地使用它们,那么本教程就是一个开始的好地方。

套接字基础
1. 介绍
多数程序员,不管他们是否使用 Java 语言进行编码,都不想很多知道关于不同计算机上的应用程序彼此间如何通信的低级细节。程序员们希望处理更容易理解的更高级抽象。Java 程序员希望能用他们熟悉的 Java 构造,通过直观接口与对象交互。
套接字在两个领域中都存在 ― 我们宁愿避开的低级细节和我们更愿处理的抽象层。本教程讨论的低级细节将只限于理解抽象应用程序所必须的部分。

2. 计算机组网 101
计算机以一种非常简单的方式进行相互间的操作和通信。计算机芯片是以 1 和 0 的形式存储并传输数据的开―闭转换器的集合。当计算机想共享数据时,它们所需做的全部就是以一致的速度、顺序、定时等等来回传输几百万比特和字节的数据流。每次想在两个应用程序之间进行信息通信时,您怎么会愿意担心那些细节呢?
为免除这些担心,我们需要每次都以相同方式完成该项工作的一组包协议。这将允许我们处理应用程序级的工作,而不必担心低级网络细节。这些成包协议称为协议栈(stack)。TCP/IP 是当今最常见的协议栈。多数协议栈(包括 TCP/IP)都大致对应于国际标准化组织(International Standards Organization,ISO)的开放系统互连参考模型(Open Systems Interconnect Reference Model,OSIRM)。OSIRM 认为在一个可靠的计算机组网中有七个逻辑层(见图)。各个地方的公司都对这个模型某些层的实现做了一些贡献,从生成电子信号(光脉冲、射频等等)到提供数据给应用程序。TCP/IP 映射到 OSI 模型中的两层的情形如图所示。
我们不想涉及层的太多细节,但您应该知道套接字位于什么地方。



3. 套接字位于什么地方
套接字大致驻留在 OSI 模型的会话层(见图)。会话层夹在其上面向应用的层和其下的实时数据通信层之间。会话层为两台计算机之间的数据流提供管理和控制服务。作为该层的一部分,套接字提供一个隐藏从导线上获取比特和字节的复杂性的抽象。换句话说,套接字允许我们让应用程序表明它想发送一些字节即可传输数据。套接字隐藏了完成该项工作的具体细节。
当您打电话时,您的声音传到传感器,传感器把它转换成可以传输的电数据。电话机是人与电信网络的接口。您无须知道声音如何传输的细节,只要知道想打电话给谁就行了。同样地,套接字扮演隐藏在未知通道上传输 1 和 0 的复杂性的高级接口的角色。



4. 把套接字暴露给应用程序
使用套接字的代码工作于表示层。表示层提供应用层能够使用的信息的公共表示。假设您打算把应用程序连接到只能识别 EBCDIC 的旧的银行系统。应用程序的域对象以 ASCII 格式存储信息。在这种情况下,您得负责在表示层上编写把数据从 EBCDIC 转换成 ASCII 的代码,然后(比方说)给应用层提供域对象。应用层然后就可以用域对象来做它想做的任何事情。
您编写的套接字处理代码只存在于表示层中。您的应用层无须知道套接字如何工作的任何事情。


...全文
104 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
5. 与主机交谈 这里我们实现 getFile() 方法,它将告诉服务器我们想要什么文件并在服务器传回其内容时接收该内容。 Java代码 1. public String getFile(String fileNameToGet) { 2. StringBuffer fileLines = new StringBuffer(); 3. 4. try { 5. socketWriter.println(fileNameToGet); 6. socketWriter.flush(); 7. 8. String line = null; 9. while ((line = socketReader.readLine()) != null) 10. fileLines.append(line + "\n"); 11. } catch (IOException e) { 12. System.out.println("Error reading from file: " + fileNameToGet); 13. } 14. 15. return fileLines.toString(); 16. } [java] view plain copy print? 1. public String getFile(String fileNameToGet) { 2. StringBuffer fileLines = new StringBuffer(); 3. 4. try { 5. socketWriter.println(fileNameToGet); 6. socketWriter.flush(); 7. 8. String line = null; 9. while ((line = socketReader.readLine()) != null) 10. fileLines.append(line + "\n"); 11. } catch (IOException e) { 12. System.out.println("Error reading from file: " + fileNameToGet); 13. } 14. 15. return fileLines.toString(); 16. } 对 getFile() 方法的调用要求一个有效的文件路径 String。它首先创建名为 fileLines 的 StringBuffer,fileLines 用于存储我们读自服务器上的文件的每一行。 Java代码 1. StringBuffer fileLines = new StringBuffer(); [java] view plain copy print? 1. StringBuffer fileLines = new StringBuffer(); 在 try{}catch{} 块中,我们用 PrintWriter 把请求发送到主机,PrintWriter 是我们在创建连接期间建立的。 Java代码 1. socketWriter.println(fileNameToGet); 2. socketWriter.flush(); [java] view plain copy print? 1. socketWriter.println(fileNameToGet); 2. socketWriter.flush(); 请注意这里我们是 flush() 该 PrintWriter,而不是关闭它。这迫使数据被发送到服务器而不关闭 Socket。 一旦我们已经写到 Socket,我们就希望有一些响应。我们不得不在 Socket 的 InputStream 上等待它,我们通过在 while 循环中调用 BufferedReader 上的readLine() 来达到这个目的。我们把每一个返回行附加到 fileLines StringBuffer(带有一个换行符以保护行): Java代码 1. String line = null; 2. while ((line = socketReader.readLine()) != null) 3. fileLines.append(line + "\n"); [java] view plain copy print? 1. String line = null; 2. while ((line = socketReader.readLine()) != null) 3. fileLines.append(line + "\n"); 6. 断开连接 这里我们实现 tearDownConnection() 方法,它将在我们使用完毕连接后负责“清除”: Java代码 1. public void tearDownConnection() { 2. try { 3. socketWriter.close(); 4. socketReader.close(); 5. } catch (IOException e) { 6. System.out.println("Error tearing down socket connection: " + e); 7. } 8. } [java] view plain copy print? 1. public void tearDownConnection() { 2. try { 3. socketWriter.close(); 4. socketReader.close(); 5. } catch (IOException e) { 6. System.out.println("Error tearing down socket connection: " + e); 7. } 8. } tearDownConnection() 方法只是分别关闭我们在 Socket 的 InputStream 和 OutputStream 上创建的 BufferedReader 和 PrintWriter。这样做会关闭我们从 Socket 获取的底层流,所以我们必须捕捉可能的 IOException。 7. 总结一下客户机 我们的类研究完了。在我们继续往前讨论服务器端的情况之前,让我们回顾一下创建和使用 Socket 的步骤: 1. 用您想连接的机器的 IP 地址和端口实例化 Socket(如有问题则抛出 Exception)。 2. 获取 Socket 上的流以进行读写。 3. 把流包装进 BufferedReader/PrintWriter 的实例,如果这样做能使事情更简单的话。 4. 对 Socket 进行读写。 5. 关闭打开的流。 附:RemoteFileClient 的代码清单 Java代码 1. import java.io.*; 2. import java.net.*; 3. 4. public class RemoteFileClient { 5. protected BufferedReader socketReader; 6. protected PrintWriter socketWriter; 7. protected String hostIp; 8. protected int hostPort; 9. 10. public RemoteFileClient(String aHostIp, int aHostPort) { 11. hostIp = aHostIp; 12. hostPort = aHostPort; 13. } 14. public String getFile(String fileNameToGet) { 15. StringBuffer fileLines = new StringBuffer(); 16. 17. try { 18. socketWriter.println(fileNameToGet); 19. socketWriter.flush(); 20. 21. String line = null; 22. while ((line = socketReader.readLine()) != null) 23. fileLines.append(line + "\n"); 24. } catch (IOException e) { 25. System.out.println("Error reading from file: " + fileNameToGet); 26. } 27. 28. return fileLines.toString(); 29. } 30. public static void main(String[] args) { 31. RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000); 32. remoteFileClient.setUpConnection(); 33. String fileContents = remoteFileClient.getFile("C:\\WINNT\\Temp\\RemoteFile.txt"); 34. remoteFileClient.tearDownConnection(); 35. 36. System.out.println(fileContents); 37. } 38. public void setUpConnection() { 39. try { 40. Socket client = new Socket(hostIp, hostPort); 41. 42. socketReader = new BufferedReader(new InputStreamReader(client.getInputStream())); 43. socketWriter = new PrintWriter(client.getOutputStream()); 44. 45. } catch (UnknownHostException e) { 46. System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort); 47. } catch (IOException e) { 48. System.out.println("Error setting up socket connection: " + e); 49. } 50. } 51. public void tearDownConnection() { 52. try { 53. socketWriter.close(); 54. socketReader.close(); 55. } catch (IOException e) { 56. System.out.println("Error tearing down socket connection: " + e); 57. } 58. } 59. } [java] view plain copy print? 1. import java.io.*; 2. import java.net.*; 3. 4. public class RemoteFileClient { 5. protected BufferedReader socketReader; 6. protected PrintWriter socketWriter; 7. protected String hostIp; 8. protected int hostPort; 9. 10. public RemoteFileClient(String aHostIp, int aHostPort) { 11. hostIp = aHostIp; 12. hostPort = aHostPort; 13. } 14. public String getFile(String fileNameToGet) { 15. StringBuffer fileLines = new StringBuffer(); 16. 17. try { 18. socketWriter.println(fileNameToGet); 19. socketWriter.flush(); 20. 21. String line = null; 22. while ((line = socketReader.readLine()) != null) 23. fileLines.append(line + "\n"); 24. } catch (IOException e) { 25. System.out.println("Error reading from file: " + fileNameToGet); 26. } 27. 28. return fileLines.toString(); 29. } 30. public static void main(String[] args) { 31. RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000); 32. remoteFileClient.setUpConnection(); 33. String fileContents = remoteFileClient.getFile("C:\\WINNT\\Temp\\RemoteFile.txt"); 34. remoteFileClient.tearDownConnection(); 35. 36. System.out.println(fileContents); 37. } 38. public void setUpConnection() { 39. try { 40. Socket client = new Socket(hostIp, hostPort); 41. 42. socketReader = new BufferedReader(new InputStreamReader(client.getInputStream())); 43. socketWriter = new PrintWriter(client.getOutputStream()); 44. 45. } catch (UnknownHostException e) { 46. System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort); 47. } catch (IOException e) { 48. System.out.println("Error setting up socket connection: " + e); 49. } 50. } 51. public void tearDownConnection() { 52. try { 53. socketWriter.close(); 54. socketReader.close(); 55. } catch (IOException e) { 56. System.out.println("Error tearing down socket connection: " + e); 57. } 58. } 59. }
  • 打赏
  • 举报
回复
我们在读完行之后关闭 BufferedReader: Java代码 1. reader.close(); [java] view plain copy print? 1. reader.close(); 如果提供给 URL 构造器的 urlString 是无效的,那么将抛出 MalformedURLException。如果发生了别的错误,例如当从连接上获取 InputStream 时,那么将抛出 IOException。 6. 总结 实际上,URLConnection 使用套接字从我们指定的 URL 中读取信息(它只是解析成 IP 地址),但我们无须了解它,我们也不关心。但有很多事;我们马上就去看看。 在继续往前讲之前,让我们回顾一下创建和使用 URLConnection 的步骤: 1. 用您想连接的资源的有效 URL String 实例化一个 URL(如有问题则抛出 MalformedURLException)。 2. 打开该 URL 上的一个连接。 3. 把该连接的 InputStream 包装进 BufferedReader 以使您能够读取行。 4. 用 BufferedReader 读文档。 5. 关闭 BufferedReader。 附: URLClient 的完整代码清单: Java代码 1. import java.io.*; 2. import java.net.*; 3. 4. public class URLClient { 5. protected HttpURLConnection connection; 6. public String getDocumentAt(String urlString) { 7. StringBuffer document = new StringBuffer(); 8. try { 9. URL url = new URL(urlString); 10. URLConnection conn = url.openConnection(); 11. BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 12. 13. String line = null; 14. while ((line = reader.readLine()) != null) 15. document.append(line + "\n"); 16. 17. reader.close(); 18. } catch (MalformedURLException e) { 19. System.out.println("Unable to connect to URL: " + urlString); 20. } catch (IOException e) { 21. System.out.println("IOException when connecting to URL: " + urlString); 22. } 23. 24. return document.toString(); 25. } 26. public static void main(String[] args) { 27. URLClient client = new URLClient(); 28. String yahoo = client.getDocumentAt("http://www.yahoo.com"); 29. 30. System.out.println(yahoo); 31. } 32. } 一个简单示例 1. 背景 我们将在本部分讨论的示例将阐明在 Java 代码中如何使用 Socket 和 ServerSocket。客户机用 Socket 连接到服务器。服务器用 ServerSocket 在端口 3000 侦听。客户机请求服务器 C: 驱动器上的文件内容。 为清楚起见,我们把示例分解成客户机端和服务器端。最后我们将把它们组合起来以使您能看到整体模样。 我们在使用 JDK 1.2 的 IBM VisualAge for Java 3.5 上开发这些代码。要自己创建这个示例,您应有完好的 JDK 1.1.7 或更高版本。客户机和服务器将只在一台机器上运行,所以您不必担心是否有一个可用的网络。 2. 创建 RemoteFileClient 类 这里是 RemoteFileClient 类的结构: Java代码 1. import java.io.*; 2. import java.net.*; 3. 4. public class RemoteFileClient { 5. protected String hostIp; 6. protected int hostPort; 7. protected BufferedReader socketReader; 8. protected PrintWriter socketWriter; 9. 10. public RemoteFileClient(String aHostIp, int aHostPort) { 11. hostIp = aHostIp; 12. hostPort = aHostPort; 13. } 14. public static void main(String[] args) { 15. } 16. public void setUpConnection() { 17. } 18. public String getFile(String fileNameToGet) { 19. } 20. public void tearDownConnection() { 21. } 22. } [java] view plain copy print? 1. import java.io.*; 2. import java.net.*; 3. 4. public class RemoteFileClient { 5. protected String hostIp; 6. protected int hostPort; 7. protected BufferedReader socketReader; 8. protected PrintWriter socketWriter; 9. 10. public RemoteFileClient(String aHostIp, int aHostPort) { 11. hostIp = aHostIp; 12. hostPort = aHostPort; 13. } 14. public static void main(String[] args) { 15. } 16. public void setUpConnection() { 17. } 18. public String getFile(String fileNameToGet) { 19. } 20. public void tearDownConnection() { 21. } 22. } 首先我们导入 java.net 和 java.io。java.net 包为您提供您需要的套接字工具。java.io 包为您提供对流进行读写的工具,这是您与 TCP 套接字通信的唯一途径。 我们给我们的类实例变量以支持对套接字流的读写和存储我们将连接到的远程主机的详细信息。 我们类的构造器有两个参数:远程主机的 IP 地址和端口号各一个,而且构造器将它们赋给实例变量。 我们的类有一个 main() 方法和三个其它方法。稍后我们将探究这些方法的细节。现在您只需知道 setUpConnection() 将连接到远程服务器,getFile() 将向远程服务器请求 fileNameToGet 的内容以及 tearDownConnection() 将从远程服务器上断开。 3. 实现 main() 这里我们实现 main() 方法,它将创建 RemoteFileClient 并用它来获取远程文件的内容,然后打印结果: Java代码 1. public static void main(String[] args) { 2. RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000); 3. remoteFileClient.setUpConnection(); 4. String fileContents = 5. remoteFileClient.getFile("C:\\WINNT\\Temp\\RemoteFile.txt"); 6. remoteFileClient.tearDownConnection(); 7. 8. System.out.println(fileContents); 9. } [java] view plain copy print? 1. public static void main(String[] args) { 2. RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000); 3. remoteFileClient.setUpConnection(); 4. String fileContents = 5. remoteFileClient.getFile("C:\\WINNT\\Temp\\RemoteFile.txt"); 6. remoteFileClient.tearDownConnection(); 7. 8. System.out.println(fileContents); 9. } main() 方法用主机的 IP 地址和端口号实例化一个新 RemoteFileClient(客户机)。然后,我们告诉客户机建立一个到主机的连接(稍后有更详细的讨论)。接着,我们告诉客户机获取主机上一个指定文件的内容。最后,我们告诉客户机断开它到主机的连接。我们把文件内容打印到控制台,只是为了证明一切都是按计划进行的。 4. 建立连接 这里我们实现 setUpConnection() 方法,它将创建我们的 Socket 并让我们访问该套接字的流: Java代码 1. public void setUpConnection() { 2. try { 3. Socket client = new Socket(hostIp, hostPort); 4. 5. socketReader = new BufferedReader( 6. new InputStreamReader(client.getInputStream())); 7. socketWriter = new PrintWriter(client.getOutputStream()); 8. 9. } catch (UnknownHostException e) { 10. System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort); 11. } catch (IOException e) { 12. System.out.println("Error setting up socket connection: " + e); 13. } 14. } [java] view plain copy print? 1. public void setUpConnection() { 2. try { 3. Socket client = new Socket(hostIp, hostPort); 4. 5. socketReader = new BufferedReader( 6. new InputStreamReader(client.getInputStream())); 7. socketWriter = new PrintWriter(client.getOutputStream()); 8. 9. } catch (UnknownHostException e) { 10. System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort); 11. } catch (IOException e) { 12. System.out.println("Error setting up socket connection: " + e); 13. } 14. } setUpConnection() 方法用主机的 IP 地址和端口号创建一个 Socket: Java代码 1. Socket client = new Socket(hostIp, hostPort); [java] view plain copy print? 1. Socket client = new Socket(hostIp, hostPort); 我们把 Socket 的 InputStream 包装进 BufferedReader 以使我们能够读取流的行。然后,我们把 Socket 的 OutputStream 包装进 PrintWriter 以使我们能够发送文件请求到服务器: Java代码 1. socketReader = new BufferedReader(new InputStreamReader(client.getInputStream())); 2. socketWriter = new PrintWriter(client.getOutputStream()); [java] view plain copy print? 1. socketReader = new BufferedReader(new InputStreamReader(client.getInputStream())); 2. socketWriter = new PrintWriter(client.getOutputStream()); 请记住我们的客户机和服务器只是来回传送字节。客户机和服务器都必须知道另一方即将发送的是什么以使它们能够作出适当的响应。在这个案例中,服务器知道我们将发送一条有效的文件路径。 当您实例化一个 Socket 时,将抛出 UnknownHostException。这里我们不特别处理它,但我们打印一些信息到控制台以告诉我们发生了什么错误。同样地,当我们试图获取 Socket 的 InputStream 或 OutputStream 时,如果抛出了一个一般 IOException,我们也打印一些信息到控制台。这是本教程的一般做法。在产品代码中,我们应该做得更完善些。
  • 打赏
  • 举报
回复
5. 什么是套接字? 既然我们已经知道套接字扮演的角色,那么剩下的问题是:什么是套接字?Bruce Eckel 在他的《Java 编程思想》一书中这样描述套接字: 套接字是一种软件抽象,用于表达两台机器之间的连接“终端”。对于一个给定的连接,每台机器上都有一个套接字,您也可以想象它们之间有一条虚拟的“电缆”,“电缆”的每一端都插入到套接字中。当然,机器之间的物理硬件和电缆连接都是完全未知的。抽象的全部目的是使我们无须知道不必知道的细节。 简言之,一台机器上的套接字与另一台机器上的套接字交谈就创建一条通信通道。程序员可以用该通道来在两台机器之间发送数据。当您发送数据时,TCP/IP 协议栈的每一层都会添加适当的报头信息来包装数据。这些报头帮助协议栈把您的数据送到目的地。好消息是 Java 语言通过"流"为您的代码提供数据,从而隐藏了所有这些细节,这也是为什么它们有时候被叫做流套接字(streaming socket)的原因。 把套接字想成两端电话上的听筒 ― 我和您通过专用通道在我们的电话听筒上讲话和聆听。直到我们决定挂断电话,对话才会结束(除非我们在使用蜂窝电话)。而且我们各自的电话线路都占线,直到我们挂断电话。 如果想在没有更高级机制如 ORB(以及 CORBA、RMI、IIOP 等等)开销的情况下进行两台计算机之间的通信,那么套接字就适合您。套接字的低级细节相当棘手。幸运的是,Java 平台给了您一些虽然简单但却强大的更高级抽象,使您可以容易地创建和使用套接字。 6. 套接字的类型 一般而言,Java 语言中的套接字有以下两种形式: • TCP 套接字(由 Socket 类实现,稍后我们将讨论这个类) • UDP 套接字(由 DatagramSocket 类实现) TCP 和 UDP 扮演相同角色,但做法不同。两者都接收传输协议数据包并将其内容向前传送到表示层。TCP 把消息分解成数据包(数据报,datagrams)并在接收端以正确的顺序把它们重新装配起来。TCP 还处理对遗失数据包的重传请求。有了 TCP,位于上层的层要担心的事情就少多了。UDP 不提供装配和重传请求这些功能。它只是向前传送信息包。位于上层的层必须确保消息是完整的并且是以正确的顺序装配的。 一般而言,UDP 强加给您的应用程序的性能开销更小,但只在应用程序不会突然交换大量数据并且不必装配大量数据报以完成一条消息的时候。否则,TCP 才是最简单或许也是最高效的选择。 因为多数读者都喜欢 TCP 胜过 UDP,所以我们将把讨论限制在 Java 语言中面向 TCP 的类。 一个秘密的套接字 1. 介绍 Java 平台在 java.net 包中提供套接字的实现。在本教程中,我们将与 java.net 中的以下三个类一起工作: • URLConnection • Socket • ServerSocket java.net 中还有更多的类,但这些是您将最经常碰到的。让我们从 URLConnection 开始。这个类为您不必了解任何底层套接字细节就能在 Java 代码中使用套接字提供一种途径。 2. 甚至不用尝试就可使用套接字 URLConnection 类是所有在应用程序和 URL 之间创建通信链路的类的抽象超类。URLConnection 在获取 Web 服务器上的文档方面特别有用,但也可用于连接由 URL 标识的任何资源。该类的实例既可用于从资源中读,也可用于往资源中写。例如,您可以连接到一个 servlet 并发送一个格式良好的 XML String到服务器上进行处理。URLConnection 的具体子类(例如 HttpURLConnection)提供特定于它们实现的额外功能。对于我们的示例,我们不想做任何特别的事情,所以我们将使用 URLConnection 本身提供的缺省行为。 连接到 URL 包括几个步骤: • 创建 URLConnection • 用各种 setter 方法配置它 • 连接到 URL • 用各种 getter 方法与它交互 接着,我们将看一些演示如何用 URLConnection 来从服务器请求文档的样本代码 3. URLClient 类 我们将从 URLClient 类的结构讲起。 Java代码 1. import java.io.*; 2. import java.net.*; 3. 4. public class URLClient { 5. protected URLConnection connection; 6. 7. public static void main(String[] args) { 8. } 9. public String getDocumentAt(String urlString) { 10. } 11. } [java] view plain copy print? 1. import java.io.*; 2. import java.net.*; 3. 4. public class URLClient { 5. protected URLConnection connection; 6. 7. public static void main(String[] args) { 8. } 9. public String getDocumentAt(String urlString) { 10. } 11. } 要做的第一件事是导入 java.net 和 java.io。 我们给我们的类一个实例变量以保存一个 URLConnection。 我们的类有一个 main() 方法,它处理浏览文档的逻辑流。我们的类还有一个 getDocumentAt() 方法,该方法连接到服务器并向它请求给定文档。下面我们将分别探究这些方法的细节。 4. 浏览文档 main() 方法处理浏览文档的逻辑流: Java代码 1. public static void main(String[] args) { 2. URLClient client = new URLClient(); 3. String yahoo = client.getDocumentAt("http://www.yahoo.com"); 4. System.out.println(yahoo); 5. } [java] view plain copy print? 1. public static void main(String[] args) { 2. URLClient client = new URLClient(); 3. String yahoo = client.getDocumentAt("http://www.yahoo.com"); 4. System.out.println(yahoo); 5. } 我们的 main() 方法只是创建一个新的 URLClient 并用一个有效的 URL String 调用 getDocumentAt()。当调用返回该文档时,我们把它存储在 String,然后将它打印到控制台。然而,实际的工作是在 getDocumentAt() 方法中完成的。 5. 从服务器请求一个文档 getDocumentAt() 方法处理获取 Web 上的文档的实际工作: Java代码 1. public String getDocumentAt(String urlString) { 2. StringBuffer document = new StringBuffer(); 3. try { 4. URL url = new URL(urlString); 5. URLConnection conn = url.openConnection(); 6. BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 7. 8. String line = null; 9. while ((line = reader.readLine()) != null) 10. document.append(line + "\n"); 11. reader.close(); 12. } catch (MalformedURLException e) { 13. System.out.println("Unable to connect to URL: " + urlString); 14. } catch (IOException e) { 15. System.out.println("IOException when connecting to URL: " + urlString); 16. } 17. return document.toString(); 18. } [java] view plain copy print? 1. public String getDocumentAt(String urlString) { 2. StringBuffer document = new StringBuffer(); 3. try { 4. URL url = new URL(urlString); 5. URLConnection conn = url.openConnection(); 6. BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 7. 8. String line = null; 9. while ((line = reader.readLine()) != null) 10. document.append(line + "\n"); 11. reader.close(); 12. } catch (MalformedURLException e) { 13. System.out.println("Unable to connect to URL: " + urlString); 14. } catch (IOException e) { 15. System.out.println("IOException when connecting to URL: " + urlString); 16. } 17. return document.toString(); 18. } getDocumentAt() 方法有一个 String 参数,该参数包含我们想获取的文档的 URL。我们在开始时创建一个 StringBuffer 来保存文档的行。然后我们用我们传进去的 urlString 创建一个新 URL。接着创建一个 URLConnection 并打开它: Java代码 1. URLConnection conn = url.openConnection(); [java] view plain copy print? 1. URLConnection conn = url.openConnection(); 一旦有了一个 URLConnection,我们就获取它的 InputStream 并包装进 InputStreamReader,然后我们又把 InputStreamReader 包装进 BufferedReader 以使我们能够读取想从服务器上获取的文档的行。在 Java 代码中处理套接字时,我们将经常使用这种包装技术,但我们不会总是详细讨论它。在我们继续往前讲之前,您应该熟悉它: Java代码 1. BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); [java] view plain copy print? 1. BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 有了 BufferedReader,就使得我们能够容易地读取文档内容。我们在 while 循环中调用 reader 上的 readLine(): Java代码 1. String line = null; 2. while ((line = reader.readLine()) != null) 3. document.append(line + "\n"); [java] view plain copy print? 1. String line = null; 2. while ((line = reader.readLine()) != null) 3. document.append(line + "\n"); 对 readLine() 的调用将直至碰到一个从 InputStream 传入的行终止符(例如换行符)时才阻塞。如果没碰到,它将继续等待。只有当连接被关闭时,它才会返回 null。在这个案例中,一旦我们获取一个行(line),我们就把它连同一个换行符一起附加(append)到名为 document 的 StringBuffer 上。这保留了服务器端上读取的文档的格式。

23,405

社区成员

发帖
与我相关
我的任务
社区描述
Java 非技术区
社区管理员
  • 非技术区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧