基于Javasocket NIO的一个CS聊天室

版权声明:本博客为记录本人学习过程而开,内容大多从网上学习与整理所得,若侵权请告知! https://blog.csdn.net/Fly_as_tadpole/article/details/84205161

不同的SelectableChannel所支持的操作是不同的。例如ServerSocketChannel代表一个ServerSocket,它就只支持OP_ACCEPT操作;

当Selector上注册的所有Channel都没有需要处理的IO操作的时候,select方法将会被阻塞,调用该方法的线程被阻塞。

int select();//默认阻塞
int select(long timeout);//设置超时
int selectNow();//立即返回

服务器上的所有的Channel(ServerSocketChannel 和 SocketChannel)都需要向selector注册。

服务器端需要使用ServerSocketChannel来监听客户端的连接请求。

ServerSocketChannel server = ServerSocketChannel.open();
InetSocketAddress isa = new InetSocketAddress("127.0.0.1",30000);
server.bind(isa);
server.configureBlocking(false);
server.register(selector,SelectionKey.OP_ACCEPT);

监听到客户端连接请求时,返回一个SocketChannel实例。


服务器端:

package com.nanhao.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;


public class Server {
    private Selector selector = null;
    static final int PORT = 30000;
    static final int BUFFSIZE = 1024;

    //定义实现编码解码的字符集对象
    private Charset charSet = Charset.forName("UTF-8");
    public void init()throws IOException{
        selector = Selector.open();
        ServerSocketChannel server = ServerSocketChannel.open();
        InetSocketAddress isa = new InetSocketAddress("127.0.0.1",PORT);
        server.bind(isa);
        //设置ServerSocket以非阻塞的方式进行
        server.configureBlocking(false);
        //将server注册到selector里面(每个套接字具有的注册功能)
        server.register(selector, SelectionKey.OP_ACCEPT);
        while(selector.select()>0){
            for(SelectionKey sk:selector.selectedKeys()){
                //一旦正在处理这个套接字,那么就要先从集合中删除这个套接字
                selector.selectedKeys().remove(sk);
                if(sk.isAcceptable()){
                    SocketChannel sc = server.accept();
                    //设置非阻塞模式
                    sc.configureBlocking(false);
                    //将该套接字注册到selector里面
                    sc.register(selector,SelectionKey.OP_READ);
                    //将之前的sk修改为准备接受其他请求
                    sk.interestOps(SelectionKey.OP_ACCEPT);
                }
                if(sk.isReadable()){
                    SocketChannel sc = (SocketChannel)sk.channel();
                    //定义准备接受数据的BUFFER
                    ByteBuffer buff = ByteBuffer.allocate(BUFFSIZE);
                    String context = "";
                    //开始读取数据
                    try{
                        while(sc.read(buff)>0){
                            buff.flip();
                            //实现解码
                            context += charSet.decode(buff);

                        }
                        System.out.println("读取的数据:"+context);
                        //将此套接字对应的channel设置成准备下一次读取
                        sk.interestOps(SelectionKey.OP_READ);
                        //如果捕获到该SK对应的channel出现异常的话,即表明该channel对应的client出现了问题
                        //所以从Selector里面取消sk的注册。
                    }catch(IOException io){
                        sk.cancel();
                        if(sk.channel() !=null){
                            sk.channel().close();
                        }


                    }
                    if(context.length()>0){
                        for(SelectionKey key :selector.keys()){
                            //获取key对应的channel
                            Channel targetChannel = key.channel();
                            if(targetChannel instanceof SocketChannel){
                                SocketChannel dest = (SocketChannel) targetChannel;
                                //实现编码
                                dest.write(charSet.encode(context));
                            }
                        }
                    }
                }
            }
        }
    }
    public static void main(String[]args) throws IOException{
        new Server().init();
    }

}

客户端:

package com.nanhao.client;

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.Scanner;

public class Client{
    private Selector selector =null;
    static final int PORT=30000;
    static final int BUFFSIZE = 1024;
    private Charset charset = Charset.forName("UTF-8");

    //创建客户端套接字
    private SocketChannel sc = null;
    public void init()throws IOException {
        selector = Selector.open();
        InetSocketAddress isa = new InetSocketAddress("127.0.0.1",PORT);
        //调用静态open方法创建连接到指定主机的SocketChannel
        sc = SocketChannel.open();
        //设置非阻塞的模式
        sc.configureBlocking(false);
        //注册到Selector
        sc.register(selector, SelectionKey.OP_READ);
        //启动读取服务器端数据库数据的线程
        new ClientThread().start();
        //创建键盘输入流
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNextLine()){
            String line = scanner.nextLine();
            //将键盘的内容写到SocketChannel
            sc.write(charset.encode(line));

        }
    }
    private class ClientThread extends Thread {

        public void run(){
            try{
                while(selector.select()>0){

                    //遍历每个IO可用的channel对应的SelectorKey
                    for(SelectionKey sk :selector.selectedKeys()){
                        selector.selectedKeys().remove(sk);
                        if(sk.isReadable()){
                            SocketChannel sc = (SocketChannel)sk.channel();
                            //创建buff
                            ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFSIZE);
                            String context = "";
                            while(sc.read(byteBuffer)>0){
                                //清空内存
                                byteBuffer.flip();
                                context += charset.decode(byteBuffer);
                            }
                            System.out.println("聊天信息:"+context);
                            sk.interestOps(SelectionKey.OP_READ);
                        }
                    }
                }
            }catch(IOException io){
                io.printStackTrace();

            }
        }

    }
    public static void main(String[]args) throws IOException{
        new Client().init();
    }
}

猜你喜欢

转载自blog.csdn.net/Fly_as_tadpole/article/details/84205161