NIO与BIO例子

最近学习java系统间通信BIO、NIO写了两个例子,如果有错误麻烦指正!
1.BIO

客户端代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SocketClient implements Runnable {



    public static void main(String[] args) {

        ExecutorService executor=Executors.newCachedThreadPool();
        for(int i=0;i<=10;i++){
            executor.execute(new SocketClient(i));
        }

    }
    private int i;
    public SocketClient(int i){
        this.i=i;
    }

    @Override
    public void run() {
     try {
            Socket socket=new Socket("127.0.0.1",8080);
            socket.setSoTimeout(500);
            System.out.println("client:"+i+"准备发送请求!!");
            PrintWriter write=new PrintWriter(socket.getOutputStream());
            BufferedReader read=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            write.println("Socket:"+i+"\rexit"); 
            write.flush();
            System.out.println("发送完毕!Socket:"+i);
            String readline=read.readLine();
            while(readline!=null){
                System.out.println("接收到服务端发送消息:"+readline);
                readline=read.readLine();
            }
            System.out.println("客户端"+i+"准备关闭!!");
            socket.close();
            write.close();
            read.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

服务器端代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

public class SocketService implements Runnable{

    public static void main(String[] args) {
        try {
            SocketAddress address = new InetSocketAddress("127.0.0.1", 8080);  
            ServerSocket sever=new ServerSocket();
            sever.bind(address);
            System.out.println("创建服务端完毕!!");
            while(true){
                Socket client= sever.accept();
                new Thread(new SocketService(client)).start();  
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    private Socket socket;

    public SocketService(Socket socket) {
        this.socket=socket;
    }

    @Override
    public void run() {
        try{
            socket.setSoTimeout(500);
            System.out.println("接收到socket请求"+System.currentTimeMillis());
            PrintWriter write=new PrintWriter(socket.getOutputStream());
            BufferedReader read=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String readline=read.readLine();
            while(readline!=null){
                write.println("服务端返回消息:"+readline);
                write.flush();
                System.out.println("接收到客户端消息:"+readline);
                readline=read.readLine();
                if(readline.equals("exit")) break;
            }   
            System.out.println("处理完毕!准备关闭流!!");
            socket.close();
            write.close();
            read.close();
        }catch(Exception e){
            e.printStackTrace();
        }


    }
}

  在写BIO客户端的时候没遇到啥问题服务器端倒是遇到两个问题。
1.服务器端如何判断流读取完毕?
  服务器端的socket.getInputStream() 得到IO对象read,read会阻塞读取,如果客户端的socket发送流完毕,但是没有关闭自身socket请求时。那么服务器端接收完毕客户端的流以后,将会继续循阻塞读取流。因为只有socket关闭以后read.readLine() 才会等于null,
迫不得已只能在客户端发送流时候自定义结尾符号exit,至于客户端的readline!=null为啥不需要结尾符,因为服务器端在处理完毕请求以后关闭了socket。

    while(readline!=null){
            write.println("服务端返回消息:"+readline);
            write.flush();
            System.out.println("接收到客户端消息:"+readline);
            readline=read.readLine();
            if(readline.equals("exit")) break;
    }

2.服务器端因为请求超时,线程直接停止了。
  服务器设置了超时请求时间socket.setSoTimeout(500) 如果处理客户端请求和主线程放在同一个线程,一旦请求超时当前线程将被停止。当然不可能为了一个请求的超时而导致服务器的停止(还有其它的异常也会导致线程停止),所以需要开辟一个新的线程处理客户端请求。

NIO
  NIO与BIO的优势就在于 服务端可以使用单线程处理客户端的多线程并发请求,
不像BIO需要为每一个请求开辟一个线程,增加了服务器的负担。

客户端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
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.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import sun.nio.ch.DirectBuffer;  

public class SocketClient implements Runnable {


    public static void main(String[] args) {
        ExecutorService executor=Executors.newCachedThreadPool();
        for(int i=0;i<=3;i++){
            executor.execute(new SocketClient(i));
        }
//      new Thread(new SocketClient(1)).start();
    }
    int i;
    public SocketClient(int i) {
        this.i=i;
    }
    @Override
    public void run() {

        try {
            SocketChannel channel=SocketChannel.open();
            SocketAddress address=new InetSocketAddress("127.0.0.1", 8080);
            channel.configureBlocking(false);
            channel.connect(address);
            Selector selector=Selector.open();
            channel.register(selector, SelectionKey.OP_CONNECT);
            while(true){
                int status=selector.select(500);//将会阻塞感兴趣的IO发生
                 //selectNow不会阻塞,调用后立即返回状态 需要在循环中不停获取
                 //status=selector.selectNow();
                if(status>0){
                  System.out.println("channel:"+i+"获得服务器响应,响应状态"+status);
                  Set<SelectionKey> keys=selector.selectedKeys();
                  Iterator<SelectionKey> its=keys.iterator();
                  while(its.hasNext()){
                      SelectionKey key=its.next();
                      if(key.isConnectable()){
                          SocketChannel sc=(SocketChannel)key.channel();
                          sc.finishConnect();//阻塞到客户端 与服务器端建立连接
                          sc.configureBlocking(false);
                          System.out.println("channel:"+i+"连接建立完毕!!");
                          ByteBuffer bf=ByteBuffer.wrap(("channel:"+i).getBytes("utf-8"));
                          int size=sc.write(bf);
                          if(size<=0){
                              key.interestOps(SelectionKey.OP_WRITE); //注册写事件
                              sc.write(bf);
                              key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);//取消注册写事件  
                          }
                          sc.register(selector,SelectionKey.OP_READ);
                      }else if(key.isReadable()){
                          SocketChannel sc=(SocketChannel)key.channel();
                          ByteBuffer buffer=ByteBuffer.allocateDirect(1024);
                          String msg="";
                          while(sc.read(buffer)>0){
                              buffer.flip();
                              msg+=Charset.forName("utf-8").decode(buffer).toString();
                          }
                          ((DirectBuffer)buffer).cleaner().clean();
                          System.out.println("客户端"+i+"接收到服务器消息"+msg);
                      }
                  }
                }
                status=-1;//因为设置了500毫秒的超时 时间
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("线程执行完毕!");
    }

}

服务器端

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.Iterator;
import java.util.Set;

import sun.nio.ch.DirectBuffer;

public class SocketService{
     public static void main(String[] args) {
        try{
            ServerSocketChannel server=ServerSocketChannel.open();
            server.configureBlocking(false);
            server.socket().bind(new InetSocketAddress("127.0.0.1", 8080));
            Selector selector=Selector.open();
            server.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务器启动完毕!!");
            while(true){
                int status=selector.select();//不建议设置超时 时间
                if(status>0){
                       Iterator<SelectionKey> keys=selector.selectedKeys().iterator();
                       while(keys.hasNext()){
                           SelectionKey key=keys.next();
                           keys.remove();
                           if(key.isAcceptable()){
                              System.out.println("可接受客户端请求:"+status);
                              ServerSocketChannel ssc=(ServerSocketChannel)key.channel();
                              SocketChannel channel=ssc.accept();
                              ByteBuffer bf=ByteBuffer.wrap(channel.getRemoteAddress().toString().getBytes("utf-8"));
                              int size=channel.write(bf);
                              if(size<=0){
                                  key.interestOps(SelectionKey.OP_WRITE); //注册写事件
                                  channel.write(bf);
                                  key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);//取消注册写事件  
                              }
                              channel.configureBlocking(false);
                              channel.register(selector,SelectionKey.OP_READ);
                           }else if(key.isReadable()){ 
                              System.out.println("可读客户端流:"+status);
                              SocketChannel sc=(SocketChannel)key.channel();
                              ByteBuffer buffer=ByteBuffer.allocateDirect(1024);
                              String msg="";
                              while(sc.read(buffer)>0){
                                  buffer.flip();
                                  msg+=Charset.forName("utf-8").decode(buffer).toString();
                              }
                              ((DirectBuffer)buffer).cleaner().clean();
                              System.out.println("服务端接收到客户端"+sc.getRemoteAddress()+"消息"+msg);
                           }
                       }
                }

            }



        }catch(Exception e){
            e.printStackTrace();
        }
     }

}

在编写NIO程序的时候一个问题
  1.channel.register(selector, SelectionKey.OP_CONNECT) 一个NIO程序可以有多个信道却只有一个选择器,同时向一个选择器多次注册那么selector.select() 将触发哪一个注册事件呢?
  在AbstractSelectableChannel.class里头找到了答案,该类是channelsocket的父类意味着每一个channel都有一个AbstractSelectableChannel对象,同一个channel在同一个selector注册感兴趣事件,都会覆盖之前的在selector上注册的事件。不同的channel对于同一个selector注册将会触发addKey(k) 事件,如果两个不同的channel对同一个selector进行注册那么selector.select() 将会得到拥有两个selectionKey的Set集合。顺便说一下selector不会重复处理同一个事件,比如我客户端注册了读感兴趣事件,服务端向客户端第一次发送了HAHA字符串,客户端通过selector.select() 获取到selectionKey对象后,以后将无法再获取到HAHA,服务器端再次发送XIXI客户端成功接收。

    public final SelectionKey register(Selector sel, int ops,
                                       Object att)
        throws ClosedChannelException
    {
        if (!isOpen())
            throw new ClosedChannelException();
        if ((ops & ~validOps()) != 0)
            throw new IllegalArgumentException();
        synchronized (regLock) {
            if (blocking)
                throw new IllegalBlockingModeException();
            SelectionKey k = findKey(sel);//查找selector是否存在
            if (k != null) {
                k.interestOps(ops);
                k.attach(att);
            }
            if (k == null) {
                // New registration
                k = ((AbstractSelector)sel).register(this, ops, att);
                addKey(k);//添加一个selectionkey
            }
            return k;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值