基于JAVA NIO的socket通信

转载自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();
		}
	}
	@Override
	public 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();  
	 }  
	@Override
	public 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 block
				e.printStackTrace();
			}
		}
	}
}
demo下载路径:http://download.csdn.net/download/ditto_zhou/10045911


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值