package main.java.com.founder.study.javaio.socket.nio;
import java.io.Closeable;
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;
import java.nio.charset.Charset;
import java.util.Set;
/**
* Created by yan on 2020/6/30.
*/
public class NioChatServer {
private static final int DEFAULT_PORT = 8888;
private static final String QUIT = "quit";
private static final int BUFFER = 1024;
private ServerSocketChannel server;
private Selector selector;
private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER);
private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER);
private Charset charset = Charset.forName("UTF-8");
private int port;
public NioChatServer(int port){
this.port = port;
}
public NioChatServer(){
this(DEFAULT_PORT);
}
public void start(){
try {
server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
//把serverSocketChannel的accept监听事件注册到selector上
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("启动服务器监听端口"+port);
while(true){
//阻塞到至少有一个通道在你注册的事件上就绪了
//方法返回的int值表示有多少通道已经就绪,是自上次调用select()方法后有多少通道变成就绪状态
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for(SelectionKey key: selectionKeys){
//处理被触发的事件
handle(key);
}
//处理完事件清空
selectionKeys.clear();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
close(selector);
}
}
private void handle(SelectionKey key) throws IOException {
//Accept事件 和客户端建立了连接
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_READ);
System.out.println("客户端"+client.socket().getPort()+"建立连接");
}
//read事件 客户端发送了消息
else if (key.isReadable()){
SocketChannel client = (SocketChannel)key.channel();
String fwdMsg = recive(client);
if(fwdMsg.isEmpty()){
//客户端异常
key.cancel();
//让处在阻塞状态的select()方法立刻返回
//该方法使得选择器上的第一个还没有返回的选择操作立即返回
selector.wakeup();
}else{
//转发给其它人
forwardMessage(client,fwdMsg);
//检查用户是否退出
if(readeyToQuit(fwdMsg)){
key.cancel();
selector.wakeup();
System.out.println("客户端"+client.socket().getPort()+"已经断开");
}
}
}
}
private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException {
for(SelectionKey key: selector.keys()){
if(key.channel() instanceof ServerSocketChannel){
continue;
}
if(key.isValid() && !client.equals(key.channel())){
wBuffer.clear();
wBuffer.put(charset.encode(fwdMsg));
wBuffer.flip();
while (wBuffer.hasRemaining()){
((SocketChannel)key.channel()).write(wBuffer);
}
}
}
}
private String recive(SocketChannel client) throws IOException {
rBuffer.clear();
while (client.read(rBuffer) > 0);
rBuffer.flip();
return String.valueOf(charset.decode(rBuffer));
}
public boolean readeyToQuit(String msg){
return QUIT.equals(msg);
}
private static void close(Closeable closeable) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NioChatServer nioChatServer = new NioChatServer(7777);
nioChatServer.start();
}
}
package main.java.com.founder.study.javaio.socket.nio;
import java.io.Closeable;
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.Set;
/**
* Created by yanon 2020/6/30.
*/
public class NioChatClient {
final String QUIT = "quit" ;
static final String DEFAULT_SERVER_HOST = "127.0.0.1";
static final int DEFAULT_PORT = 8888;
private static final int BUFFER = 1024;
private SocketChannel client;
private Selector selector;
private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER);
private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER);
private Charset charset = Charset.forName("UTF-8");
private String host;
private int port;
public NioChatClient(){
this(DEFAULT_SERVER_HOST,DEFAULT_PORT);
}
public NioChatClient(String host,int port){
this.host=host;
this.port=port;
}
public void start(){
try {
client = SocketChannel.open();
client.configureBlocking(false);
selector = Selector.open();
client.register(selector, SelectionKey.OP_CONNECT);
client.connect(new InetSocketAddress(host,port));
while(true){
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for(SelectionKey key: selectionKeys){
//处理被触发的事件
handle(key);
}
//处理完事件清空
selectionKeys.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void handle(SelectionKey key) throws IOException {
//connet事件 --连接就绪事件
if(key.isConnectable()){
SocketChannel client = (SocketChannel)key.channel();
if(client.isConnectionPending()){
client.finishConnect();
//处理用户的输入
new Thread(new UserInputHandler(this)).start();
}
client.register(selector,SelectionKey.OP_READ);
}
//read事件--服务器转发消息
else if(key.isReadable()){
SocketChannel client = (SocketChannel)key.channel();
String msg = recive(client);
if(msg.isEmpty()){
//服务器异常
close(selector);
}else{
System.out.println(msg);
}
}
}
private String recive(SocketChannel client) throws IOException {
rBuffer.clear();
while(client.read(rBuffer)>0);
return String.valueOf(charset.decode(rBuffer));
}
public void send(String msg) throws IOException {
wBuffer.clear();
wBuffer.put(charset.encode(msg));
wBuffer.flip();
while(wBuffer.hasRemaining()){
client.write(wBuffer);
}
//检查用户是否退出
if(readeyToQuit(msg)){
close(selector);
}
}
public boolean readeyToQuit(String msg){
return QUIT.equals(msg);
}
private static void close(Closeable closeable) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package main.java.com.founder.study.javaio.socket.nio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* Created by yan on 2020/6/28.
*/
public class UserInputHandler implements Runnable{
final String QUIT = "quit" ;
private NioChatClient chatClient;
public UserInputHandler( NioChatClient chatClient ){
this.chatClient = chatClient ;
}
@Override
public void run() {
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
while (true){
//先发给服务端
try {
chatClient.send(consoleReader.readLine());
chatClient.readeyToQuit(consoleReader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}