NIO通讯模式的简单实现
为了简化过程,在写服务器时,只能读取信息;在写客户端时,只能发送信息。当局域网下双方连接上了对方的端口,即可实现双向通信。基于Selector这个选择器,在单线程下也可以实现多方通信。
主要需要:SeverSocketChannel,SocketChannel,Selector,SelectionKey
代码如下:
package Message1;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/*
* @version:1.0
* @author:何老表
* @time:2020/8/22
*/
public class Message1
{
public static void main(String[] args)
{
EventQueue.invokeLater(()->{
MessageFrame frame=new MessageFrame("Message1.0");
frame.setVisible(true);
});
}
}
class MessageFrame extends JFrame
{
private JButton connectButton;
private JButton sendButton;
private JButton cancelButton;
private JButton acceptButton;
private JTextArea messages;
private JLabel hostLabel;
private JLabel portLabel;
private JTextField hostField;
private JTextField portField;
private JTextField messageField;
private Server server;
private SocketChannel socketChannel=null;
private ServerSocketChannel serverSocketChannnel=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("连接");
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();
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("You:"+word+"\n");
messageField.setText("");
}
}catch(IOException e)
{
messages.append("发送消息时出错:"+e);
}
}
//创建服务器
class Server extends Thread
{
private Selector selector=null;
@Override
public void run()
{
try
{
//创建可中断服务器和选择器
serverSocketChannnel=ServerSocketChannel.open();
selector=Selector.open();
//设置为非阻塞模式
serverSocketChannnel.configureBlocking(false);
int port =Integer.parseInt(messageField.getText().trim());
//键入端口
serverSocketChannnel.socket().bind(new InetSocketAddress(port),10);
//将服务器通道注册在选择器上,并设置为等待模式
serverSocketChannnel.register(selector, SelectionKey.OP_ACCEPT);
messageField.setText("");
messages.append("服务器已经启动启动,等待新接入的客户机,本机端口为:"+port+"\n");
acceptButton.setEnabled(false);
}
catch(IOException e)
{
messages.append("创建服务器过程中发生错误:"+e);
messages.append("\n");
}
//死循环,轮询所有通道是否有响应
while(true)
{
try
{
//开始轮询
selector.select();
//得到所有通道的键值
Set<SelectionKey> selectionKey=selector.selectedKeys();
Iterator<SelectionKey> it=selectionKey.iterator();
SelectionKey key=null;
while(it.hasNext())
{
key = it.next();
//移走,防止重复操作
it.remove();
try
{
handleInput(selector,key);
}
catch(IOException e)
{
if(key!=null) {
key.cancel();
if(key.channel()!=null)
key.channel().close();
}
}
}
}catch(Exception e)
{
messages.append("接受消息时发生错误:"+e);
}
}
}
private void handleInput(Selector selector, SelectionKey key) throws IOException
{
if(key.isValid())
{
//处理刚刚建立的连接
if(key.isAcceptable())
{
//由键值得到相应服务器
ServerSocketChannel ssc=(ServerSocketChannel)key.channel();
//得到套间字
SocketChannel sc=ssc.accept();
//设置为非阻塞模式
sc.configureBlocking(false);
//在选择器上注册为可读的
sc.register(selector, SelectionKey.OP_READ);
}
if(key.isReadable())
{
SocketChannel channel=(SocketChannel)key.channel();
ByteBuffer readBuffer=ByteBuffer.allocate(1024*10);
int readBytes=channel.read(readBuffer);
if(readBytes>0)
{
readBuffer.flip();
byte[] bytes=new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String word=new String(bytes,"UTF-8");
messages.append("Others:"+word+"\n");
}else if(readBytes<0)
{
key.cancel();
socketChannel.close();
}
else ;
}
}
}
}
}
赶快拿回去试一下吧!