用NIO实现的超简易聊天室

基于NIO的简易聊天室

由于水平有限,只能做出这样的程度,先暂时展示源码,后面再逐一补上分析QAQ

运行环境:jdk8

这是客户端
package com.qin.nio.groupchat; //我的包名

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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;

public class GroupChatClient {
	
	private final String HOST = "127.0.0.1";
	private final int POST = 6667;
	private Selector selector;
	private SocketChannel socketChannel;
	private String username;
	
	//构造器,完成初始化工作
	public GroupChatClient() throws IOException {
		
		selector = Selector.open();
		//连接服务器
		socketChannel = socketChannel.open(new InetSocketAddress(HOST,POST));
		//设置非阻塞
		socketChannel.configureBlocking(false);
		//将channel 注册到selector
		socketChannel.register(selector, SelectionKey.OP_READ);
		//得到username
		username = socketChannel.getLocalAddress().toString().substring(1);
		System.out.println(username+" is ok...");
	
		
	}
	
	//先服务器发送消息
	public void sendIndo(String info) {
		info = username + "说"+ info;
		
		try {
			socketChannel.write(ByteBuffer.wrap(info.getBytes()));
		}catch(IOException e) {
			e.printStackTrace();
		}
		
	}
	
	//读取从服务器端回复的消息
	public void readIndo() {
		
		try {
			int readChannels = selector.select(1000);
			if(readChannels > 0) { //有可用的通道
				Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
				while(iterator.hasNext()) {
					SelectionKey key = iterator.next();
					if(key.isReadable()) {
						//得到相关的通道
						SocketChannel channel = (SocketChannel) key.channel();
						//得到一个buffer
						ByteBuffer buffer = ByteBuffer.allocate(1024);
						//读取
						channel.read(buffer);
						//把从缓冲区读到的数据转成字符串
						String msg = new String(buffer.array());
						System.out.println(msg.trim());
					}
					iterator.remove();
				}
				
				
			}else {
				//System.out.println("没有可用的通道...");
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public static void main(String[] args) throws IOException {
		//启动客户端
		GroupChatClient chatClient = new GroupChatClient();
		
		//启动一个线程,每个3秒读取从服务端发送的数据
		new Thread(()->{
			while(true) {
				chatClient.readIndo();
				try {
					Thread.currentThread().sleep(3000);
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
		
		//发送数据
		Scanner scanner = new Scanner(System.in);
		while(scanner.hasNextLine()) {
			String str = scanner.nextLine();
			chatClient.sendIndo(str);
		}
	}
}

这是服务端
package com.qin.nio.groupchat;//我的包名

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class GroupChatServer {
	//定义相关的属性
	private Selector selector;
	private ServerSocketChannel listenChannel;
	private static final int PORT = 6667;
	
	
	//构造器
	public GroupChatServer() {
		try {
			//得到选择器
			selector = Selector.open();
			//ServerSocketChannel
			listenChannel = ServerSocketChannel.open();
			//绑定端口
			listenChannel.socket().bind(new InetSocketAddress(PORT));
			//设置非阻塞
			listenChannel.configureBlocking(false);
			//将该listen注册到selector中
			listenChannel.register(selector, SelectionKey.OP_ACCEPT);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//监听
	public void listen() {
		try{
			while(true) {
				//进行连接(隔1秒),结果赋值给count
				int count = selector.select();
				
				if(count > 0) { //有事件处理
					//拿到所有的keys和迭代器,进行遍历
					Set<SelectionKey> selectedKeys = selector.selectedKeys();
					Iterator<SelectionKey> iterator = selectedKeys.iterator();
					
					while(iterator.hasNext()) {
						//对每一个单独的key进行处理
						SelectionKey key = iterator.next();
						
						//监听到accept
						if(key.isAcceptable()) {
							SocketChannel sc = listenChannel.accept();
							sc.configureBlocking(false);
							//将sc注册到selector
							sc.register(selector, SelectionKey.OP_READ);
							System.out.println(sc.getRemoteAddress()+" 注册成功/上线 ");
						}
						
						if(key.isReadable()) { //通道发出read事件
							readData(key);
						}
						
						//删除key,防止重复处理
						iterator.remove();
					}
				}else {
					System.out.println("等待~~");
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally {
			
		}
	}

	//读取客户端的消息
	private void readData(SelectionKey key) {
		//定义一个socketchannel
		SocketChannel channel = null;
		try {
			//取到关联的channel
			channel = (SocketChannel)key.channel();
			
			//创建一个buffer
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			
			int count = channel.read(buffer);			
			//根据count的值进行处理
			if(count > 0) {
				//把缓存区的数据转成字符串
				String msg = new String(buffer.array());
				//输出
				System.out.println("FROM 客户端"+msg);
				
				//向其他客户端转发消息(去掉自己)
				senInfoToOtherClients(msg, channel);
			}
		}catch(IOException e) {
			try {
				System.out.println(channel.getRemoteAddress()+"离线");
				//取消注册,关闭通道
				key.cancel();
				channel.close();
				//
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
	}
	
	//转发消息给其他用户(通道)
	private void senInfoToOtherClients(String msg,SocketChannel self) throws IOException {
		System.out.println("服务器转发消息中~~~");
		//遍历所有注册到selector上的socketchannel,并排除自己
		for(SelectionKey key: selector.keys()) {
			//通过key,拿到对应的channel
			Channel targetChannel = key.channel();
			
			//排除自己
			if(targetChannel instanceof SocketChannel && targetChannel != self) {
				//转型
				SocketChannel dest = (SocketChannel)targetChannel;
				//将msg存到buffer
				ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
				
				//将当前的数据写入到通道中
				dest.write(buffer);
			}
		}
	}
	
	public static void main(String[] args) {
		//创建一个服务器端
		GroupChatServer chatServer = new GroupChatServer();
		chatServer.listen();
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值