NIO中的服务器端和客户端的连接

NIO客户端:

package src.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

/**
 * @author EddyYang
 *
 */
public class NBClient {
	boolean ifExit = false;
	SocketChannel sc ;
	public SocketChannel connect(String host,int port)throws UnknownHostException,IOException{
		System.out.println("客户端开始运行...");
		sc = SocketChannel.open();
		sc.configureBlocking(false);
		InetSocketAddress inet_socket_addr = new InetSocketAddress(host,port);
		sc.connect(inet_socket_addr);
		if(sc.isConnectionPending()){
			sc.finishConnect();
		}
		return sc;
	}
	public void request(SocketChannel sc)throws IOException{
		System.out.println("请输入字符串");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String msg = null;
		ByteBuffer bytebuff = ByteBuffer.allocate(1024);
		int nbytes = 0;
		msg = in.readLine();
		bytebuff = ByteBuffer.wrap(msg.getBytes());
		nbytes = sc.write(bytebuff);		//把接受到的数据传给服务器。
		if(msg.equalsIgnoreCase("exit")){
			ifExit = true;
		}
	}
	public String getResponse(SocketChannel sc)throws IOException{
		ByteBuffer buff= ByteBuffer.allocate(1024);
		buff.clear();
		sc.read(buff);
		buff.flip();
		Charset charset = Charset.forName("GBK");
		CharsetDecoder decoder = charset.newDecoder();
		CharBuffer charbuff = decoder.decode(buff);
		return charbuff.toString();
	}
	public static void main(String[] args){
		try{
			NBClient nbc = new NBClient();
			SocketChannel sc = nbc.connect("127.0.0.1",8888);
			while(!nbc.ifExit){
				/*
				 * 发送数据
				 */
				nbc.request(sc);
				/*
				 * 接受数据
				 * 因为是不阻塞,有时候没有返回数据就处理了(但是可以下次在读取)
				 */
				String msg = nbc.getResponse(sc);
				System.out.println("从服务器返回数据:"+msg);
			}
		}catch(IOException e){
			e.printStackTrace();
		}
	}
}


 

NIO服务器端:

 

package src.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.Set;

public class NBServer {
	private ServerSocketChannel server_socket_channel ;
	private Selector selector ;
	
	/*
	 * response()方法用作对客户端请求的一个响应。
	 * SelectionKey 代表从集合中取得的消息。
	 * A token representing the registration of a selectableChannel with a selector.
	 * A selection key is created each time a channel is registered with a selector.
	 * (每一次当一个通道被注册到一个选择器时,一个SelectionKey对象就被创建)
	 */
	public void response(SelectionKey k,String msg)throws IOException{
		/*
		 * SelectionKey.channel():
		 * Returns the channel for which this key was created.
		 */
		SocketChannel socket_channel = (SocketChannel)k.channel();
		/*
		 * 运用allocate(1024)方法创建一个1024大小的字节缓冲区
		 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		buffer.flip();				//调整3个指针。
		/*
		 * 用 wrap()方法:
		 * wraps a byte array into a buffer.
		 */
		buffer = ByteBuffer.wrap(msg.getBytes());
		/*
		 * 用SocketChannel的writer()方法将缓冲的内容写出去。
		 */
		socket_channel.write(buffer);
		/*
		 * 请大家思考,这里为什么要将msg变量设置为null??
		 */
		msg = null;
	}
	/*
	 * String processRequest(SelectionKey k)方法用做处理客户端发来的消息
	 * 其中要进行字节流和字符流的转换工作。
	 */
	public String processRequest(SelectionKey k)throws IOException{
		ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
		/*
		 * 用 selectionKey的channel()方法得到一个 SocketChannel对象
		 */
		SocketChannel socket_channel = (SocketChannel)k.channel();
		socket_channel.read(buffer);
		/*
		 * 用通道读的方式将内容读到缓存当中,并且调整指针
		 */
		buffer.flip();
		Charset charset = Charset.forName("GBK");
		/*
		 * 用字符集对象 charset 的newDecoder()方法获得一个解码器对象。
		 */
		CharsetDecoder decoder = charset.newDecoder();
		/*
		 * 在ByteBuffer中存放的是字节,但程序需要把字节转换成字符串,才能进行对字符串的操作。
		 */
		CharBuffer charBuffer = decoder.decode(buffer);
		/*
		 * retruns a string containing the characters in this buffer.
		 */
		return charBuffer.toString();
	}
	public void init(int port)throws IOException{
		System.out.println("服务器开始运行...");
		/*
		 * opens a server-socket channel.
		 * 创建一个 ServerSocketChannel对象。
		 */
		server_socket_channel = ServerSocketChannel.open();
		/*
		 * configureBlocking(boolean block)
		 * Adjusts this channel's blocking mode.
		 * 设置该通道为非阻塞的。
		 */
		server_socket_channel.configureBlocking(false);
		/*
		 * Retrieves a server socket associated with this channel.
		 */
		ServerSocket server_socket = server_socket_channel.socket();
		/*
		 * creates a socket address where the IP address is the 
		 * wildcard(通配符)address and the port number a specified value.
		 * 用来表示Socket IP地址和端口号相结合的网络地址
		 */
		InetSocketAddress socket_address = new InetSocketAddress(port);
		/*
		 * Binds the server socket to a specific address
		 * (IP address and the port number)
		 */
		server_socket.bind(socket_address);
		/*
		 * opens a selector.打开一个选择器。
		 */
		selector = Selector.open();
		/*
		 * Registers this channel with the given selectors,returning a selectionKey.
		 * 表示服务器已经收到并且接收一个客户端请求。
		 */
		server_socket_channel.register(selector,SelectionKey.OP_ACCEPT);
		
		SocketChannel socket_channel = null;
		while(true){
			/*
			 * 选择一组键,其相应的通道已为 I/O 操作准备就绪。 
			 * 此方法执行处于阻塞模式的选择操作。
			 * 仅在至少选择一个通道、调用此选择器的 wakeup 方法,或者当前的线程已中断(以先到者为准)后此方法才返回。 
			 * 返回:已更新其准备就绪操作集的键的数目,该数目可能为零 
			 * 
			 * select(int i)方法,用来判断是否有事件发生;它是个阻塞方法,只要有事件发生,就返回现有事件的数量。
			 */
			int keynum = selector.select(1000);
			if(keynum>0){
				/*
				 * returns this seletor's selected-key set.
				 * selectedKeys()方法,用来从集合中取得消息,返回的也是个集合类型的。(Set)
				 */
				Set keyset = selector.selectedKeys();
				Iterator it = keyset.iterator();
				while(it.hasNext()){
					SelectionKey select_key = (SelectionKey)it.next();
					it.remove();
					if(!select_key.isValid()){
						select_key.cancel();
						continue;
					}
					/*
					 * test wether this key's channel is ready to accept a new socket connection.
					 */
					if(select_key.isAcceptable()){
						/*
						 * Returns the channel for which this key was created.
						 */
						ServerSocketChannel ssc = (ServerSocketChannel)select_key.channel();
						/*
						 * accepts a connection made to this channel's socket.
						 */
						socket_channel = (SocketChannel)ssc.accept();
						socket_channel.configureBlocking(false);
						socket_channel.register(selector,SelectionKey.OP_READ);
					}else if(select_key.isReadable()){
						try{
							String msg = "";
							msg = processRequest(select_key);
							System.out.println("从客户端收到数据:"+msg);
							if(msg.equalsIgnoreCase("exit")){
								select_key.cancel();
								continue;
							}
							if(msg.length()>0){
								response(select_key,"服务器返回数据:"+msg);
							}
						}catch(Exception e){
							System.out.println("出异常啦");
							e.printStackTrace();
							select_key.cancel();
						}
					}
				}
			}
		}
	}
	public static void main(String[] args){
		try{
			NBServer nbserver = new NBServer();
			nbserver.init(8888);
		}catch(IOException e){
			e.printStackTrace();
		}
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值