Server端代码:
public class ServerSocket {
public static final int PORT=9888;
public static void main(String[] args){
try {
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
Selector selector= Selector.open();
serverSocketChannel.configureBlocking(false); //设置非阻塞
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true){
int n=selector.select();
if(n==0){
continue;
}
Iterator<SelectionKey> it=selector.selectedKeys().iterator();
while(it.hasNext()){
try {
SelectionKey key=it.next();
it.remove();
if(key.isAcceptable()){
ServerSocketChannel cl=(ServerSocketChannel)key.channel();
SocketChannel sc=cl.accept(); //接收到连接请求后,设置客户端channel为异步
sc.configureBlocking(false);
sc.register(selector,SelectionKey.OP_READ);
}
if(key.isConnectable()){
System.out.println("server channel is receive client connection");
}
if(key.isReadable()){
System.out.println("server channel isReadable enter");
ByteBuffer bf=ByteBuffer.allocate(1024);
SocketChannel clientChannel=(SocketChannel) key.channel();
boolean needShutdownChannel=readAndPrint(bf,clientChannel);
if(needShutdownChannel){
key.cancel();
clientChannel.close();
continue;
}
writeToClient(bf, clientChannel);
}
if(key.isWritable()){
System.out.println("enter isWritable ");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeToClient(ByteBuffer bf, SocketChannel clientChannel) throws IOException {
bf.put(("receiver client msg,return hello world,"+new Date()).getBytes());
bf.flip();
while(bf.hasRemaining()){
clientChannel.write(bf);
}
bf.clear();
}
/**
* 读取数据并且打印出来,这里有个问题,当客户端关闭连接后,主方法会一直进入key.isReadable,进入死循环,
* 这里我自己判断,如果是第一次就读不到字节,关闭channel,
* @param bf
* @param clientChannel
* @return
* @throws IOException
*/
private static boolean readAndPrint(ByteBuffer bf, SocketChannel clientChannel) throws IOException {
try {
int index=0;
int n=-1;
while((n=clientChannel.read(bf))>0){
index++;
bf.flip();
while(bf.hasRemaining()){
byte[] b=new byte[bf.remaining()];//字节数组的长度要跟bf的长度一样,否则会抛异常
while(bf.hasRemaining()){
bf.get(b);
}
System.out.println(new String(b));
}
bf.clear();
}
if(n<=0 && index==0){
return true;
}
return false;
} catch (IOException e) {
e.printStackTrace();
return true;
}
}
}
client端代码:
public class ClientSocket {
public static int serverPort=9888;
public static void main(String[] args) throws InterruptedException {
try {
SocketChannel socketChannel=SocketChannel.open();
Selector selector= Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(new InetSocketAddress("192.168.3.27",serverPort));
while(true){
int n=selector.select();
if(n==0){
continue;
}
Iterator<SelectionKey> it=selector.selectedKeys().iterator();
while (it.hasNext()){
SelectionKey key=it.next();
it.remove();
if(key.isConnectable()){
ByteBuffer bf=ByteBuffer.allocate(1024);
SocketChannel clientChannel=(SocketChannel) key.channel();
//finishConnection校验是否连接成功,同步会阻塞直到成功或者抛出异常
//异步模式,会直接返回true|false
while(!clientChannel.finishConnect()){
Thread.sleep(100);
}
write(clientChannel, bf);
clientChannel.register(selector,SelectionKey.OP_READ);
}
if(key.isReadable()){
//获取channel
SocketChannel clientChannel=(SocketChannel) key.channel();
//初始化字节
ByteBuffer bf=ByteBuffer.allocate(1024);
readAndPrint(clientChannel,bf);
clientChannel.close(); //关闭channel
return;
}
if(key.isWritable()){
System.out.println("client isWritable enter");
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void write(SocketChannel clientChannel, ByteBuffer bf) throws IOException {
bf.put(("hello server,"+new Date()).getBytes());
bf.flip();
while(bf.hasRemaining()){
clientChannel.write(bf);
}
bf.clear();
}
private static void readAndPrint(SocketChannel clientChannel, ByteBuffer bf) throws IOException {
while(clientChannel.read(bf)>0){
bf.flip();
while(bf.hasRemaining()){
byte[] b=new byte[bf.remaining()];
while(bf.hasRemaining()){
bf.get(b);
}
System.out.println(new String(b));
}
bf.clear();
}
}
}
这里有几个坑,在注释里面有的:
1 想把接收到的bytebuffer打印出来,定义的 byte[]数组接收,数组大小要跟bf.remaining()大小相等,要这么写 byte[] b=new byte[bf.remaining()];
2 客户端关闭连接后,server端 方法会一直进入 isReadable代码块
if(key.isReadable()){ }
只是这时候从ByteBuffer读到的数据长度是0 ,不是 -1
3客户端在 isConnectioned方法里面发送数据会抛异常,要先执行
clientChannel.finishConnect()
但是看注释发现这个方法在 异步模式不代表一定会等待连接建立,连接没成功可能返回false,当然,也可能我英语不好,翻译不准确,
所以我只能按照下面的方式这么写
- while(!clientChannel.finishConnect()){
- Thread.sleep(100);
- }