基于NIO和多线程的网络多客户聊天室

聊天室服务器
package com.charroomserver;

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

/**
 * 开始时间2017年8月13日14:49:26
 * 网络多客户聊天室
 * 功能1:客户端通过JAVA NIO连接到服务器,支持多客户端的连接
 * 功能2:客户端初次连接时,服务器提示输入昵称,之后发送消息都需要按照规定的格式,带着昵称发送消息
 * 功能3:客户端登陆后,发送设置好的欢迎消息和在线人数给客户端,并且通知其他客户端该客户端上线
 * 功能4:服务器收到已登录客户端输入内容,转发至其他登陆客户端*/

public class ChatRoomServer {
	ServerSocketChannel serverSocketChannel;
	Selector selector;
	Charset charset = Charset.forName("GBK");
	int port = 9999;
	
	//初始化数据
	public void init() {
		try {
			serverSocketChannel = ServerSocketChannel.open();
			selector = Selector.open();
			serverSocketChannel.configureBlocking(false);
			serverSocketChannel.bind(new InetSocketAddress(port));//设置IP
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
			watching();
			
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			try {
				serverSocketChannel.close();
				selector.close();
			} catch (IOException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}
	
	//向客户端发送消息
	public void broadCast(String str, String username) throws IOException {
		Set<SelectionKey> keys = selector.keys();//因为同时可能有很多时间发生,并且需要知道发生的具体内容以及主体,获取所有的keys
		Iterator<SelectionKey> iterator = keys.iterator();//创建一个迭代器,迭代获取对象
		while (iterator.hasNext()) {
			SelectionKey selectionKey = iterator.next();
			Channel channel = selectionKey.channel();//排除serverSocket
			if (channel instanceof SocketChannel) {//关心的是SocketChannel事件
				SocketChannel socketChannel = (SocketChannel) channel;
				socketChannel.write(charset.encode(username+ "对大家说 : " + str));
			}
		}
	}
	
	public void watching() throws IOException {
		System.out.println("服务器启动成功.......");
		while (true){
			int readyChannels = selector.select();//等待事件发生,反应当前有多少事件发生
			if (readyChannels == 0) {
				continue;
			}
			//开始出力事件
			Set<SelectionKey> keys = selector.selectedKeys();//因为同时可能有很多时间发生,并且需要知道发生的具体内容以及主体
			Iterator<SelectionKey> iterator = keys.iterator();//创建一个迭代器,迭代获取对象
			while (iterator.hasNext()) {
				SelectionKey selectionKey = iterator.next();
				if (selectionKey.isAcceptable()) {
					//客户端接入事件
					SocketChannel channel = serverSocketChannel.accept();
					channel.configureBlocking(false);//设置阻塞模式
					channel.register(selector, SelectionKey.OP_READ);//创建一个read的Registr
					//写入欢迎信息
					channel.write(charset.encode("欢迎来到聊天室,请输入姓名"));	
					//selectionKey.attach(new UserInfo());
				} else if (selectionKey.isReadable()) {
					//获取客户端的channel
					SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
					//获取channel内容,创建bytebuffer大小为128的容器
					UserInfo userInfo = (UserInfo) selectionKey.attachment();
					ByteBuffer buffer = ByteBuffer.allocate(128);
					int flag = socketChannel.read(buffer);
					StringBuffer stringBuffer = new StringBuffer();
					while (flag > 0) {
						buffer.flip();
						stringBuffer.append(charset.decode(buffer));//后期需要重构

						buffer.clear();
						flag = socketChannel.read(buffer);
					}
					if (userInfo != null && userInfo.init) {
						broadCast(stringBuffer.toString(),userInfo.getName());//将消息内容传递给BroadCast处理
					} else {
						UserInfo info = new UserInfo();
						info.setName(stringBuffer.toString());
						info.setInit(true);
						selectionKey.attach(info);
						socketChannel.write(charset.encode("您好" + info.getName() + "现在您可以聊天室里的人聊天了。"));
					}
				}
				iterator.remove();
			}
		}
		
	}
	
	public static void main(String[] args) {
		//创建serverSocketChannel
		try (ServerSocketChannel  serverSocketChannel = ServerSocketChannel.open();) {ServerSocketChannel 是一个可以监听新进来的TCP连接的通道
			serverSocketChannel.configureBlocking(false);//证明是非阻塞的,只有继承了selectAbleChanel
			ChatRoomServer chatRoomServer = new ChatRoomServer();
			//serverSocketChannel.configureBlocking(block)
			System.out.println("what");
			 Selector selector = Selector.open();//创建一个Selector
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
			chatRoomServer.init();
			//注册选择器,在客户端接入的时候,委托selecter管理接入事件 
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class UserInfo{
	String name;
	
	boolean init = false;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public boolean isInit() {
		return init;
	}

	public void setInit(boolean init) {
		this.init = init;
	}
	
}

聊天室客户端

package com.charroomserver;

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.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class ChatRoomClient {
	SocketChannel channel;
	Selector selector;
	int port = 9999;
	String ip = "127.0.0.1";

	public void init() {
		try {
			channel = SocketChannel.open(new InetSocketAddress(ip, port));
			channel.configureBlocking(false);
			selector = Selector.open();
			channel.register(selector, SelectionKey.OP_READ);
 
			// 启动线程
			new MySelectorThread(selector).start();
			while (true) {
				// 获取控制台输入
				Scanner scanner = new Scanner(System.in);
				String content = scanner.nextLine();
				channel.write(Charset.forName("GBK").encode(content));
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				channel.close();
				selector.close();
			} catch (IOException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}

	}

	class MySelectorThread extends Thread {
		Selector selector;

		public MySelectorThread(Selector selector) {
			// TODO Auto-generated constructor stub
			this.selector = selector;
		}

		@Override
		public void run() {
			// 处理事件
			try {
				while (true) {
					int readyChannels = selector.select();// 等待事件发生,反应当前有多少事件发生
					if (readyChannels == 0) {
						continue;
					}
					// 开始处理事件
					Set<SelectionKey> keys = selector.selectedKeys();// 因为同时可能有很多时间发生,并且需要知道发生的具体内容以及主体
					Iterator<SelectionKey> iterator = keys.iterator();// 创建一个迭代器,迭代获取对象
					while (iterator.hasNext()) {
						SelectionKey selectionKey = iterator.next();
						if (selectionKey.isReadable()) {
							SocketChannel channel = (SocketChannel) selectionKey.channel();
							ByteBuffer buffer = ByteBuffer.allocate(128);
							StringBuffer stringBuffer = new StringBuffer();
							while (channel.read(buffer) > 0) {
								buffer.flip();
								stringBuffer.append(Charset.forName("GBK").decode(buffer));
								buffer.clear();
							}
							System.out.println(stringBuffer.toString());
						}
						iterator.remove();
					}
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		ChatRoomClient chatRoomClient = new ChatRoomClient();
		chatRoomClient.init();
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值