1. 服务端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set; public class GroupChatServer {
private Selector selector;
private ServerSocketChannel listenChannel;
private static final int PORT = 8888; // 构造 器
public GroupChatServer() {
try {
selector = Selector.open();
listenChannel = ServerSocketChannel.open();
// 绑定端口
listenChannel.socket().bind(new InetSocketAddress(PORT));
// 设置非阻塞模式
listenChannel.configureBlocking(false);
// 将listenChannel注册到selector
listenChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 监听
*/
public void listen() {
try {
while (true) {
//select(2000) 只阻塞2秒
// int count = selector.select(2000);
// select()方法一直阻塞
int count = selector.select();
if (count > 0) {
// 遍历得到所有的SelectionKey集合 Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();
while (keysIterator.hasNext()) {
// 处理每个selectionKey
SelectionKey selectionKey = keysIterator.next();
// 监听到连接事件
if (selectionKey.isAcceptable()) {
SocketChannel socketChannel = listenChannel.accept();
socketChannel.configureBlocking(false);
//注册
socketChannel.register(selector, SelectionKey.OP_READ);
// 提示上线
System.out.println(socketChannel.getRemoteAddress() + "上线");
}
// 通道可读事件
if (selectionKey.isReadable()) {
// todo 处理读
readMessage(selectionKey);
}
// 删除key,防止重复处理
keysIterator.remove();
}
} else {
// System.out.println("等待中。。。。");
}
}
} catch (Exception e) {
e.printStackTrace();
} } /**
* 读取客户端消息
*
* @param key
*/
public void readMessage(SelectionKey key) {
SocketChannel socketChannel = null;
try {
socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int read = socketChannel.read(byteBuffer);
// 根据read值做处理,如果read > 0 说明真的读取到数据了
if (read > 0) {
String message = new String(byteBuffer.array());
System.out.println("Form Client : " + message);
// todo 向其它客户端 转发消息
sendInfoToOtherClient(message, socketChannel);
} } catch (IOException e) {
try {
System.out.println(socketChannel.getRemoteAddress() + "离线了。。。");
// 离线之后取消注册
key.cancel();
// 关闭channel
socketChannel.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} /**
* 转发消息到其它客户端
*
* @param message 转发的消息
* @param self : 排除的channel
*/
public void sendInfoToOtherClient(String message, SocketChannel self) throws IOException {
System.out.println("服务器转发消息中。。。");
// 遍历所有注册到selector上的socketChannel,并排除自己
Set<SelectionKey> keys = selector.keys();
for (SelectionKey key : keys) {
// 通过key取出对应的SocketChannel
Channel targetChannel = key.channel();
// 排除自己
if (targetChannel instanceof SocketChannel && targetChannel != self) {
SocketChannel target = (SocketChannel) targetChannel;
// 将message 存储到buffer
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
// 将buffer中的数据写入通道
target.write(buffer);
} }
} public static void main(String[] args) {
//创建对象
GroupChatServer chatServer = new GroupChatServer();
chatServer.listen();
}
}

2. 客户端

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;
import java.util.Set; public class GroupChatClient {
// 服务器ip
private final String HOST = "127.0.0.1";
// 服务器端口
private final int PORT = 8888;
private Selector selector;
private SocketChannel socketChannel;
private String username; public GroupChatClient() {
try {
selector = Selector.open();
socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
username = socketChannel.getLocalAddress().toString().substring(1);
System.out.println(username + " ok!");
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 向服务端发送消息
*
* @param info 消息内容
*/
public void sendInfo(String info) {
info = username + "说:" + info;
try {
socketChannel.write(ByteBuffer.wrap(info.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 从服务端 读取消息
*/
public void readInfo() {
try {
int readChannels = selector.select(2000);
if (readChannels > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyInterator = keys.iterator(); while (keyInterator.hasNext()) {
SelectionKey selectionKey = keyInterator.next();
if (selectionKey.isReadable()) {
// 得到读相关通道
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
socketChannel.read(byteBuffer);
//把buffer中的数据转成字符 串
String messge = new String(byteBuffer.array());
System.out.println( messge.trim());
}
//删除当前selectionKey
keyInterator.remove();
} } else {
// System.out.println("没有可用通道。。。。") }
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
// 启动客户端
GroupChatClient chatClient = new GroupChatClient(); // 启动一个线程每隔2秒读取从服务端发送过来的数据
new Thread() {
@Override
public void run() {
while (true) {
chatClient.readInfo();
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start(); // 发送数据到服务端
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String info = scanner.next();
chatClient.sendInfo(info);
} }
}

  

NIO编程之多客户端聊天系统的更多相关文章

  1. JDK NIO编程

    我们首先需要澄清一个概念:NIO到底是什么的简称?有人称之为New I/O,因为它相对于之前的I/O类库是新增的,所以被称为New I/O,这是它的官方叫法.但是,由于之前老的I/O类库是阻塞I/O, ...

  2. Reactor 典型的 NIO 编程模型

    Doug Lea 在 Scalable IO in Java 的 PPT 中描述了 Reactor 编程模型的思想,大部分 NIO 框架和一些中间件的 NIO 编程都与它一样或是它的变体.本文结合 P ...

  3. Java IO编程全解(四)——NIO编程

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7793964.html 前面讲到:Java IO编程全解(三)——伪异步IO编程 NIO,即New I/O,这 ...

  4. NIO和IO(BIO)的区别及NIO编程介绍

    IO(BIO)和NIO的区别:其本质就是阻塞和非阻塞的区别. 阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,那么程序就一直等着,直到传输完毕为止. 非阻塞概念:应用程序直接可以获取已经 ...

  5. Nio编程模型总结

    终于,这两天的考试熬过去了, 兴致冲冲的来整理笔记来, 这篇博客是我近几天的NIO印象笔记汇总,记录了对Selector及Selector的重要参数的理解,对Channel的理解,常见的Channel ...

  6. NIO 编程模型

    NIO 编程模型 Doug Lea 在 Scalable IO in Java 的 PPT 中描述了 Reactor 编程模型的思想,大部分 NIO 框架和一些中间件的 NIO 编程都与它一样或是它的 ...

  7. 手动搭建I/O网络通信框架3:NIO编程模型,升级改造聊天室

    第一章:手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊 第二章:手动搭建I/O网络通信框架2:BIO编程模型实现群聊 在第二章中用BIO编程模型,简单的实现了一 ...

  8. 深入学习Netty(2)——传统NIO编程

    前言 学习Netty编程,避免不了从了解Java 的NIO编程开始,这样才能通过比较让我们对Netty有更深的了解,才能知道Netty大大的好处.传统的NIO编程code起来比较麻烦,甚至有遗留Bug ...

  9. 循序渐进Socket网络编程(多客户端、信息共享、文件传输)

    循序渐进Socket网络编程(多客户端.信息共享.文件传输) 前言:在最近一个即将结束的项目中使用到了Socket编程,用于调用另一系统进行处理并返回数据.故把Socket的基础知识总结梳理一遍. 1 ...

随机推荐

  1. HTML-空格字符实体

      不换行空格,全称No-Break Space,它是最常见和我们使用最多的空格,大多数的人可能只接触了 ,它是按下space键产生的空格.在HTML中,如果你用空格键产生此空格,空格是不会累加的(只 ...

  2. (转)grep命令

    1.作用Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来.grep全称是Global Regular Expression Print,表示全局 ...

  3. sc 使用了配置中心后,如何设置远程和本地配置的优先级

    在 spring 中,如何获取一个 key 的值? applicationContext.getEnvironment().getProperty("swagger.show") ...

  4. js面向对象程序设计之继承

    在面向对象语言中继承分成两种:接口继承和实现继承.解释一下,接口继承只继承方法的签名,而实现继承则是继承实际的方法.但是ECMAScript中的函数没有签名所以无法进行接口继承,只能是实现实现继承.而 ...

  5. 树莓派3b折腾指南

    最近入手了树梅派3b,搭建了宿舍共享的热点和NAS,搭建透明代理科学上网的计划还没实现. 先报个价,一套折腾下来花了500大洋,树梅派3加外壳200,电源加内存卡100,显示器淘宝二手150,有线键鼠 ...

  6. Java ——Number & Math 类 装箱 拆箱 代码块

    本节重点思维导图 当需要使用数字的时候,我们通常使用内置数据类型,如:byte.int.long.double 等 int a = 5000; float b = 13.65f; byte c = 0 ...

  7. 【SD系列】SAP 创建销售订单-用外部给号的方法步骤

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[SD系列]SAP 创建销售订单-用外部给号的方 ...

  8. 第一章:Java语言概述与环境开发

    1.计算机高级语言按程序的执行方式可以分为编译型和解释型两种: 2.JAVA程序的执行过程必须经过先编译后解释两个步骤: 3.JAVA语言里负责执行字节码文件的是JAVA虚拟机 (Java Virtu ...

  9. vue中的provide/inject讲解

    最近在看element-ui的源码,发现了一个这样的属性:inject.遂查看官网provider/inject provider/inject:简单的来说就是在父组件中通过provider来提供变量 ...

  10. python基础-11 socket,IO多路复用,select伪造多线程,select读写分离。socketserver源码分析

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