服务端:

Loader.java

package net.chatroom.server;

public class Loader {

    public static void main(String[] args) {
Deamon deamon = new Deamon(9999);
new Thread(deamon).start();
} }

Util.java

package net.chatroom.server;

import java.nio.charset.Charset;
import java.util.HashSet; public class Util { public static Charset charset = Charset.forName("UTF-8"); // 相当于自定义协议格式,与客户端协商好
public static String USER_CONTENT_SPILIT = "#@#"; // 用来记录在线人数,以及昵称
public static HashSet<String> users = new HashSet<String>();
public static String USER_EXIST = "system message: user exist, please change a name";
}

Deamon.java

package net.chatroom.server;

import java.io.IOException;
import java.net.InetSocketAddress;
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.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List; public class Deamon implements Runnable { private boolean flag = true; private ServerSocketChannel serverChannel = null;
private Selector selector = null;
/**
* 记录进来的所有的客户端连接
* */
private List<SocketChannel> clientChannels = null; public void setFlag(boolean flag) {
this.flag = flag;
} public Deamon(int port) {
try {
serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
this.clientChannels = new ArrayList<SocketChannel>();
System.out.println("Server is listening now...");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} @Override
public void run() {
// System.out.println("server listening..");
while (this.flag) {
int num = 0;
try {
//此处select()阻塞了线程
num = selector.select();
} catch (IOException e) {
System.out.println("Error while select channel:" + e);
}
if (num > 0) {
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) {
// 监听到有新的连接则再注册读操作
this.clientChannels.add(Dealer.accept(selector,
serverChannel));
} else if (key.isReadable()) {
// 监听到读操作
try {
Dealer.read(selector, key, clientChannels);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("server to close..");
if (this.serverChannel != null && this.serverChannel.isOpen()) {
try {
this.serverChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (this.selector != null && this.selector.isOpen()) {
try {
this.selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }

Dealer.java

package net.chatroom.server;

import java.io.IOException;
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.rmi.server.Skeleton;
import java.util.List;
import java.util.Scanner; public class Dealer { public static SocketChannel accept(Selector selector,
ServerSocketChannel serverChannel) {
SocketChannel channel = null;
try {
channel = serverChannel.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ); channel.write(Util.charset.encode("Please input your name.")); } catch (Exception e) {
System.out.println("Error while configure socket channel :" + e);
if (channel != null) {
try {
channel.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return channel;
} public static void read(Selector selector, SelectionKey selectionkey,
List<SocketChannel> clientChannels) throws IOException {
SocketChannel socketClientChannel = (SocketChannel) selectionkey
.channel();
ByteBuffer buffer = ByteBuffer.allocateDirect(6 * 1024);
StringBuilder content = new StringBuilder();
int num = 0;
try {
// 将客户端发上来的消息读到buffer
//循环将通道中数据读入buffer
while (socketClientChannel.read(buffer) > 0) {
buffer.flip();// 切换成读
content.append(Util.charset.decode(buffer));
} System.out.println("num:" + num);
System.out.println("Server is listening from client :"
+ socketClientChannel.getRemoteAddress() + " data rev is: "
+ content);
} catch (IOException e) {
/**
* 如果出现异常,则需要关闭连接。故把num设置为-1,用下面的关闭逻辑来关闭channel
*/
num = -1;
} if (num >= 0) {
if (content.length() > 0) {
String[] arrayContent = content.toString().split(
Util.USER_CONTENT_SPILIT);
// 注册用户
if (arrayContent != null && arrayContent.length == 1) {
String name = arrayContent[0];
if (Util.users.contains(name)) {
socketClientChannel.write(Util.charset
.encode(Util.USER_EXIST));
} else {
Util.users.add(name);
int onlineNum = clientChannels.size();
String message = "welcome " + name
+ " to chat room! Online numbers:" + onlineNum;
BroadCast2(clientChannels, null, message);
}
}
// 注册完了,发送消息
else if (arrayContent != null && arrayContent.length > 1) {
String name = arrayContent[0];
String message = content.substring(name.length()
+ Util.USER_CONTENT_SPILIT.length());
message = name + " say: " + message;
if (Util.users.contains(name)) {
// 不回发给发送此内容的客户端
BroadCast2(clientChannels, socketClientChannel, message);
}
} // /**
// * 把读到的数据原封不动的下发给客户端
// */
// int length = clientChannels.size();
// for (int index = 0; index < length; index++) {
// // 循环所有的客户端连接,下发数据
// buffer.flip();
// try {
// // 将buffer里的数据再下发给客户端的通道
// clientChannels.get(index).write(buffer);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
} else {
/**
* 如果未读到数据,对方关闭了SocketChannel 所以服务器这边也要关闭
*/
try {
socketClientChannel.close();
int length = clientChannels.size();
for (int index = 0; index < length; index++) {
if (clientChannels.get(index).equals(socketClientChannel)) {
// 移除当前接受的通道
clientChannels.remove(index);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
} } // TODO 要是能检测下线,就不用这么统计了
public static int OnlineNum(Selector selector) {
int res = 0;
for (SelectionKey key : selector.keys()) {
Channel targetchannel = key.channel(); if (targetchannel instanceof SocketChannel) {
res++;
}
}
return res;
} public void BroadCast(Selector selector, SocketChannel except,
String content) throws IOException {
// 广播数据到所有的SocketChannel中
for (SelectionKey key : selector.keys()) {
Channel targetchannel = key.channel();
// 如果except不为空,不回发给发送此内容的客户端
if (targetchannel instanceof SocketChannel
&& targetchannel != except) {
SocketChannel dest = (SocketChannel) targetchannel;
dest.write(Util.charset.encode(content));
}
}
} public static void BroadCast2(List<SocketChannel> socketChannels,
SocketChannel except, String content) throws IOException {
for (SocketChannel socketChannel : socketChannels) {
if (!socketChannel.equals(except)) {
// 除了自己,其它通道都通知
socketChannel.write(Util.charset.encode(content));
}
}
} }

客户端:

Loader.java

package net.chatroom.client;

import java.util.Scanner;

import net.chatroom.server.Util;

public class Loader {

    public static void main(String[] args) {
String name = "";
Deamon deamon = new Deamon("127.0.0.1", 9999);
new Thread(deamon).start(); // 在主线程中 从键盘读取数据输入到服务器端
Scanner scan = new Scanner(System.in);
while (scan.hasNextLine()) {
String line = scan.nextLine();
if ("".equals(line))
continue; // 不允许发空消息
if ("".equals(name)) {
name = line;
line = name + Util.USER_CONTENT_SPILIT;
} else {
line = name + Util.USER_CONTENT_SPILIT + line;
}
deamon.chancelToWrite(Util.charset.encode(line));// sc既能写也能读,这边是写
}
}
}

Deamon.java

package net.chatroom.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
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; import net.chatroom.server.Util; public class Deamon implements Runnable {
/**
* 选择器,用于监听注册在上面的SocketChannel的状态
*/
private Selector selector = null; /**
* SocketChannel 用户发送和接受数据的信道
*/
private SocketChannel channel = null; /**
* 运行标识。在线程里此标识为false的时候会推出线程
* 该属性在ExitCommandListener里通过调用setFlag方法修改,用于通知线程用户要求退出的程序
*/
private boolean flag = true; public void setFlag(boolean flag) {
this.flag = flag;
} public Deamon(String address, int port) {
try {
channel = SocketChannel.open(new InetSocketAddress(address, port));
channel.configureBlocking(false);
selector = Selector.open();
// 客户端直接注册读和写操作
channel.register(selector, SelectionKey.OP_READ
| SelectionKey.OP_WRITE); } catch (IOException e) {
e.printStackTrace();
} } public void chancelToWrite(ByteBuffer buffer){
try {
channel.write(buffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} @Override
public void run() {
System.out.println("client run..");
while (this.flag) {
/**
* 如果可以继续执行,则在循环体内循环执行监听选择操作
*/
int num = 0;
try {
/**
* 得到处于可读或者可写状态的SocketChannel对象的个数
*/
// 客户端的select()并不阻塞线程,是因为客户端一启动就是SelectionKey.OP_WRITE状态
// System.out.println("client select..");
num = this.selector.select(); // System.out.println("client num:"+num);
} catch (IOException e) {
/**
* 如果出现异常,则此处应该加上日志打印,然后跳出循环,执行循环体下面的释放资源操作
*/
break;
} if (num > 0) {
/**
* 如果有多个SocketChannel处于可读或者可写状态,则轮询注册在Selector上面的SelectionKey
*/
Iterator<SelectionKey> keys = selector.selectedKeys()
.iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
/**
* 此步操作用于删除该SelectionKey的被选中状态
*/
keys.remove();
if (key.isReadable()) {
System.out.println("client isReadable..");
/**
* 如果是读操作,则调用读操作的处理逻辑
*/
try {
Dealer.read((SocketChannel) key.channel());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (key.isWritable()) {
//客户端的写状态是一直就绪的
// System.out.println("client isWritable..");
/**
* 如果是写操作,则调用写操作的处理逻辑
*/
// Dealer.write((SocketChannel) key.channel());
}
}
} /*取消关注,多用在多线程的时候
* key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
*
* 增加关注
* key.interestOps(key.interestOps() | SelectionKey.OP_READ);
* */ try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} if (this.channel != null && this.channel.isOpen()) {
/**
* 关闭SocketChannel
*/
try {
this.channel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (this.selector != null && this.selector.isOpen()) {
/**
* 关闭Selector选择器
*/
try {
this.selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Dealer.java

package net.chatroom.client;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel; import net.chatroom.server.Util; public class Dealer {
/**
* 从SocketChannel中读取信息
*
* @param channel
* @throws IOException
*/
public static void read(SocketChannel channel) throws IOException { /**
* 初始化缓冲区
*/
ByteBuffer buffer = ByteBuffer.allocateDirect(6 * 1024);
/**
* 读到的字节数
*/
int num = 0;
String content = "";
while ((num = channel.read(buffer)) > 0) {
buffer.flip();
content += Util.charset.decode(buffer);
}
//若系统发送通知名字已经存在,则需要换个昵称
if(Util.USER_EXIST.equals(content)) {
// name = "";
System.out.println("name has exists.");
}
System.out.println(content);
} /**
* 想SocketChannel中写入数据
*
* @param channel
*/
public static void write(SocketChannel channel) { // /**
// * 从消息队列中获取要发送的消息
// */
// String msg = MsgQueue.getInstance().get();
// if (msg == null) {
// /**
// * 如果消息队列中没有要发送的消息,则返回。
// */
// return;
// }
// /**
// * 初始化缓冲区
// */
// ByteBuffer buffer = ByteBuffer.allocateDirect(6 * 1024);
//
// /**
// * 把消息放到缓冲区中
// */
// buffer.put(msg.getBytes());
//
// /**
// * 重置缓冲区指针
// */
// buffer.flip();
// try {
// /**
// * 把缓冲区中的数据写到SocketChannel里
// */
// channel.write(buffer);
// } catch (IOException e) {
// e.printStackTrace();
// }
}
}

java NIO经典实例的更多相关文章

  1. java nio socket实例

    Server端代码: public class NioServer { //通道管理器 private Selector selector; //获取一个ServerSocket通道,并初始化通道 p ...

  2. Java递归算法经典实例(兔子问题、阶乘、1到100累加)

    https://blog.csdn.net/isitman/article/details/61199070

  3. Java NIO原理及实例

    Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O.下面是java NIO的工作原理: 1. 由一个专门的线程来处理所有的 IO 事件,并负责分发. 2. ...

  4. Java NIO Socket编程实例

    各I/O模型优缺点 BIO通信模型 BIO主要的问题在于每当有一个新的客户端请求接入时,服务端必须创建一个新的线程处理新接入的客户端链路,一个线程只能处理一个客户端连接 线程池I/O编程 假如所有可用 ...

  5. Java nio socket与as3 socket(粘包解码)连接的应用实例

    对Java nio socket与as3 socket连接的简单应用 <ignore_js_op>Java nio socket与as3 socket连接的应用实例.rar (9.61 K ...

  6. java nio实现非阻塞Socket通信实例

    服务器 package com.java.xiong.Net17; import java.io.IOException; import java.net.InetSocketAddress; imp ...

  7. Java NIO 聊天室实例

    最近写了个Java NIO聊天室聊天的程序,NIO学习起来比较困难的,我的代码能给大家起到一个抛砖引玉的作用! 服务端: package test.javanio; /** * @author * @ ...

  8. java NIO socket 通信实例

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zhuyijian135757/article/details/37672151 java Nio 通 ...

  9. java NIO 实例之多人聊天

    关键抽象 1.定义一个HashMap<String,SocketChannel>用户存储每个用户的管道. 2.服务端监听read事件,获取消息后轮询hashmap发送消息给用户模型内的所有 ...

随机推荐

  1. PHP-关于$_SERVER

    类似于Nginx中的请求头,所有header,都可以使用 $http_xxx来使用,比如$http_accept,甚至包括自定义的,比如,$http_x_forwarded_host proxy_se ...

  2. 联想笔记本如何关闭功能键,快捷键,如Fn+F1与F1切换

    在BIOS设置界面,进入 "Configuration" 菜单,查看是否有"Hotkey mode"选项,如果有的话,您由Enable更改为Disable,然后 ...

  3. python3.0_day9_scoket基础之篇

    一.socket简单介绍 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求 ...

  4. CSS中:display:none与visible:hidden的区别

    display:none视为不存在且不加载,即,不为被隐藏的对象保留其物理空间,即该对象在页面上彻底消失. visibility:hidden隐藏,但在浏览时保留位置,即,使对象在网页上不可见,但该对 ...

  5. Deferred和Promise之间有什么区别呢?

    一个promise就是一个由异步函数返回的对象. deferred对象就是jQuery的回调函数解决方案. 总结 jQuery 的ajax 就是返回一个promise 对象,里面含有done(), f ...

  6. JAVA包命名规范

    学习Java的童鞋们都知道,Java的包.类.接口.方法.变量.常量:JavaEE的三层模型等都有一套约定俗成的命名规则. 我学习每种语言都会关注相应的命名规则,一则体现自己比较专业:二来方便后检查, ...

  7. eclipse打包jar文件(含外部jar包)的方法

    在项目发布前,使用eclipse导出普通的jar包时,如果配置不好,在运行命令Java -jar /test.jar 时可能会出现如下三类错误信息: 1.no main manifest attrib ...

  8. 解决tableView分割线左边不到边的情况

    //解决tableView分割线左边不到边的情况//    if ([tableView respondsToSelector:@selector(setSeparatorInset:)]) {//  ...

  9. AngularJs中的服务

    一.angularJs中的简单服务应用 下面的例子让我们明白在AngularJs中如何去调用文件中的数据,从而将文件中的数据显示在页面上;改变url的地址,也可以去调用后台接口. 实例: <!D ...

  10. LitDB笔记

    首先,LitDB存储的不是json,而是bson.这里就有问题了,json是可以无限扩展的,但是bson不行.所有insert的时候需要使用实体类而不是hashtable.但是它又提供了一个叫做Bso ...