套接字 Sockets
URLs 和 URLConnections 提供了一个相对较高的访问互联网资源的机制。 有时候,程序需要较低级别的网络通信,例如,要编写客户端 - 服务器应用程序时。
在客户端 - 服务器应用程序中,服务器提供一些服务,例如处理数据库查询或发送当前股票价格。 客户端使用服务器提供的服务,向用户显示数据库查询结果或向投资者提供股票购买建议。 客户端和服务器之间的通信必须可靠。也就是说,没有数据可以被丢弃,并且它必须按照服务器发送它的相同顺序到达客户端。
TCP 提供了一种可靠的点对点通信通道,即互联网上的客户端 - 服务器应用程序用来相互通信。 要通过 TCP 进行通信,客户端程序和服务器程序将建立彼此的连接。每个程序都将套接字绑定到连接的末尾。 为了进行通信,客户端和服务器分别读取和写入绑定到连接的套接字。
什么是套接字
通常,服务器运行在特定的计算机上,并且具有绑定到特定端口号的 socket 。 服务器只是在等待,监听客户端的套接字以发出连接请求。
在客户端:客户端知道服务器运行的机器的主机名以及服务器正在侦听的端口号。 要建立连接请求,客户端会尝试与服务器的机器和端口上的服务器汇合。客户端还需要向服务器标识自身, 以便绑定到在此连接期间将使用的本地端口号。这通常由系统分配。
如果一切顺利,服务器接受连接。一旦接受,服务器将获得绑定到同一本地端口的新套接字, 并且其远程端点将设置为客户端的地址和端口。它需要一个新的套接字,以便它可以继续监听原始套接字的连接请求, 同时适应所连接的客户端的需要。
在客户端,如果连接被接受,则成功创建套接字并且客户端可以使用套接字与服务器通信。 客户端和服务器现在可以通过写入或读取其套接字进行通信。
定义:一个套接字是在网络上运行两个程序之间的双向通信链路的一个端点。套接字绑定到端口号,以便 TCP 层可以识别数据将被发送到的应用程序。
端点是 IP 地址和端口号的组合。每个 TCP 连接可以由其两个端点唯一标识。这样你可以在主机和服务器之间建立多个连接。
在 java.net 包中的 Socket 实现了 Java 程序和网络上另一个程序之间的双向连接。
socket 位于平台相关的实现之上,隐藏了来自 Java 程序的任何特定系统的详细信息。 通过使用 java.net.socket 类而不是依赖于本机代码,您的 Java 程序可以以独立于平台的方式在网络上进行通信。
此外,java.net 还包括 ServerSocket 类,它实现了一个服务器可以用来侦听和接受客户端连接的套接字。 本课向您展示如何使用 Socket 和 ServerSocket 类。
如果您尝试连接到 Web,则 URL 类和相关类(URLConnection,URLEncoder)可能比套接字类更合适。 实际上,URL 是与 Web 相对较高级别的连接,并将套接字用作底层实现的一部分。
读取和写入
看一个简单的例子,它说明了一个程序如何使用 Socket 类建立到一个服务器程序的连接, 然后,客户端如何通过 Socket 向服务器发送数据并接收数据。
EchoClient 客户端;EchoServer 服务端,使用了 rfc862 Echo Protocol; 所以该服务端可以为所有基于 rfc862 协议的客户端服务;
EchoClient : 提供输入,并打印服务器把我们输入的返回回来的数据
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class EchoServer { public static void main(String[] args) throws IOException { args = new String[]{"9000"}; if (args.length != 1) { System.err.println("使用方法:java EchoServer <端口号>"); System.exit(1); } int portNumber = Integer.parseInt(args[0]); try ( ServerSocket serverSocket = new ServerSocket(Integer.parseInt(args[0])); Socket clientSocket = serverSocket.accept(); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); ) { String inputLine; while ((inputLine = in.readLine()) != null) { out.println(inputLine); } } catch (IOException e) { System.out.println("在端口上监听时捕获异常" + portNumber + "或者监听连接"); System.out.println(e.getMessage()); } } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class EchoClient { public static void main(String[] args) throws IOException { args = new String[]{"localhost", "9000"}; if (args.length != 2) { System.err.println("java EchoClient <主机名> <端口号>"); System.exit(1); } String hostName = args[0]; int portNumber = Integer.parseInt(args[1]); try ( Socket echoSocket = new Socket(hostName, portNumber); PrintWriter out = new PrintWriter(echoSocket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(echoSocket.getInputStream())); BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in)) ) { String userInput; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("echo:" + in.readLine()); } } catch (UnknownHostException e) { System.err.println("host " + hostName); System.exit(1); } catch (IOException e) { System.err.println("无法获得连接的I/O" + hostName); System.exit(1); } } }
代码很简单,就不啰嗦的翻译了;
这个客户端程序非常简单,因为 echo 服务器实现了一个简单的协议。客户端将文本发送到服务器, 服务器将其回送。当您的客户端程序正在与更复杂的服务器(如 HTTP 服务器)交谈时,您的客户端程序也将更加复杂。 然而,基础知识与他们在这个程序中的基本相同:
打开一个 socket。
打开输入流并输出流到 socket。
根据服务器的协议读取和写入流。
关闭流。
关闭 socket。
只有第 3 步因客户端而异,取决于服务器。其他步骤基本保持不变。