基于JAVA NIO的socket通信

来源:互联网 发布:知柏地黄丸 多少钱 编辑:程序博客网 时间:2024/05/27 16:40

转载自http://blog.csdn.net/z69183787/article/details/52945595

自己做了点修改,将连接由客户端一方关闭

1. 公共类,处理传递中文字符时候的乱码问题

package cn.erong.mfo.utils;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.charset.Charset;import java.nio.charset.CharsetDecoder;import java.nio.charset.CharsetEncoder;public class Constants {public static int readCapacity = 1024;public static Charset charset = Charset.forName("UTF-8");public static CharsetEncoder encoder = charset.newEncoder();public static CharsetDecoder decoder = charset.newDecoder();public static ByteBuffer encoder(CharBuffer charbuffer) throws IOException{return encoder.encode(charbuffer);}public static CharBuffer decoder(ByteBuffer buffer) throws IOException{return decoder.decode(buffer);}}
java nio 中处理乱码的类charset ,创建 Charset charset = Charset.forName("指定编码格式")

charsetencoder将字符缓冲区,编码为字节缓存区

charsetdecoder 将字节缓存区,解码为字符缓冲区

2. 服务端

package cn.erong.mfo.utils;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;public class NioServer extends Thread{public static void main(String[] args) {NioServer server = new NioServer("127.0.0.1", 23000);server.start();}private InetSocketAddress address;private ByteBuffer readBuffer;private Selector selector;public NioServer(){}public NioServer(String localhost,int port){address = new InetSocketAddress(localhost, port);}private void init(){readBuffer = ByteBuffer.allocate(Constants.readCapacity); try {selector = Selector.open();ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.configureBlocking(false);serverChannel.bind(address);//注册到信道上serverChannel.register(selector, SelectionKey.OP_ACCEPT);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {try {init();while(true){int num = selector.select();if(num>0){Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()){SelectionKey sk = iterator.next();handlerKey(sk);//注意将键移除iterator.remove();}}}} catch (IOException e) {e.printStackTrace();}}private void handlerKey(SelectionKey sk) {if(sk.isAcceptable()){accept(sk);}else if(sk.isReadable()){readMsg(sk);}}public void accept(SelectionKey sk) {try {ServerSocketChannel serverChannel= (ServerSocketChannel)sk.channel();SocketChannel channel = serverChannel.accept();if(channel!=null){//将信道一定注册为非阻塞的channel.configureBlocking(false);channel.register(sk.selector(), SelectionKey.OP_READ);}} catch (IOException e) {e.printStackTrace();}}public void readMsg(SelectionKey sk) {SocketChannel channel = null;try {channel = (SocketChannel) sk.channel();readBuffer.clear();int num = channel.read(readBuffer);if(num>0){//将数据取出来readBuffer.flip();System.out.println("接受到的数据:"+num+"字节");CharBuffer buffer =  Constants.decoder(readBuffer);String recevieMsg = buffer.toString();System.out.println(recevieMsg);String answer = getAnswer(recevieMsg);CharBuffer cbuffer = CharBuffer.wrap(answer.toCharArray());channel.write(Constants.encoder(cbuffer));}} catch (IOException e) {e.printStackTrace();}}public void writeMsg(SelectionKey sk) throws IOException{}private String getAnswer(String question){          String answer = null;                     switch(question){          case "who":              answer = "我是小娜\n";              break;          case "what":              answer = "我是来帮你解闷的\n";              break;          case "where":              answer = "我来自外太空\n";              break;          case "hi":              answer = "hello\n";              break;          case "bye":              answer = "88\n";              break;          default:                  answer = "请输入 who, 或者what, 或者where";          }                     return answer;      }  }

note:

1. 在将选择器注册到信道channel前,必须将channel设置为非阻塞,否则会抛出IllegalBlockingModeException

2. 对于信道的read,write等操作,都必须注意在此之前,操作对应的缓冲区使用clear(),flip()

read --->>> clear(): 将position设置为0,limit设置为capacity

write--->>> flip(); 将limit设置为position,position设置为0

decode(byteBuffer),会从字节缓存区中取出数据放到生成的charBuffer中,这样调用之前,也要调用

缓冲区的flip方法

3. 在对select.selectionkeys()进行遍历,操作每个key,之后,需要将key从键集中移除,否则会阻塞,无法获取新的连接。

4.不建议使用new String(byteBuffer.array()) 去解码缓存区,因为.array()返回的是整个缓存区的内容,而decoder是返回position到limit间的数据。

3. 客户端

package cn.erong.mfo.utils;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.CharBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.Iterator;import java.util.Random;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;public class NioClient implements Runnable{public static void main(String[] args) {for(int i=0;i<5;i++){Runnable client = new NioClient("127.0.0.1", 23000);Thread t = new Thread(client);t.start();}}private BlockingQueue<String >words;private Random random;private InetSocketAddress address;public NioClient (String localname,int port){address = new InetSocketAddress(localname, port);}private void init(){words = new ArrayBlockingQueue<>(5);try {              words.put("hi");              words.put("who");              words.put("what");              words.put("where");              words.put("bye");          } catch (InterruptedException e) {              e.printStackTrace();          }            random = new Random();  } private String getWord(){          return words.poll();   }  @Overridepublic void run() {init();SocketChannel channel = null;Selector selector = null;try {selector = Selector.open();channel = SocketChannel.open();channel.configureBlocking(false);channel.connect(address);channel.register(selector, SelectionKey.OP_CONNECT);boolean isOver = false;while(!isOver){int num = selector.select();if(num>0){Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while(iterator.hasNext()){SelectionKey sk = iterator.next();if(sk.isConnectable()){if(channel.isConnectionPending()){if(channel.finishConnect()){//只有成功连接,才能注册read事件sk.interestOps(SelectionKey.OP_READ);//向服务端写数据channel.write(Constants.encoder(CharBuffer.wrap(getWord())));}else{sk.cancel();// 将这个键注销}}}else if(sk.isReadable()){ByteBuffer buffer = ByteBuffer.allocate(1024);int bnum =  channel.read(buffer);if(bnum>0){buffer.flip();CharBuffer cbuffer = Constants.decoder(buffer);System.out.println(Thread.currentThread().getId()+"客户端收到数据:"+cbuffer.toString());String word = getWord();                          if(word != null){                              channel.write(Constants.encoder(CharBuffer.wrap(word)));                          }                          else{                              isOver = true;                          }                          try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}iterator.remove();}}}} catch (IOException e) {e.printStackTrace();}finally{try {if(channel!=null){channel.close();}if(selector!=null){selector.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
demo下载路径:http://download.csdn.net/download/ditto_zhou/10045911


原创粉丝点击