相比于BIO(堵塞的IO,B是Blocking的意思),NIO(同步非堵塞IO)和AIO(异步非阻塞IO)的性能要比其高很多。异步通讯不以反复轮询为方式,主程序不会阻塞在死循环内,而是会继续的执行下去。如果收到消息,会自动调用回调函数,完成指定的操作。而NIO和AIO的复杂性显然大于BIO
前几天笔者写了一个NIO的通讯窗口,本次写一个AIO的窗口。主要用到
AsynchronousServerSocketChannel、AsynchronousSocketChannel。向server.accept方法中传递一个CompletionHandler,并实现completed和failed方法。complete方法是回调函数,当accept成功执行时会调用此方法。并且套件字也会传递给ComletionHandler.
运行出来就是这个效果:
代码如下:
package Message2;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/*
* @version:2.0
* @author:阿基
* @time:2020/8/22
*/
public class Message2
{
public static void main(String[] args)
{
EventQueue.invokeLater(()->{
MessageFrame frame=new MessageFrame("Message 2.0");
frame.setVisible(true);
});
}
}
class MessageFrame extends JFrame
{
private JButton connectButton;
private JButton sendButton;
private JButton cancelButton;
private JButton acceptButton;
private JTextArea messages;
String name;
private JLabel hostLabel;
private JLabel portLabel;
private JTextField hostField;
private JTextField portField;
private JTextField messageField;
private Server server;
private SocketChannel socketChannel=null;
public MessageFrame(String title)
{
super(title);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
hostLabel=new JLabel("客户机IP地址:");
hostField=new JTextField(10);
portLabel=new JLabel("客户机端口");
portField=new JTextField(5);
JPanel northPanel=new JPanel();
connectButton=new JButton("连接");
WindowListener listener=new MyWindowHandler();
addWindowListener(listener);
connectButton.addActionListener(event->{
String ip=hostField.getText().trim();
int port =Integer.parseInt(portField.getText().trim());
if(connect(ip,port))
messages.append("连接成功\n");
});
northPanel.add(hostLabel,BorderLayout.NORTH);
northPanel.add(hostField,BorderLayout.NORTH);
northPanel.add(portLabel,BorderLayout.NORTH);
northPanel.add(portField,BorderLayout.NORTH);
northPanel.add(connectButton,BorderLayout.NORTH);
add(northPanel,BorderLayout.NORTH);
messages=new JTextArea("本机的IP地址查询DOS命令:ipconfig\n",20,60);
add(new JScrollPane(messages),BorderLayout.CENTER);
acceptButton=new JButton("开启服务器");
sendButton=new JButton("发送信息");
cancelButton=new JButton("退出");
messageField=new JTextField(20);
sendButton.addActionListener(event->{
sendMessage();
});
acceptButton.addActionListener(event->{
server=new Server();
server.start();
});
cancelButton.addActionListener(event->
{
System.exit(1);
});
JPanel southPanel=new JPanel();
southPanel.add(messageField,BorderLayout.CENTER);
southPanel.add(acceptButton,BorderLayout.SOUTH);
southPanel.add(sendButton,BorderLayout.SOUTH);
southPanel.add(cancelButton,BorderLayout.SOUTH);
add(southPanel,BorderLayout.SOUTH);
pack();
}
//判断是否可以连接成功
private boolean connect(String ip, int port)
{
try {
socketChannel=SocketChannel.open();
return socketChannel.connect(new InetSocketAddress(ip,port));
}catch(IOException e)
{
messages.append("连接失败:"+e);
return false;
}
}
public void sendMessage()
{
//输入IP和端口
String ip=hostField.getText().trim();
int port =Integer.parseInt(portField.getText().trim());
try {
//创建通道,并建立连接
socketChannel=SocketChannel.open();
socketChannel.connect(new InetSocketAddress(ip,port));
//设置为非阻塞模式
socketChannel.configureBlocking(false);
String word=messageField.getText();
word=name+":"+word;
if(word!=""|word!=null) {
//建立缓存区
ByteBuffer writeBuffer=ByteBuffer.allocate(1024*10);
//将输入的字符串以“UTF-”转换为数组
byte[] bytes=word.getBytes("UTF-8");
//将数组写入缓存区
writeBuffer.put(bytes);
//翻转缓存区, The limit is set to
//the current position and then
//the position is set to zero. If the mark is defined then it is discarded.
writeBuffer.flip();
socketChannel.write(writeBuffer);
messages.append(word+"\n");
messageField.setText("");
}
}catch(IOException e)
{
messages.append("发送消息时出错:"+e);
}
}
//创建服务器
class Server extends Thread
{
@Override
public void run()
{
try
{
AsynchronousServerSocketChannel server=AsynchronousServerSocketChannel.open();
int port=Integer.parseInt(messageField.getText());
server.bind(new InetSocketAddress(port));
messages.append("服务器已开启,端口为"+port+"\n");
messageField.setText("");
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel channel, Void attachment)
{
server.accept(null,this);
ByteBuffer readBuffer=ByteBuffer.allocate(1024*10);
channel.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment)
{
attachment.flip();
String word = null;
try {
word = new String(attachment.array(),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
messages.append(word+"\n");
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
messages.append("写入过程出现错误:"+exc+"\n");
}
});
}
@Override
public void failed(Throwable exc, Void attachment) {
// TODO Auto-generated method stub
}
});
}catch(IOException e)
{
messages.append("服务器已经创建:"+e+"\n");
}
}
}
class MyWindowHandler extends WindowAdapter
{
public void windowOpened(WindowEvent e)
{
name=JOptionPane.showInputDialog(null, "请输入昵称", "欢迎", JOptionPane.INFORMATION_MESSAGE);
messages.append("你的昵称是:"+name+"\n");
}
}
}