NIO之SocketChannel,SocketChannel ,DatagramChannel解读

目录

基本概述

ServerSocketChannel

打开 ServerSocketChannel

关闭 ServerSocketChannel

监听新的连接

阻塞模式 

非阻塞模式

SocketChannel 

 SocketChannel 介绍

SocketChannel 特征

创建 SocketChannel 

连接校验

读写模式

读写

DatagramChannel

打开 DatagramChannel

接收数据

发送数据

连接

DatagramChannel 示例


基本概述

(1)SocketChannel 就是 NIO 对于非阻塞 socket 操作的支持的组件,其在 socket 上 封装了一层,主要是支持了非阻塞的读写。同时改进了传统的单向流 API,,Channel同时支持读写。

(2)socket 通道类主要分为 DatagramChannel、SocketChannel 和 ServerSocketChannel,它们在被实例化时都会创建一个对等 socket 对象。要把一个 socket 通道置于非阻塞模式,我们要依靠所有 socket 通道类的公有超级类: SelectableChannel。就绪选择(readiness selection)是一种可以用来查询通道的 机制,该查询可以判断通道是否准备好执行一个目标操作,如读或写。非阻塞 I/O 和 可选择性是紧密相连的,那也正是管理阻塞模式的 API 代码要在 SelectableChannel 超级类中定义的原因。

(3)设置或重新设置一个通道的阻塞模式是很简单的,只要调用 configureBlocking( )方法即可,传递参数值为 true 则设为阻塞模式,参数值为 false 值设为非阻塞模式。可以通过调用 isBlocking( )方法来判断某个 socket 通道当前处于 哪种模式。

AbstractSelectableChannel.java 中实现的 configureBlocking()方法如下:

ServerSocketChannel

ServerSocketChannel 是一个基于通道的 socket 监听器。它同我们所熟悉的java.net.ServerSocket 执行相同的任务,不过它增加了通道语义,因此能够在非阻塞 模式下运行。

由于 ServerSocketChannel 没有 bind()方法,因此有必要取出对等的 socket 并使用 它来绑定到一个端口以开始监听连接。我们也是使用对等 ServerSocket 的 API 来根 据需要设置其他的 socket 选项。

同 java.net.ServerSocket 一样,ServerSocketChannel 也有 accept( )方法。 ServerSocketChannel 的 accept()方法会返回 SocketChannel 类型对象, SocketChannel 可以在非阻塞模式下运行。

以下代码演示了如何使用一个非阻塞的 accept( )方法:

public class FileChannelAccept {public static final String GREETING = "Hello java nio.\r\n";public static void main(String[] argv) throws Exception {int port = 1234; // defaultif (argv.length > 0) {port = Integer.parseInt(argv[0]);}ByteBuffer buffer = ByteBuffer.wrap(GREETING.getBytes());ServerSocketChannel ssc = ServerSocketChannel.open();ssc.socket().bind(new InetSocketAddress(port));ssc.configureBlocking(false);while (true) {System.out.println("Waiting for connections");SocketChannel sc = ssc.accept();if (sc == null) {System.out.println("null");Thread.sleep(2000);} else {System.out.println("Incoming connection from: " +sc.socket().getRemoteSocketAddress());buffer.rewind();sc.write(buffer);sc.close();}}}
}

 

打开 ServerSocketChannel

通过调用 ServerSocketChannel.open() 方法来打开 ServerSocketChannel.

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

关闭 ServerSocketChannel

通过调用 ServerSocketChannel.close() 方法来关闭 ServerSocketChannel.

serverSocketChannel.close();

监听新的连接

通过 ServerSocketChannel.accept() 方法监听新进的连接。当 accept()方法返回时候,它返回一个包含新进来的连接的 SocketChannel。因此, accept()方法会一直阻塞 到有新连接到达。

通常不会仅仅只监听一个连接,在 while 循环中调用 accept()方法. 如下面的例子:

阻塞模式 

会在 SocketChannel sc = ssc.accept();这里阻塞住进程。

非阻塞模式

ServerSocketChannel 可以设置成非阻塞模式。在非阻塞模式下,accept() 方法会立 刻返回,如果还没有新进来的连接,返回的将是 null。 因此,需要检查返回的 SocketChannel 是否是 null.如:

SocketChannel 

 SocketChannel 介绍

Java NIO 中的 SocketChannel 是一个连接到 TCP 网络套接字的通道。

A selectable channel for stream-oriented connecting sockets.

以上是 Java docs 中对于 SocketChannel 的描述:SocketChannel 是一种面向流连接 sockets 套接字的可选择通道。从这里可以看出:

SocketChannel 是用来连接 Socket 套接字

SocketChannel 主要用途用来处理网络 I/O 的通道

SocketChannel 是基于 TCP 连接传输

SocketChannel 实现了可选择通道,可以被多路复用的

SocketChannel 特征

  • 对于已经存在的 socket 不能创建 SocketChannel
  • SocketChannel 中提供的 open 接口创建的 Channel 并没有进行网络级联,需要使 用 connect 接口连接到指定地址
  • 未进行连接的 SocketChannle 执行 I/O 操作时,会抛出NotYetConnectedException
  • SocketChannel 支持两种 I/O 模式:阻塞式和非阻塞式
  • SocketChannel 支持异步关闭。如果 SocketChannel 在一个线程上 read 阻塞,另 一个线程对该 SocketChannel 调用 shutdownInput,则读阻塞的线程将返回-1 表示没有 读取任何数据;如果 SocketChannel 在一个线程上 write 阻塞,另一个线程对该 SocketChannel 调用 shutdownWrite,则写阻塞的线程将抛出AsynchronousCloseException
  • SocketChannel 支持设定参数
  1. SO_SNDBUF 套接字发送缓冲区大小
  2. SO_RCVBUF 套接字接收缓冲区大小
  3. SO_KEEPALIVE 保活连接
  4. O_REUSEADDR 复用地址
  5. SO_LINGER 有数据传输时延缓关闭 Channel (只有在非阻塞模式下有用)
  6. TCP_NODELAY 禁用 Nagle 算法

创建 SocketChannel 

方式一:

SocketChannel socketChannel = SocketChannel.open(newInetSocketAddress("www.baidu.com", 80));

方式二:

SocketChannel socketChanne2 = SocketChannel.open();
socketChanne2.connect(new InetSocketAddress("www.baidu.com", 80));

直接使用有参 open api 或者使用无参 open api,但是在无参 open 只是创建了一个SocketChannel 对象,并没有进行实质的 tcp 连接。 

连接校验

socketChannel.isOpen(); // 测试 SocketChannel 是否为 open 状态
socketChannel.isConnected(); //测试 SocketChannel 是否已经被连接
socketChannel.isConnectionPending(); //测试 SocketChannel 是否正在进行连接
socketChannel.finishConnect(); //校验正在进行套接字连接的 SocketChannel是否已经完成连接

读写模式

前面提到 SocketChannel 支持阻塞和非阻塞两种模式:

socketChannel.configureBlocking(false);

通过以上方法设置 SocketChannel 的读写模式。false 表示非阻塞,true 表示阻塞。 

读写

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
ByteBuffer byteBuffer = ByteBuffer.allocate(16);
socketChannel.read(byteBuffer);
socketChannel.close();
System.out.println("read over");

 以上为阻塞式读,当执行到 read 出,线程将阻塞,控制台将无法打印 read over

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
socketChannel.configureBlocking(false);
ByteBuffer byteBuffer = ByteBuffer.allocate(16);
socketChannel.read(byteBuffer);
socketChannel.close();
System.out.println("read over");

以上为非阻塞读,控制台将打印 read over 读写都是面向缓冲区,这个读写方式与前文中的FileChannel 相同。 

DatagramChannel

正如 SocketChannel 对应 Socket,ServerSocketChannel 对应 ServerSocket,每 一个 DatagramChannel 对象也有一个关联的 DatagramSocket 对象。正如 SocketChannel 模拟连接导向的流协议(如 TCP/IP),DatagramChannel 则模拟包 导向的无连接协议(如 UDP/IP)。DatagramChannel 是无连接的,每个数据报 (datagram)都是一个自包含的实体,拥有它自己的目的地址及不依赖其他数据报的 数据负载。与面向流的的 socket 不同,DatagramChannel 可以发送单独的数据报给 不同的目的地址。同样,DatagramChannel 对象也可以接收来自任意地址的数据包。 每个到达的数据报都含有关于它来自何处的信息(源地址)

打开 DatagramChannel

DatagramChannel server = DatagramChannel.open();
server.socket().bind(new InetSocketAddress(10086));

此例子是打开 10086 端口接收 UDP 数据包

接收数据

通过 receive()接收 UDP 包

ByteBuffer receiveBuffer = ByteBuffer.allocate(64);
receiveBuffer.clear();
SocketAddress receiveAddr = server.receive(receiveBuffer);

SocketAddress 可以获得发包的 ip、端口等信息,用 toString 查看,格式如下 /127.0.0.1:57126 

发送数据

通过 send()发送 UDP 包

DatagramChannel server = DatagramChannel.open();
ByteBuffer sendBuffer = ByteBuffer.wrap("client send".getBytes());
server.send(sendBuffer, new InetSocketAddress("127.0.0.1",10086));

连接

UDP 不存在真正意义上的连接,这里的连接是向特定服务地址用 read 和 write 接收发送数据包。

client.connect(new InetSocketAddress("127.0.0.1",10086));
int readSize= client.read(sendBuffer);
server.write(sendBuffer);

 read()和 write()只有在 connect()后才能使用,不然会抛 NotYetConnectedException 异常。用 read()接收时,如果没有接收到包,会抛 PortUnreachableException 异常。

DatagramChannel 示例

客户端发送,服务端接收的例子

    /*** 发包的 datagram** @throws IOException* @throws InterruptedException*/@Testpublic void sendDatagram() throws IOException, InterruptedException {DatagramChannel sendChannel= DatagramChannel.open();InetSocketAddress sendAddress= new InetSocketAddress("127.0.0.1",9999);while (true) {sendChannel.send(ByteBuffer.wrap("发包".getBytes("UTF-8")), sendAddress);System.out.println("发包端发包");Thread.sleep(1000);}}

   /*** 收包端** @throws IOException*/@Testpublic void receive() throws IOException {DatagramChannel receiveChannel= DatagramChannel.open();InetSocketAddress receiveAddress= new InetSocketAddress(9999);receiveChannel.bind(receiveAddress);ByteBuffer receiveBuffer= ByteBuffer.allocate(512);while (true) {receiveBuffer.clear();SocketAddress sendAddress= receiveChannel.receive(receiveBuffer);receiveBuffer.flip();System.out.print(sendAddress.toString() + " ");System.out.println(Charset.forName("UTF-8").decode(receiveBuffer));}}

 

    /*** 只接收和发送 9999 的数据包** @throws IOException*/@Testpublic void testConect1() throws IOException {DatagramChannel connChannel = DatagramChannel.open();connChannel.bind(new InetSocketAddress(9998));connChannel.connect(new InetSocketAddress("127.0.0.1", 9999));connChannel.write(ByteBuffer.wrap("发包".getBytes("UTF-8")));ByteBuffer readBuffer = ByteBuffer.allocate(512);while (true) {try {readBuffer.clear();connChannel.read(readBuffer);readBuffer.flip();System.out.println(Charset.forName("UTF-8").decode(readBuffer));} catch (Exception e) {}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/255812.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

未安装任何音频设备解决方案(2021/12/27)

联想小新,win10,驱动全部更新,在官网下载重装,cmd输入代码都没有用。 使用设置,点击轻松使用,点击音频卡住无响应。 喇叭红叉,右键显示扬声器安装程序unknown。然而扬声器界面显示工作正常。 …

保姆级教程!Windows右下角扬声器有红叉,点击声音设置输出显示“未安装任何音频输出设备”?

打开电脑,音频设备无法正常播放,扬声器处有红叉,F1~F4无法使用,在音频播放器中发现无音频输出设备,插上耳机也没有声音!这可能是因为扬声器驱动器未及时更新,或更新失败,按照以下教程…

Win10喇叭图标出现红叉提示“未安装任何音频输出设备“

如果驱动都安装了还是没有用,右击右下角喇叭图标→打开音量设置→管理声音设备→全都禁用→启用麦克风和扬声器

Win10小喇叭显示红叉,显示未找到输出设备的解决方式

最近遇到一个问题,重装win10系统之后,新系统右下角的小喇叭一直显示红叉,右击显示“扬声器安装程序unknown”,查了网上很多的教程,不是通过驱动精灵、鲁大师重新安装声卡驱动,就是将声卡驱动回退回原来版本…

“未安装任何音频输出设备”解决办法

安了个安卓模拟器,把电脑搞聋了,百度了好多办法没有用,最后在b站一个评论区解决了,记录一下以免以后再发生类似问题 问题: 小喇叭有个小红叉 解决: 打开设备管理器>展开“系统设备”>英特尔R智音…

华硕笔记本win10安装后喇叭红叉解决方案

朋友把华硕灵耀14S送到电脑店去装win10,结果喇叭红叉。显示未安装任何音频输出设备。店员打算拆机换声卡…然后朋友果断拒绝,找到了我 设备管理器里也没有声音控制器,系统设备里也找不到关于声音的物体…… 安装各种驱动精灵app,…

Windows系统,声音显示红色叉号,没声音修复

win7喇叭上显示红叉号,没声音,修复: 通常装了驱动后就没问题了,或者:右键点击喇叭—播放设备—右键单击:未安装音频设备, 左键点击:显示禁用设备,启用扬声器&#xff0c…

ThinkPad 声卡出现未安装任何音频输出设备

电脑型号: ThinkPad L14 (报错应该是与电脑无关的,windows下都可能出现此问题) 操作系统: Windows 10 64位 专业版 报错现象: windows自动安装了一个更新,然后就出现电脑无声音了。声音小喇…

win10音频服务器未修复,大神教你处理win10系统没声音音量图标红叉“音频服务未运行”的修复步骤...

大家在使用电脑工作的时候会遇到win10系统没声音音量图标红叉“音频服务未运行”的问题,尽管处理方法特别的容易,可是也有很多朋友不知道win10系统没声音音量图标红叉“音频服务未运行”究竟要怎么处理。有关如何解决win10系统没声音音量图标红叉“音频服…

WIN10 未安装任何音频驱动 扬声器有个小红叉(已解决)

经历了无数次的下载安装各种驱动,无数次重启,几乎试了全网所有的方法都是不行,最后使用了国外论坛的方法终于成功了 此方法必须要先重启电脑,重启后立刻进行此操作!! 我当时右击扬声器图标,选择…

自动缩放Kubernetes上的Kinesis Data Streams应用程序

想要学习如何在Kubernetes上自动缩放您的Kinesis Data Streams消费者应用程序,以便节省成本并提高资源效率吗?本文提供了一个逐步指南,教您如何实现这一目标。 通过利用Kubernetes对Kinesis消费者应用程序进行自动缩放,您可以从其…

PHP异步:在PHP中使用 fsockopen curl 实现类似异步处理的功能

PHP从主流来看,是一门面向过程的语言,它的最大缺点就是无法实现多线程管理,其程序的执行都是从头到尾,按照逻辑一路执行下来,不可能出现分支,这一点是限制php在主流程序语言中往更高级的语言发展的原因之一…

chatgpt赋能python:Python中转化为列表的详细介绍

Python中转化为列表的详细介绍 Python是一门高级编程语言,它使用起来简单易学,被广泛应用于大数据处理、科学计算、机器学习等领域。在Python编程中,列表是一种非常重要的数据结构,它允许我们存储和操作一组数据,并且…

keil的flash连接失败的原因

一 没有加载flash 添加所需要的驱动用的flash就行,看好自己设备的型号就能连上。 二 连接的端口有问题 一般这种情况直接换个USB接口就行,问题不大。注意stlink或者jlink的选择要和keil对应,在flash中可以设置。 1 点击魔法棒 2 选择Debug…

xp系统steam无法连接到更新服务器,教你win10系统steam更新失败的解决教程

steam是全球最大的游戏平台之一,它为广大游戏爱好者提供了游戏下载、购买、更新、讨论等多种功能,可是有时候会出现steam无法下载和更新不了的问题,怎么办?就此问题,小编整理了win10系统steam更新失败的解决教程,现分…

登录蒸汽平台显示连接服务器异常,蒸汽平台连接服务器失败

如果本地网络正常并且Steam硬盘空间足够,则可能是Steam问题或路由器配置问题. 由于STEAM的网络故障很多,而且非常烦人,因此官方网站提供了有关路由器配置问题的说明,您可以查看一下路由器的配置。 详细答案: 原因1: 您…

steam微信支付无法服务器,steam用微信支付失败怎么办

说起微信支付,相信我们每个人都不陌生,但是微信支付也有失败的时候,这时候我们就需要掌握正确的退款手续才行。steam用微信支付失败怎么办?了解网购安全,首先就要了解佰佰安全网小编就带您认识一下吧。 1、在付款提交订…

win10安装steam有损计算机,win10系统steam安装更新失败的解决方法

很多小伙伴都遇到过win10系统steam安装更新失败的困惑吧,一些朋友看过网上零散的win10系统steam安装更新失败的处理方法,并没有完完全全明白win10系统steam安装更新失败是如何解决的,今天小编准备了简单的解决办法,只需要按照  …

无法连接至远程计算机 pubg,绝地求生无法连接到steam网络完美解决办法

steam是一个大型游戏平台,汇集了国内外海量热门游戏,其中的绝地求生赫然在列。近期部分玩家在使用steam游戏平台的时候的会遇到部分意外情况,无法连接到steam网络怎么办是咨询量最大的一个问题。下面带来的是绝地求生无法连接到steam网络完美…

steam网络相关问题-社区错误代码118/无法自动登陆/短期内来自您网络的失败登录过多/无法连接至steam网络(2021/2/18更新)

文章首发及后续更新:https://mwhls.top/1560.html 新的更新内容请到mwhls.top查看。 无图/无目录/格式错误/更多相关请到上方的文章首发页面查看。 从电信宽带换成了移动宽带,突然出现了好多问题...但好在解决了,还有其它问题请留言。 2021/2…