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
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