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();
}
}
}