我们还是用NIO通信,client建立连接后不断向server发送对象Invocation。需要远端调用的方法,这里演示发送“foo”参数,服务端传回来”Fello foo”.
public class HelloServiceImpl implements HelloService {
public String hello(String name) {
return "Hello " + name;
}
}
Invocation包含类名,方法名,方法参数等
public class Invocation implements Serializable{
private static final long serialVersionUID = 1L;
private Class<?> clazz;
private String methodName;
private Class<?>[] parameterTypes;
private Object[] arguments;
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class<?>[] getParameterTypes() {
return parameterTypes;
}
public void setParameterTypes(Class<?>[] parameterTypes) {
this.parameterTypes = parameterTypes;
}
public Object[] getArguments() {
return arguments;
}
public void setArguments(Object[] arguments) {
this.arguments = arguments;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Invocation{");
sb.append("methodName='").append(methodName).append('\'');
sb.append(", clazz=").append(clazz);
sb.append('}');
return sb.toString();
}
}
client负责发送序列化后的invocation对象,接收响应后反序列化数据,打印结果。
public class NIOClient extends Thread {
private Selector selector;
private void initClient(int port) throws IOException {
InetSocketAddress address = new InetSocketAddress(port);
selector = Selector.open();
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(address);
}
public void run() {
System.out.println("客户端已经启动!");
try {
while (selector.select() > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isConnectable()) {
SocketChannel channel = (SocketChannel) key.channel();
// 如果正在连接,则完成连接
if (channel.isConnectionPending()) {
channel.finishConnect();
}
channel.register(selector, SelectionKey.OP_WRITE);
}
if (key.isReadable()) {
doRead(key);
}
if (key.isWritable()) {
doWrite(key);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (ClosedChannelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void doRead(SelectionKey key) throws IOException, ClassNotFoundException {
System.out.println("read data from server...");
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(Constant.BUFFER_SIZE);
if (channel.read(buffer) > 0) {
Object object = NIOUtil.getObject(buffer);
System.out.println(object);
channel.register(selector, SelectionKey.OP_WRITE);
}
}
private void doWrite(SelectionKey key) throws Exception {
System.out.println("write data to server...");
SocketChannel channel = (SocketChannel) key.channel();
//传递类名,方法名,方法参数
Invocation invocation = new Invocation();
invocation.setClazz(HelloServiceImpl.class);
invocation.setMethodName("hello");
invocation.setParameterTypes(new Class[]{String.class});
invocation.setArguments(new Object[]{"foo"});
ByteBuffer buffer = NIOUtil.getByteBuffer(invocation);
while (buffer.hasRemaining()) {
channel.write(buffer);
}
channel.register(selector, SelectionKey.OP_READ);
}
public void stopServer() throws IOException {
if (selector != null && selector.isOpen()) {
selector.close();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
NIOClient client = new NIOClient();
try {
client.initClient(8859);
client.start();
} catch (Exception e) {
client.stopServer();
}
}
}
Server和client端相似,反序列化Invocation,得到调用的类名和方法名以及参数等,以反射方式调用本地对象方法,将结果发送给client。
package nio2;
import common.Invocation;
import common.Constant;
import java.io.*;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/**
* Created by juemingzi on 16/5/21.
*/
public class NIOServer extends Thread {
private Selector selector;
public void run() {
System.out.println("服务端线程已经启动!");
try {
while (selector.select() > 0) {
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeySet.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
processKey(key);
} catch (ClosedSelectorException cek) {
cek.printStackTrace();
} catch (CancelledKeyException ck) {
ck.printStackTrace();
key.cancel();
} catch (Throwable e) {
e.printStackTrace();
}
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (selector != null) {
selector.close();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private void processKey(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
doAccept(key);
}
if (key.isReadable()) {
doRead(key);
}
if (key.isWritable()) {
doWrite(key);
}
}
private void doAccept(SelectionKey key) throws IOException {
System.out.println(Thread.currentThread().getName() + " accept connection...");
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
}
private void doRead(SelectionKey key) throws IOException {
System.out.println(Thread.currentThread().getName() + " read data from client...");
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(Constant.BUFFER_SIZE);
try {
buffer.clear();
if (channel.read(buffer) > 0) {
buffer.flip();
//序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(buffer.array()));
try {
Invocation invocation = (Invocation) objectInputStream.readObject();
//RPC调用在这里实现
Class<?> clazz = invocation.getClazz();
Object object = clazz.newInstance();
Method method = clazz.getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Object result = method.invoke(object, invocation.getArguments());
channel.register(selector, SelectionKey.OP_WRITE, result);//发送响应
} finally {
objectInputStream.close();
}
}
} catch (Throwable e) {
e.printStackTrace();
key.cancel();
}
}
private void doWrite(SelectionKey key) throws IOException {
System.out.println(Thread.currentThread().getName() + " write data to client...");
SocketChannel channel = (SocketChannel) key.channel();
Object object = key.attachment();
if (object != null) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutput = new ObjectOutputStream(byteArrayOutputStream);
try {
objectOutput.writeObject(object);
objectOutput.flush();
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArrayOutputStream.toByteArray());
while (byteBuffer.hasRemaining()) {
channel.write(byteBuffer);
}
channel.register(selector, SelectionKey.OP_READ);
} finally {
objectOutput.close();
byteArrayOutputStream.close();
}
}
}
public void initServer(int port) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void stopServer() throws IOException {
if (!selector.isOpen()) {
selector.close();
}
}
public static void main(String[] args) throws IOException {
NIOServer server = new NIOServer();
try {
server.initServer(8859);
server.start();
} catch (Exception e) {
e.printStackTrace();
server.stopServer();
}
}
}
NIOServer运行截图:
NIOClient运行截图: