nio 通讯

nio 服务端:

package nio.study.serverclient;

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

public class EchoSelectorServer {

//缓存大小

private static final int BUFSIZE = 256;

//超时时间

private static final int TIMEOUT = 3000;

//端口

private static final int PORT = 8888;

public static void main(String[] args) throws IOException {

Selector selector = Selector.open();

ServerSocketChannel listnChannel = ServerSocketChannel.open();

listnChannel.socket().bind(new InetSocketAddress(PORT));

// 只有非阻塞信道才可以注册选择器,因此需要将其配置为适当的状态

listnChannel.configureBlocking(false);

// 在注册过程中指出该信道可以进行“accept”操作

listnChannel.register(selector, SelectionKey.OP_ACCEPT);

Protocol protocol = new EchoProtocol(BUFSIZE);

while (true) {

if (selector.select(TIMEOUT) == 0) {

continue;

}

Iterator<SelectionKey> keyIter =

selector.selectedKeys().iterator();

while (keyIter.hasNext()) {

SelectionKey key = keyIter.next();

System.out.println("isAcceptable:"+key.isAcceptable());

if (key.isAcceptable()) {

protocol.handleAccept(key);

}

/*if(!key.isConnectable()){

System.out.println("cancel.......");

key.cancel();

}*/

System.out.println("isReadable: "+key.isReadable());

if (key.isReadable()) {

protocol.handleRead(key);

}

// System.out.println("isWritable: "+key.isWritable()

// +";isValid="+key.isValid());

try{

if (key.isWritable() && key.isValid()) {

protocol.handleWrite(key);

}

}catch(Exception e){

e.printStackTrace();

key.cancel();

if(null != key.channel() && key.channel().isOpen()){

key.channel();

}

}

// 由于select()操作只是向Selector所关联的键集合中添加元素

// 因此,如果不移除每个处理过的键,

// 它就会在下次调用select()方法时仍然保留在集合中

// 而且可能会有无用的操作来调用它。

keyIter.remove();

}

}

}

}

package nio.study.serverclient;

import java.io.IOException;

import java.nio.channels.SelectionKey;

public interface Protocol {

    public void handleAccept(SelectionKey key) throws IOException;

    public void handleRead(SelectionKey key) throws IOException;

    public void handleWrite(SelectionKey key) throws IOException;

}

package nio.study.serverclient;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

public class EchoProtocol implements Protocol {

private int bufsize;// 为每个客户端信道创建的缓冲区大小

public EchoProtocol(int bufsize) {

this.bufsize = bufsize;

}

public void handleAccept(SelectionKey key) throws IOException {

// channel()方法返回注册时用来创建的Channel,该Channel是一个ServerSocketChannel,

// 因为这是我们注册的唯一一种支持accept操作的信道,

// accept()方法为传入的连接返回一个SocketChannel实例。

SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();

// 这里无法注册阻塞式信道,必须是非阻塞式的

channel.configureBlocking(false);

// 可以通过SelectionKey类的selector()方法来获取相应的Selector。

// 我们根据指定大小创建了一个新的ByteBuffer实例,

// 并将其作为参数传递给register()方法。它将作为附件,与regiter()方法所返回的

// SelectionKey实例相关联。

channel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer

.allocateDirect(bufsize));

}

public void handleRead(SelectionKey key) throws IOException {

// 根据其支持数据读取操作可知,这是一个SocketChannel。

SocketChannel channel = (SocketChannel) key.channel();

// 建立连接后,有一个ByteBuffer附件加到该SelectionKey实例上,这个附件里面的内容将

// 会在发送的时候用到,附件始终是附着这个长连接上

ByteBuffer buf = (ByteBuffer) key.attachment();

long bytesRead = channel.read(buf);

// System.out.println("data::"+ new

// String(buf.array(),0,(int)bytesRead));

// 如果read()方法返回-1,则表示底层连接已经关闭,此时需要关闭信道。

// 关闭信道时,将从选择器的各种集合中移除与该信道关联的键。

if (bytesRead == -1) {

channel.close();

} else if (bytesRead > 0) {

// 将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作

buf.flip();

// 根据缓冲区可读字节数创建字节数组 //buf.remaining() 可读取长度

byte[] bytes = new byte[buf.remaining()];

// 将缓冲区可读字节数组复制到新建的数组中

buf.get(bytes);

String expression = new String(bytes, "UTF-8");

System.out.println("服务器收到消息:" + expression);

// 处理数据

buf.clear();

// 这里依然保留了信道的可读操作,虽然缓冲区中可能已经没有剩余空间了,

key.interestOps(SelectionKey.OP_WRITE);

}

}

public void handleWrite(SelectionKey key) throws IOException {

System.out.println("======write---");

// 附加到SelectionKey上的ByteBuffer包含了之前从信道中读取的数据。

// 读取客户端的数据 begin

//ByteBuffer buf = (ByteBuffer) key.attachment();

// 该方法用来修改缓冲区的内部状态,以指示write操作从什么地方获取数据,及还剩多少数据

// buf.flip();

//////// end

String back = "99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend";

back =back+back+back;

// 将消息编码为字节数组

byte[] bytes = back.getBytes();

// 根据数组容量创建ByteBuffer

ByteBuffer buf2 = ByteBuffer.allocate(bytes.length);

buf2.clear();

// 将字节数组复制到缓冲区

buf2.put(bytes);

// flip操作 ,从缓冲的0位置开始写

buf2.flip();

System.out.println("======write---2");

System.out.println("write:" + new String(bytes));

SocketChannel channel = (SocketChannel) key.channel();// 获取信道

if(buf2.hasRemaining()){

int len =channel.write(buf2);// 向信道中写数据

System.out.println("len:"+len);

}

if(null !=channel){

channel.close();

}

}

}

客户端:

package nio.study.serverclient;

import java.net.InetSocketAddress;

import java.net.SocketException;

import java.nio.ByteBuffer;

import java.nio.channels.SocketChannel;

public class TCPEchoClientNoblocking {

public void send(int i) throws Exception{

String server = "127.0.0.1";

String mgs = i+"1234567890qwertyuiopasdfghjklmnbvcxzqwertyuiopasdfghjklmnbvcxz";

      byte[] data = mgs.getBytes();

      int servPort = 8888;

      SocketChannel clntChan =SocketChannel.open();

      clntChan.configureBlocking(false);

      //我们通过持续调用finishConnect()方法来“轮询”连接状态,该方法在连接成功建立之前

       //一直返回false。打印操作显示了在等待连接建立的过程中,程序还可以执行其他任务

      if (!clntChan.connect(new InetSocketAddress(server,servPort))) {

          while (!clntChan.finishConnect()) 

          }

      }

      ByteBuffer writeBuf =ByteBuffer.wrap(data);

      ByteBuffer readBuf =ByteBuffer.allocate(data.length);

      int bytesRcvd =0;

      int topNum =0;

     

      StringBuffer sb = new StringBuffer(64);

      while (bytesRcvd >= topNum) {

      if (writeBuf.hasRemaining()) {

      

      clntChan.write(writeBuf);

      

      }

      topNum = bytesRcvd;

      bytesRcvd =clntChan.read(readBuf);

      

    // 

      readBuf.flip();

      if(bytesRcvd >0){

       sb.append(new String(readBuf.array(), 0,readBuf.limit()));

      System.out.println("len;;;"+bytesRcvd +readBuf.hasRemaining());

      }

      readBuf.clear();

      }

      

      System.out.println("rev::"+sb.toString());

      

      clntChan.close();

}

    public static void main(String[] args) throws Exception {

    for(int i=0;i<300;i++){

    TCPEchoClientNoblocking t = new TCPEchoClientNoblocking();

   

    t.send(i);

    }

       

    }

}

日志:

服务端:

isAcceptable:true

isReadable: false

isAcceptable:false

isReadable: true

服务器收到消息:295ABCDEFGHIJKLMNOPQRSTUVWXYZ789564559

isAcceptable:false

isReadable: false

======write---

======write---2

write:99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend

len:270

isAcceptable:true

isReadable: false

isAcceptable:false

isReadable: true

服务器收到消息:296ABCDEFGHIJKLMNOPQRSTUVWXYZ789564559

isAcceptable:false

isReadable: false

======write---

======write---2

write:99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend

len:270

客户端:

len;;;38true

len;;;38true

len;;;38true

len;;;38true

len;;;38true

len;;;38true

len;;;38true

len;;;4true

rev::99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend99999server1234567890123456789123456789=99999999999999999999=qwertyuioplkjhgfdsazxcvbnmend

猜你喜欢

转载自gjp014.iteye.com/blog/2367040
NIO