今天就跟大家聊聊有关Java中如何进行TCP和套接字入门,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
JDK 提供了对 TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protocol,用户数据报协议)这两个数据传输协议的支持。本文开始探讨 TCP。
TCP 基础知识
在“服务器-客户端”这种架构中,服务器和客户端各自维护一个端点,两个端点需要通过网络进行数据交换。TCP 为这种需求提供了一种可靠的流式连接,流式的意思是传出和收到的数据都是连续的字节,没有对数据量进行大小限制。一个端点由 IP 地址和端口构成(专业术语为“元组 {IP 地址, 端口}”)。这样,一个连接就可以由元组 {本地地址, 本地端口, 远程地址, 远程端口} 来表示。
连接过程
在 TCP 编程接口中,端点体现为 TCP 套接字。共有两种 TCP 套接字:主动和被动,“被动”状态也常被称为“侦听”状态。服务器和客户端利用套接字进行连接的过程如下:
1、服务器创建一个被动套接字,开始循环侦听客户端的连接。
2、客户端创建一个主动套接字,连接服务器。
3、服务器接受客户端的连接,并创建一个代表该连接的主动套接字。
4、服务器和客户端通过步骤 2 和 3 中创建的两个主动套接字进行数据传输。
下面是连接过程的图解:
一个简单的 TCP 服务器
JDK 提供了 ServerSocket 类来代表 TCP 服务器的被动套接字。下面的代码演示了一个简单的 TCP 服务器(多线程阻塞模式),它不断侦听并接受客户端的连接,然后将客户端发送过来的文本按行读取,全文转换为大写后返回给客户端,直到客户端发送文本行 bye:
public class TcpServer implements Runnable { private ServerSocket serverSocket; public TcpServer(int port) throws IOException { // 创建绑定到某个端口的 TCP 服务器被动套接字。 serverSocket = new ServerSocket(port); } @Override public void run() { while (true) { try { // 以阻塞的方式接受一个客户端连接,返回代表该连接的主动套接字。 Socket socket = serverSocket.accept(); // 在新线程中处理客户端连接。 new Thread(new ClientHandler(socket)).start(); } catch (IOException ex) { ex.printStackTrace(); } } } } public class ClientHandler implements Runnable { private Socket socket; public ClientHandler(Socket socket) { this.socket = Objects.requireNonNull(socket); } @Override public void run() { try (Socket s = socket) { // 减少代码量的花招…… // 包装套接字的输入流以读取客户端发送的文本行。 BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream(), StandardCharsets.UTF_8)); // 包装套接字的输出流以向客户端发送转换结果。 PrintWriter out = new PrintWriter(new OutputStreamWriter( s.getOutputStream(), StandardCharsets.UTF_8), true); String line = null; while ((line = in.readLine()) != null) { if (line.equals("bye")) { break; } // 将转换结果输出给客户端。 out.println(line.toUpperCase(Locale.ENGLISH)); } } catch (IOException ex) { ex.printStackTrace(); } } }
阻塞模式的编程方式简单,但存在性能问题,因为服务器线程会卡死在接受客户端的 accept() 方法上,不能有效利用资源。套接字支持非阻塞模式,现在暂时略过。
一个简单的 TCP 客户端
JDK 提供了 Socket 类来代表 TCP 客户端的主动套接字。下面的代码演示了上述服务器的客户端:
public class TcpClient implements Runnable { private Socket socket; public TcpClient(String host, int port) throws IOException { // 创建连接到服务器的套接字。 socket = new Socket(host, port); } @Override public void run() { try (Socket s = socket) { // 再次减少代码量…… // 包装套接字的输出流以向服务器发送文本行。 PrintWriter out = new PrintWriter(new OutputStreamWriter( s.getOutputStream(), StandardCharsets.UTF_8), true); // 包装套接字的输入流以读取服务器返回的文本行。 BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream(), StandardCharsets.UTF_8)); Console console = System.console(); String line = null; while ((line = console.readLine()) != null) { if (line.equals("bye")) { break; } // 将文本行发送给服务器。 out.println(line); // 打印服务器返回的文本行。 console.writer().println(in.readLine()); } // 通知服务器关闭连接。 out.println("bye"); } catch (IOException ex) { ex.printStackTrace(); } } }
从 JDK 文档可以看到,ServerSocket 和 Socket 在初始化的时候,可以设定一些参数,还支持延迟绑定。这些东西对性能和行为都有所影响。后续两篇文章将分别详解这两个类的初始化。
看完上述内容,你们对Java中如何进行TCP和套接字入门有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。