简单NIO通讯模式的实现

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 ;
				}
			}
		}
	}
}

效果图
赶快拿回去试一下吧!

猜你喜欢

转载自blog.csdn.net/m0_47202518/article/details/108171246