AIO实现非阻塞通信

java7 NIO2 提供了异步Channel支持,这种异步Channel可以提供更高效的IO,这种基于异步Channel的IO被称为异步IO(Asynchronous IO)

IO操作分为两步:1、程序发出IO请求  2、完成实际的IO操作

阻塞和非阻塞IO是根据第一步划分的:

发出IO请求如果阻塞线程则是阻塞IO,如果不阻塞线程,则是非阻塞IO。

同步IO和异步IO是根据第二步划分:

如果实际的IO操作是由操作系统完成,再将结果返回给应用程序,这就是异步IO。

如果实际的IO需要应用程序本身去执行,会阻塞线程,那就是同步IO。

(java传统的IO操作和基于Channel的非阻塞IO都是同步IO)

NIO2提供了一系列以Asynchronous开头的Channel接口和类。

其中AsynchronousSocketChannel、AsynchronousServerSocketChannel是支持TCP通信的异步Channel。

AsynchronousServerSocketChannel:负责监听的Channel,与ServerSocketChannel相似。

AsynchronousServerSocketChannel使用需要三步:

1)调用open()静态方法创建AsynchronousServerSocketChannel实例

2)调用AsynchronousServerSocketChannel的bind()方法让他在指定IP,端口监听。

3)调用AsynchronousServerSocketChannel的accept()方法接收连接请求。

AsynchronousSocketChannel:与SocketChannel类似,执行具体的IO操作

AsynchronousSocketChannel的用法也可以分为三步:

1)调用Open()静态方法创建AsynchronousSocketChannel实例

2)调用AsynchronousSocketChannel的connect()方法让他在指定IP,端口服务器。

3)调用AsynchronousSocketChannel的read()、write()方法进行读写。

AsynchronousServerSocketChannel、AsynchronousSocketChannel都允许使用线程池管理,open()方法创建对应实例时都可以传入AsynchronousChannelGroup。AsynchronousChannelGroup创建需要传入一个线程池ExecutorService。

AsynchronousServerSocketChannel的accept()方法、AsynchronousSocketChannel的read()、write()方法都有两个版本

1)返回Future对象版本:必须等到Future的get方法返回时IO操作才完成,get方法会阻塞线程的。

2)需要传入CompletionHandler版本:通过ComplctionHandler完成相关操作。

CompletionHandler是一个接口,该接口中定义了两个方法:

completed(V result,A attachment):当IO操作完成时触发该方法,第一参数表示IO操作返回的参数;第二个参数表示发起IO操作时传入的附加参数。

failed(Trowable exc,A attachment):当IO操作失败事触发该方法,第一参数表示异常信息,,第二个参数表示发起IO操作时传入的附加参数。

下面使用Future对象版本实现简单的AIO服务端、客户端通信:

package net;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.Future; public class SimpleAIOServer { static final int PORT = 30000; public static void main(String[] args) throws Exception { try (//创建AsynchronousServerSocketChannel实例
AsynchronousServerSocketChannel serverSocketChannel =
AsynchronousServerSocketChannel.open();)
{
serverSocketChannel.bind(new InetSocketAddress(PORT));
while(true) {
//采用循环接收客户端的连接
Future<AsynchronousSocketChannel> future = serverSocketChannel.accept();
//获取连接后返回AsynchronousSocketChannel
AsynchronousSocketChannel socketChannel = future.get(); //向客户端输出数据
Future<Integer> future1 =socketChannel.write(ByteBuffer.wrap("AIO HELLO".
getBytes("UTF-8")));
future1.get();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package net; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.Future; public class SimpleAIOClient { static final int PORT = 30000; public static void main(String[] args) throws Exception { //用户读取数据的Buffer
ByteBuffer buff = ByteBuffer.allocate(1024);
Charset charset = Charset.forName("UTF-8");
try(//创建AsynchronousSocketChannel实例
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();)
{
//连接到远程服务器
Future<Void> future = socketChannel.connect(new InetSocketAddress("127.0.0.1", PORT));
future.get(); buff.clear();
//socketChannel中读取数据
Future<Integer> future1 = socketChannel.read(buff);
future1.get(); buff.flip();
String content = charset.decode(buff).toString();
System.out.println("服务器:" + content); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

结果:

服务器:AIO HELLO

AIO实现多人聊天

package net;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class AIOServer { static final int PORT = 30000;
static List<AsynchronousSocketChannel> channelList = new ArrayList<>(); public void init() throws IOException {
//创建一个线程池
ExecutorService executor = Executors.newFixedThreadPool(20);
//以指定线程池创建分组管理器
AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(executor);
//以线程池创建AsynchronousServerSocketChannel
AsynchronousServerSocketChannel serverSocketChannel =
AsynchronousServerSocketChannel.open(channelGroup);
//绑定端口
serverSocketChannel.bind(new InetSocketAddress(PORT));
//使用CompletionHandler处理客户端连接请求,此处的Handler主要处理客户端连接请求
serverSocketChannel.accept(null, new AcceptHandler(serverSocketChannel)); } public static void main(String[] args) throws Exception {
AIOServer aioServer = new AIOServer();
aioServer.init();
Thread.sleep(Integer.MAX_VALUE);
//不让服务器停止
while(true) {}
} }
package net;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutionException; public class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, Object>{ private AsynchronousServerSocketChannel serverSocketChannel = null;
public AcceptHandler(AsynchronousServerSocketChannel serverSocketChannel) {
this.serverSocketChannel = serverSocketChannel ;
} //定义一个Buffer准备读取数据
ByteBuffer buff = ByteBuffer.allocate(1024);
Charset charset = Charset.forName("UTF-8"); //当IO操作完成时触发该方法
@Override
public void completed(final AsynchronousSocketChannel socketChannel, Object attachment) {
//记录新进来的Channel
AIOServer.channelList.add(socketChannel); //准备接收客户端的下一次连接
serverSocketChannel.accept(null, this); //读取客户端数据,此处的Handler主要处理读取客户数据
socketChannel.read(buff, null, new CompletionHandler<Integer, Object>() { @Override
public void completed(Integer result, Object attachment) {
buff.flip();
//将Buffer中的数据转换成字符串
String content = charset.decode(buff).toString();
//将客户端发来的数据 发送到么每个客户端
for(AsynchronousSocketChannel asc : AIOServer.channelList) {
try {
asc.write(ByteBuffer.wrap(content.getBytes(charset))).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
//清空buff容器,用户读取下一次数据
buff.clear();
} //当IO操作失败事触发该方法
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读取数据失败:"+ exc);
//读取数据失败,客户端出问题,移除对应的channel
AIOServer.channelList.remove(socketChannel);
}
}); } @Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接失败:"+exc);
} }
package net;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke; public class AIOClient {
static final int PORT = 30000;
//与服务器通信的异步Channel
AsynchronousSocketChannel socketChannel = null; JFrame mainWin = new JFrame("多人聊天");
JTextArea jta = new JTextArea(16,48);
JTextField jtf = new JTextField(40);
JButton sendBtn = new JButton("发送"); public void init() {
mainWin.setLayout(new BorderLayout());
jta.setEditable(false);
mainWin.add(new JScrollPane(jta),BorderLayout.CENTER);
JPanel jp = new JPanel();
jp.add(jtf);
jp.add(sendBtn); @SuppressWarnings("serial")
Action sendAction = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
String content = jtf.getText();
if(content.trim().length() > 0) {
//将输入内容写到channel中
try {
socketChannel.write(ByteBuffer.
wrap(content.getBytes(StandardCharsets.UTF_8))).get();
} catch (InterruptedException | ExecutionException e1) {
e1.printStackTrace();
}
}
jtf.setText("");
}
}; sendBtn.addActionListener(sendAction);
jtf.getInputMap().put(KeyStroke.getKeyStroke('\n', InputEvent.CTRL_MASK), "send");
jtf.getActionMap().put("send", sendAction);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.add(jp, BorderLayout.SOUTH);
mainWin.pack();
mainWin.setVisible(true);
} public void connect() throws Exception {
//用于读取数据的buffer
ByteBuffer buff = ByteBuffer.allocate(1024);
//创建一个线程池
ExecutorService executor = Executors.newFixedThreadPool(80);
//以指定线程池创建分组管理器
AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(executor);
//以分组管理器创建AsynchronousSocketChannel
socketChannel = AsynchronousSocketChannel.open(channelGroup);
//连接服务器
socketChannel.connect(new InetSocketAddress("127.0.0.1", PORT)).get();
jta.append("***与服务器连接成功***\n");
socketChannel.read(buff, null, new CompletionHandler<Integer, Object>() { @Override
public void completed(Integer result, Object attachment) {
buff.flip();
//将Buffer转换成字符串
String content = StandardCharsets.UTF_8.decode(buff).toString();
//显示从服务器读取的数据
jta.append("某人说:"+content+"\n");
buff.clear();
//为下一次读取数据做准备
socketChannel.read(buff, null, this);
} @Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读取数据失败"+exc);
}
});
} public static void main(String[] args) throws Exception {
AIOClient aioClient = new AIOClient();
aioClient.init();
aioClient.connect();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package net;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.Charset;
import java.util.concurrent.Future;
 
public class SimpleAIOClient {
 
    static final int PORT = 30000;
     
    public static void main(String[] args) throws Exception {
         
        //用户读取数据的Buffer
        ByteBuffer buff = ByteBuffer.allocate(1024);
        Charset charset = Charset.forName("UTF-8");
        try(//创建AsynchronousSocketChannel实例
            AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();)
        {
            //连接到远程服务器
            Future<Void> future = socketChannel.connect(new InetSocketAddress("127.0.0.1", PORT));
            future.get();
             
            buff.clear();
            //socketChannel中读取数据
            Future<Integer> future1 = socketChannel.read(buff);
            future1.get();
             
            buff.flip();
            String content = charset.decode(buff).toString();
            System.out.println("服务器:" + content);
         
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

结果:

java网络编程基础——TCP网络编程三的更多相关文章

  1. java网络编程基础——TCP网络编程一

    基于TCP协议的网络编程 TCP/IP协议是一种可靠的网络协议,它的通信的两端各自建立一个Socket,从而在通信的两端之间形成网络虚拟链路. Java使用Socket对象来代表两端的通信端口,并通过 ...

  2. java网络编程基础——TCP网络编程二

    1.半关闭的Socket 前面的服务器和客户端通信时总是以行为最小数据单位,但是在某些协议里,通信的数据单位可能是多行的,当出现多行数据时就 出现一个问题:Socket输出流如何表示输出数据已经结束. ...

  3. 【网络编程1】网络编程基础-TCP、UDP编程

    网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...

  4. 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系

    [Linux网络编程]TCP网络编程中connect().listen()和accept()三者之间的关系 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: conn ...

  5. Spark编程基础_RDD初级编程

    摘要:Spark编程基础_RDD初级编程 RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变.可分区.里面的元素 ...

  6. java网络编程基础——基本网络支持

    基本网络支持 java.net包主要为网络编程提供支持. 1.InetAddress InetAddress类代表IP地址,还有两个子类:Inet4Address.Inet6Address. pack ...

  7. Linux socket网络编程基础 tcp和udp

    Socket TCP网络通信编程 首先,服务器端需要做以下准备工作: (1)调用socket()函数.建立socket对象,指定通信协议. (2)调用bind()函数.将创建的socket对象与当前主 ...

  8. 网络编程基础:网络基础之网络协议、socket模块

    操作系统(简称OS)基础: 应用软件不能直接操作硬件,能直接操作硬件的只有操作系统:所以,应用软件可以通过操作系统来间接操作硬件 网络基础之网络协议: 网络通讯原理: 连接两台计算机之间的Intern ...

  9. Python黑客编程基础3网络数据监听和过滤

    网络数据监听和过滤 课程的实验环境如下: •      操作系统:kali Linux 2.0 •      编程工具:Wing IDE •      Python版本:2.7.9 •      涉及 ...

随机推荐

  1. TensorFlow反向传播算法实现

    TensorFlow反向传播算法实现 反向传播(BPN)算法是神经网络中研究最多.使用最多的算法之一,用于将输出层中的误差传播到隐藏层的神经元,然后用于更新权重. 学习 BPN 算法可以分成以下两个过 ...

  2. httprunnermanager安装和配置

    服务端安装mysql数据库(建议5.7以上的,mysql安装教程),设置utf-8编码,创建对应的数据库,设置好相应的用户名,密码,然后启动mysql 下载httprunnermanager 安装ht ...

  3. fiddler修改请求包和返回包

    设置好过滤后,找到需要修改的包,按如下步骤进行包的数据修改1.设置"禁止上传"(禁止XX为本人自己理解,专业术语不记得了,高手可留言笔者重新修订博文),打上断点,如下标志就是在请求 ...

  4. mybatis学习——类型别名(typeAliases)

    为什么要用类型别名? 答:类型别名可为 Java 类型设置一个缩写名字. 它仅用于 XML 配置,意在降低冗余的全限定类名书写. 举个例子说明: 在我们编写映射文件的时候: <?xml vers ...

  5. Pandas高级教程之:Dataframe的合并

    目录 简介 使用concat 使用append 使用merge 使用join 覆盖数据 简介 Pandas提供了很多合并Series和Dataframe的强大的功能,通过这些功能可以方便的进行数据分析 ...

  6. 【NX二次开发】获取视图当前的剪辑边界UF_VIEW_ask_current_xy_clip()

    UF_VIEW_ask_current_xy_clip()这个函数网上还没有详细的说明,我花了一点时间,详细得理解了一下函数返回的4个值的意思,作为一个猜想,希望有人能验证一下. 获取视图当前的剪辑边 ...

  7. 【NX二次开发】根据视图名称旋转视图,在布局中替换视图uc6464

    uc6464("布局名","旧视图名","新视图名");输入布局名.旧视图名.新视图名.如果布局名为空则更新当前布局.如果旧视图名为空,则工 ...

  8. Linux面试题(史上最全、持续更新、吐血推荐)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...

  9. 重新整理 .net core 实践篇—————HttpClientFactory[三十二]

    前言 简单整理一下HttpClientFactory . 正文 这个HttpFactory 主要有下面的功能: 管理内部HttpMessageHandler 的生命周期,灵活应对资源问题和DNS刷新问 ...

  10. 有趣的开源项目集结完毕,HelloGitHub 月刊第 63 期发布啦!

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这里有实战项目.入门教程.黑科技.开源书籍.大厂开源项目等,涵盖多种编程语言 Pyt ...