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. RGB-D对红外热像仪和毫米波雷达标定

    RGB-D对红外热像仪和毫米波雷达标定 Extrinsic Calibration of Thermal IR Camera and mmWave Radar by Exploiting Depth ...

  2. 【题解】【洛谷 P1967】 货车运输

    目录 洛谷 P1967 货车运输 原题 题解 思路 代码 洛谷 P1967 货车运输 原题 题面请查看洛谷 P1967 货车运输. 题解 思路 根据题面,假设我们有一个普通的图: 作图工具:Graph ...

  3. 【渗透实战】记一次艰难的内网漫游第四期_蹭我WIFI?看我如何利用组合拳日进蹭网者内网

    /文章作者:Kali_MG1937 CSDN博客ID:ALDYS4 QQ:3496925334/ 内网漫游系列第三期:[渗透实战]记一次艰难的内网漫游第三期_我是如何利用APT攻击拿到内网最高权限的 ...

  4. 约会Rendezvous

    约会 Rendezvous 内存限制:128 MiB 时间限制:1000 ms 标准输入输出     题目描述 给定一个有 nnn 个顶点的有向图,每个顶点有且仅有一条出边.每次询问给出两个顶点 ai ...

  5. 不管卷不卷,面试还是得问问你G1原理!

    所有的垃圾回收器的目的都是朝着减少STW的目的而前进,G1(Garbage First)回收器的出现颠覆了之前版本CMS.Parallel等垃圾回收器的分代收集方式,从2004年Sun发布第一篇关于G ...

  6. 我对SpringMVC的浅见

    之前在学校没接触框架这东西之前只接触过MVC的model1和model2,而真正接触SpringMVC的时候是在一年前,在学习过程中,我这才意识到SpringMVC大大简化了以前的开发工程,到了社会上 ...

  7. 快来为你的 .NET 应用加个监控吧!

    导读 CZGL.ProcessMetrics 是一个 Metrics 库,能够将程序的 GC.CPU.内存.机器网络.磁盘空间等信息记录下来,使用 Prometheus 采集信息,然后使用 Grafa ...

  8. Linux中su和sudo的用法

    su -#su - oldboy //当执行这个命令的时候表示切换到oldboy用户,并且重新读取用户环境相关配置文件,具体的来说就是执行下用户家目录下.bash_profile和.bashrc文件, ...

  9. Centos 8 上定时备份Gitlab ,脚本实现定时备份,备份恢复

    定时备份 要求 为了能够备份和恢复,请确保你的系统上安装了Rsync yum install rsync -y 配置备份目标机器免密认证 执行ssh-keygen -t rsa 生成私钥和公钥 ssh ...

  10. AcWing 241. 楼兰图腾

    #include<bits/stdc++.h> using namespace std; const int N=2e5+5; typedef long long ll; ll ans,l ...