基于Java NIO的Socket通信

Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础。

下面看看,Java实现的一个服务端和客户端通信的例子。

NIO模式的基本原理描述如下:

服务端打开一个通道(ServerSocketChannel),并向通道中注册一个选择器(Selector),这个选择器是与一些感兴趣的操作的标识(SelectionKey,即通过这个标识可以定位到具体的操作,从而进行响应的处理)相关联的,然后基于选择器(Selector)轮询通道(ServerSocketChannel)上注册的事件,并进行相应的处理。

客户端在请求与服务端通信时,也可以向服务器端一样注册(比服务端少了一个SelectionKey.OP_ACCEPT操作集合),并通过轮询来处理指定的事件,而不必阻塞。

下面的例子,主要以服务端为例,而客户端只是简单地发送请求数据和读响应数据。

服务端实现,代码如下所示:

[java] view
plain
copy

  1. package org.shirdrn.java.communications.nio;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.nio.ByteBuffer;
  5. import java.nio.channels.SelectionKey;
  6. import java.nio.channels.Selector;
  7. import java.nio.channels.ServerSocketChannel;
  8. import java.nio.channels.SocketChannel;
  9. import java.util.Iterator;
  10. import java.util.Set;
  11. import java.util.logging.Logger;
  12. /**
  13. * NIO服务端
  14. *
  15. * @author shirdrn
  16. */
  17. public class NioTcpServer extends Thread {
  18. private static final Logger log = Logger.getLogger(NioTcpServer.class.getName());
  19. private InetSocketAddress inetSocketAddress;
  20. private Handler handler = new ServerHandler();
  21. public NioTcpServer(String hostname, int port) {
  22. inetSocketAddress = new InetSocketAddress(hostname, port);
  23. }
  24. @Override
  25. public void run() {
  26. try {
  27. Selector selector = Selector.open(); // 打开选择器
  28. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 打开通道
  29. serverSocketChannel.configureBlocking(false); // 非阻塞
  30. serverSocketChannel.socket().bind(inetSocketAddress);
  31. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 向通道注册选择器和对应事件标识
  32. log.info("Server: socket server started.");
  33. while(true) { // 轮询
  34. int nKeys = selector.select();
  35. if(nKeys>0) {
  36. Set<SelectionKey> selectedKeys = selector.selectedKeys();
  37. Iterator<SelectionKey> it = selectedKeys.iterator();
  38. while(it.hasNext()) {
  39. SelectionKey key = it.next();
  40. if(key.isAcceptable()) {
  41. log.info("Server: SelectionKey is acceptable.");
  42. handler.handleAccept(key);
  43. } else if(key.isReadable()) {
  44. log.info("Server: SelectionKey is readable.");
  45. handler.handleRead(key);
  46. } else if(key.isWritable()) {
  47. log.info("Server: SelectionKey is writable.");
  48. handler.handleWrite(key);
  49. }
  50. it.remove();
  51. }
  52. }
  53. }
  54. } catch (IOException e) {
  55. e.printStackTrace();
  56. }
  57. }
  58. /**
  59. * 简单处理器接口
  60. *
  61. * @author shirdrn
  62. */
  63. interface Handler {
  64. /**
  65. * 处理{@link SelectionKey#OP_ACCEPT}事件
  66. * @param key
  67. * @throws IOException
  68. */
  69. void handleAccept(SelectionKey key) throws IOException;
  70. /**
  71. * 处理{@link SelectionKey#OP_READ}事件
  72. * @param key
  73. * @throws IOException
  74. */
  75. void handleRead(SelectionKey key) throws IOException;
  76. /**
  77. * 处理{@link SelectionKey#OP_WRITE}事件
  78. * @param key
  79. * @throws IOException
  80. */
  81. void handleWrite(SelectionKey key) throws IOException;
  82. }
  83. /**
  84. * 服务端事件处理实现类
  85. *
  86. * @author shirdrn
  87. */
  88. class ServerHandler implements Handler {
  89. @Override
  90. public void handleAccept(SelectionKey key) throws IOException {
  91. ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
  92. SocketChannel socketChannel = serverSocketChannel.accept();
  93. log.info("Server: accept client socket " + socketChannel);
  94. socketChannel.configureBlocking(false);
  95. socketChannel.register(key.selector(), SelectionKey.OP_READ);
  96. }
  97. @Override
  98. public void handleRead(SelectionKey key) throws IOException {
  99. ByteBuffer byteBuffer = ByteBuffer.allocate(512);
  100. SocketChannel socketChannel = (SocketChannel)key.channel();
  101. while(true) {
  102. int readBytes = socketChannel.read(byteBuffer);
  103. if(readBytes>0) {
  104. log.info("Server: readBytes = " + readBytes);
  105. log.info("Server: data = " + new String(byteBuffer.array(), 0, readBytes));
  106. byteBuffer.flip();
  107. socketChannel.write(byteBuffer);
  108. break;
  109. }
  110. }
  111. socketChannel.close();
  112. }
  113. @Override
  114. public void handleWrite(SelectionKey key) throws IOException {
  115. ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
  116. byteBuffer.flip();
  117. SocketChannel socketChannel = (SocketChannel)key.channel();
  118. socketChannel.write(byteBuffer);
  119. if(byteBuffer.hasRemaining()) {
  120. key.interestOps(SelectionKey.OP_READ);
  121. }
  122. byteBuffer.compact();
  123. }
  124. }
  125. public static void main(String[] args) {
  126. NioTcpServer server = new NioTcpServer("localhost", 1000);
  127. server.start();
  128. }
  129. }

客户端实现,代码如下所示:

[java] view
plain
copy

  1. package org.shirdrn.java.communications.nio;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.nio.ByteBuffer;
  5. import java.nio.channels.SocketChannel;
  6. import java.util.logging.Logger;
  7. /**
  8. * NIO客户端
  9. *
  10. * @author shirdrn
  11. */
  12. public class NioTcpClient {
  13. private static final Logger log = Logger.getLogger(NioTcpClient.class.getName());
  14. private InetSocketAddress inetSocketAddress;
  15. public NioTcpClient(String hostname, int port) {
  16. inetSocketAddress = new InetSocketAddress(hostname, port);
  17. }
  18. /**
  19. * 发送请求数据
  20. * @param requestData
  21. */
  22. public void send(String requestData) {
  23. try {
  24. SocketChannel socketChannel = SocketChannel.open(inetSocketAddress);
  25. socketChannel.configureBlocking(false);
  26. ByteBuffer byteBuffer = ByteBuffer.allocate(512);
  27. socketChannel.write(ByteBuffer.wrap(requestData.getBytes()));
  28. while (true) {
  29. byteBuffer.clear();
  30. int readBytes = socketChannel.read(byteBuffer);
  31. if (readBytes > 0) {
  32. byteBuffer.flip();
  33. log.info("Client: readBytes = " + readBytes);
  34. log.info("Client: data = " + new String(byteBuffer.array(), 0, readBytes));
  35. socketChannel.close();
  36. break;
  37. }
  38. }
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. public static void main(String[] args) {
  44. String hostname = "localhost";
  45. String requestData = "Actions speak louder than words!";
  46. int port = 1000;
  47. new NioTcpClient(hostname, port).send(requestData);
  48. }
  49. }

上述实现,NioTcpServer服务线程启动后,监听指定端口,等待客户端请求的到来,然后NioTcpClient客户端进程启动并发送请求数据,服务端接收到请求数据后,响应客户端(将请求的数据作为响应数据写回到客户端通道SocketChannel,并等待客户端处理)。

实际上,客户端和服务端可以采用同样轮询的非阻塞模式来实现,为简单实现在这个例子中我们把客户端角色简化了,而实际上它可能在另一个系统通信中充当服务端角色。

另外,上面对于不同事件是采用非线程的方式来处理,只是简单地调用处理的方法。在实际中,如果存在大量连接、读写请求,可以考虑使用线程池来更大程度地并发处理,提高服务端处理的速度和吞吐量,提升系统性能。

时间: 2024-08-03 15:31:35

基于Java NIO的Socket通信的相关文章

基于Java的TCP Socket通信详解(计算机端/Android手机端)

TCP Socket通信是一种比较常用的基于连接的网络通信方式.本文通过Java实现TCP Socket通信,并将其用于计算机端.Android手机端,同时做到代码规范化,实现代码最大化复用. 本文代码可在GitHub下载,建议对照源码阅读文章 https://github.com/jzj1993/JavaTcpSocket TCP连接的建立 客户端和服务器间通过三次握手建立TCP连接.在Java中,连接建立完成后,服务器端和客户端分别获取到一个Socket实例,之后就可以通过这个Socket实

Android基础入门教程——7.6.4 基于UDP协议的Socket通信

Android基础入门教程--7.6.4 基于UDP协议的Socket通信 标签(空格分隔): Android基础入门教程 本节引言: 本节给大家带来Socket的最后一节:基于UDP协议的Socket通信,在第一节中我们已经详细地 比较了两者的区别,TCP和UDP最大的区别在于是否需要客户端与服务端建立连接后才能进行 数据传输,如果你学了前两节TCP的,传输前先开服务端,accept,等客户端接入,然后获得 客户端socket然后进行IO操作,而UDP则不用,UDP以数据报作为数据的传输载体,

基于java.nio.channels的编程实践-II

介绍 为了提供并发处理效率,把用户的请求连接随机分配到线程池的线程进行处理,hbase也是采用同样的方式处理用户请求的 客户端代码可以参考:基于java.nio.channels的编程实践-I 代码 import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.chan

Android基础入门教程——7.6.3 基于TCP协议的Socket通信(2)

Android基础入门教程--7.6.3 基于TCP协议的Socket通信(2) 标签(空格分隔): Android基础入门教程 本节引言: 上节中我们给大家接触了Socket的一些基本概念以及使用方法,然后写了一个小猪简易聊天室的 Demo,相信大家对Socket有了初步的掌握,本节我们来学习下使用Socket来实现大文件的断点续传! 这里讲解的是别人写好的一个Socket上传大文件的例子,不要求我们自己可以写出来,需要的时候会用 就好! 1.运行效果图: 1.先把我们编写好的Socket服务

Android基础入门教程——7.6.2 基于TCP协议的Socket通信(1)

Android基础入门教程--7.6.2 基于TCP协议的Socket通信(1) 标签(空格分隔): Android基础入门教程 本节引言: 上一节的概念课枯燥无味是吧,不过总有点收获是吧,本节开始我们来研究基于TCP协议的Socket 通信,先来了解下Socket的概念,以及Socket通信的模型,实现Socket的步骤,以及作为Socket服务 端与客户端的两位各做要做什么事情!好的,我们由浅入深来扣这个Socket吧! 1.什么是Socket? 2.Socket通信模型: Socket通信

java网络编程Socket通信详解

Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Socket.像大家熟悉的QQ.MSN都使用了Socket相关的技术.下面就让我们一起揭开Socket的神秘面纱. Socket编程 一.网络基础知识(参考计算机网络)            关于计算机网络部分可以参考相关博客:           <TCP/IP协议栈及OSI参考模型详解> http://wangdy.blog.51cto.com/3845563/

android的NDK和java进行本地socket通信

关于Android应用与Framework的socket通信,相信关心这个问题的朋友们已经看过<android使用socket使底层和framework通信>这篇文章,美中不足的是作者只贴出一些关键的代码片段而并没有放出源码.我这里还是以一个能实际运行的例子为基础来讲,这样也方便大家学习. 首先看一下效果,如下图.我填写姓名"Potter",选择性别"Mr"然后点击发送,底层socket收到消息后将消息直接返回给我,我将返回的结果(Mr.Potter)直

基于Java NIO的多人在线聊天工具源码实现(登录,单聊,群聊)

近来在学习Java NIO网络开发知识,写了一个基于Java NIO的多人在线聊天工具练练手.源码公开在Coding上: https://coding.net/u/hust_wsh/p/MyChat/git ,开发环境是Ubuntu14.04+Eclipse Mars+JDK1.8. 要想编写一个基于Java NIO的多人在线聊天工具,我总结需要以下几方面的地址:客户端服务器模型,Java NIO中的Selector,SocketChannel,ByteBuffer,Collections以及序

基于NIO的Socket通信

一.NIO模式的基本原理: 服务端: 首先,服务端打开一个通道(ServerSocketChannel),并向通道中注册一个通道调度器(Selector):然后向通道调度器注册感兴趣的事件SelectionKey(如:OP_ACCEPT),接着就可以使用通道调度器(Selector)轮询通道(ServerSocketChannel)上注册的事件,并进行相应的处理. 客户端: 客户端在请求与服务端通信时,也可以像服务器端一样注册感兴趣的事件(比服务端少了SelectionKey.OP_ACCEPT