本文链接:http://blog.csdn.net/kongxx/article/details/8293469
一直没时间继续写,这两天总算找了点时间把当时的一些想法简单实现了一下,比较初略,主要是记下自己的想法,下次有机会了再慢慢细化吧。
对于Socket编程来说,通常我们遇到的最大的麻烦就是要定义自己的协议,用来在server端和client端处理请求和响应,当socket处理的请求对象越来越多以后,如果规则定义不清楚就会导致代码急剧膨胀,并且维护性变差,所以这里我想了一个简单的方式来处理这种情况。
下面大概说一下我的想法
1. 首先会有几个和业务相关的类,User,MyUserService和MyUserServiceImpl。User就是我们通常的实体类;MyUserService是我们针对User实体类提供的业务逻辑接口,比较简单就写了三个方法;MyUserServiceImpl是业务逻辑实现类。
User.java
- package com.googlecode.garbagecan.test.socket.sample10;
- public class User implements java.io.Serializable {
- private static final long serialVersionUID = 1L;
- private String name;
- private String password;
- public User() {
- }
- public User(String name, String password) {
- this.name = name;
- this.password = password;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- }
- package com.googlecode.garbagecan.test.socket.sample10;
- import java.util.List;
- public interface MyUserService {
- List<User> list(int size);
- User findByName(String name);
- void test();
- }
MyUserServiceImpl.java
- package com.googlecode.garbagecan.test.socket.sample10;
- import java.util.ArrayList;
- import java.util.List;
- public class MyUserServiceImpl implements MyUserService {
- @Override
- public List<User> list(int size) {
- List<User> users = new ArrayList<User>();
- for (int i = 0; i < size; i++) {
- users.add(new User("user_" + i, "password_" + i));
- }
- return users;
- }
- @Override
- public User findByName(String name) {
- return new User(name, null);
- }
- @Override
- public void test() {
- // do nothing
- }
- }
2. 服务器端类,主要有三个类MyServer,MyServerSimpleImpl和MyServerNIOImpl。MyServer是服务器端接口类,用来启动Socket server;MyServerSimpleImpl和MyServerNIOImpl是两个实现类,其中MyServerSimpleImpl是使用简单的Socket实现的,MyServerNIOImpl是使用java nio包里的类实现的,这个实现会有更好的性能。
MyServer.java
- package com.googlecode.garbagecan.test.socket.sample10;
- public interface MyServer {
- public void startup() throws Exception;
- public void shutdown() throws Exception;
- }
MyServerSimpleImpl.java
- package com.googlecode.garbagecan.test.socket.sample10;
- import com.googlecode.garbagecan.test.socket.IOUtil;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.lang.reflect.Method;
- import java.net.ServerSocket;
- import java.net.Socket;
- public class MyServerSimpleImpl implements MyServer {
- private int port;
- public MyServerSimpleImpl(int port) {
- this.port = port;
- }
- public void startup() throws Exception {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- ServerSocket server = new ServerSocket(port);
- while (true) {
- Socket socket = server.accept();
- invoke(socket);
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- }).start();
- }
- @Override
- public void shutdown() throws Exception {
- // Implement me
- }
- private void invoke(final Socket socket) {
- new Thread(new Runnable() {
- public void run() {
- ObjectInputStream ois = null;
- ObjectOutputStream oos = null;
- try {
- ois = new ObjectInputStream(socket.getInputStream());
- oos = new ObjectOutputStream(socket.getOutputStream());
- Object obj = ois.readObject();
- MyRequest request = (MyRequest) obj;
- MyResponse response = execute(request);
- oos.writeObject(response);
- oos.flush();
- } catch (Exception ex) {
- ex.printStackTrace();
- } finally {
- IOUtil.closeQuietly(ois);
- IOUtil.closeQuietly(oos);
- IOUtil.closeQuietly(socket);
- }
- }
- }).start();
- }
- private MyResponse execute(MyRequest request) throws Exception {
- Class clazz = request.getRequestClass();
- String methodName = request.getRequestMethod();
- Class<?>[] parameterTypes = request.getRequestParameterTypes();
- Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
- Object[] parameterValues = request.getRequestParameterValues();
- final Object obj = method.invoke(clazz.newInstance(), parameterValues);
- return new MyGenericResponse(obj);
- }
- }
MyServerNIOImpl.java
- package com.googlecode.garbagecan.test.socket.sample10;
- import com.googlecode.garbagecan.test.socket.SerializableUtil;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.lang.reflect.Method;
- import java.net.InetSocketAddress;
- import java.nio.ByteBuffer;
- import java.nio.channels.SelectionKey;
- import java.nio.channels.Selector;
- import java.nio.channels.ServerSocketChannel;
- import java.nio.channels.SocketChannel;
- import java.util.Iterator;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- public class MyServerNIOImpl implements MyServer {
- private final static Logger logger = Logger.getLogger(MyServerNIOImpl.class.getName());
- private int port;
- public MyServerNIOImpl(int port) {
- this.port = port;
- }
- public void startup() throws Exception {
- new Thread(new Runnable() {
- @Override
- public void run() {
- Selector selector = null;
- ServerSocketChannel serverSocketChannel = null;
- try {
- selector = Selector.open();
- serverSocketChannel = ServerSocketChannel.open();
- serverSocketChannel.configureBlocking(false);
- serverSocketChannel.socket().setReuseAddress(true);
- serverSocketChannel.socket().bind(new InetSocketAddress(port));
- serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
- while (selector.select() > 0) {
- try {
- Iterator<SelectionKey> it = selector.selectedKeys().iterator();
- while (it.hasNext()) {
- SelectionKey readyKey = it.next();
- it.remove();
- invoke((ServerSocketChannel) readyKey.channel());
- }
- } catch(Exception ex) {
- logger.log(Level.SEVERE, ex.getMessage(), ex);
- }
- }
- } catch (Exception ex) {
- logger.log(Level.SEVERE, ex.getMessage(), ex);
- } finally {
- try {
- selector.close();
- } catch(Exception ex) {}
- try {
- serverSocketChannel.close();
- } catch(Exception ex) {}
- }
- }
- }).start();
- }
- @Override
- public void shutdown() throws Exception {
- // Implement me
- }
- private void invoke(ServerSocketChannel serverSocketChannel) throws Exception {
- SocketChannel socketChannel = null;
- try {
- socketChannel = serverSocketChannel.accept();
- MyRequest myRequest = receiveData(socketChannel);
- MyResponse myResponse = execute(myRequest);
- sendData(socketChannel, myResponse);
- } finally {
- try {
- socketChannel.close();
- } catch(Exception ex) {}
- }
- }
- private MyResponse execute(MyRequest request) throws Exception {
- Class clazz = request.getRequestClass();
- String methodName = request.getRequestMethod();
- Class<?>[] parameterTypes = request.getRequestParameterTypes();
- Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
- Object[] parameterValues = request.getRequestParameterValues();
- final Object obj = method.invoke(clazz.newInstance(), parameterValues);
- return new MyGenericResponse(obj);
- }
- private MyRequest receiveData(SocketChannel socketChannel) throws IOException {
- MyRequest myRequest = null;
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ByteBuffer buffer = ByteBuffer.allocate(1024);
- try {
- byte[] bytes;
- int size = 0;
- while ((size = socketChannel.read(buffer)) >= 0) {
- buffer.flip();
- bytes = new byte[size];
- buffer.get(bytes);
- baos.write(bytes);
- buffer.clear();
- }
- bytes = baos.toByteArray();
- Object obj = SerializableUtil.toObject(bytes);
- myRequest = (MyRequest)obj;
- } finally {
- try {
- baos.close();
- } catch(Exception ex) {}
- }
- return myRequest;
- }
- private void sendData(SocketChannel socketChannel, MyResponse myResponse) throws IOException {
- byte[] bytes = SerializableUtil.toBytes(myResponse);
- ByteBuffer buffer = ByteBuffer.wrap(bytes);
- socketChannel.write(buffer);
- }
- }
MyClient.java
- package com.googlecode.garbagecan.test.socket.sample10;
- public interface MyClient {
- public <T> T execute(MyRequest request, MyResponseHandler<T> handler);
- public MyResponse execute(MyRequest request);
- }
MyClientSimpleImpl.java
- package com.googlecode.garbagecan.test.socket.sample10;
- import com.googlecode.garbagecan.test.socket.IOUtil;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketAddress;
- public class MyClientSimpleImpl implements MyClient {
- private String host;
- private int port;
- public MyClientSimpleImpl(String host, int port) {
- this.host = host;
- this.port = port;
- }
- public <T> T execute(MyRequest request, MyResponseHandler<T> handler) {
- MyResponse response = execute(request);
- return handler.handle(response);
- }
- public MyResponse execute(MyRequest request) {
- MyResponse response = null;
- Socket socket = null;
- ObjectOutputStream oos = null;
- ObjectInputStream ois = null;
- try {
- socket = new Socket();
- SocketAddress socketAddress = new InetSocketAddress(host, port);
- socket.connect(socketAddress, 10 * 1000);
- oos = new ObjectOutputStream(socket.getOutputStream());
- oos.writeObject(request);
- oos.flush();
- ois = new ObjectInputStream(socket.getInputStream());
- Object obj = ois.readObject();
- if (obj != null) {
- response = (MyResponse)obj;
- }
- } catch(IOException ex) {
- ex.printStackTrace();
- } catch (ClassNotFoundException ex) {
- ex.printStackTrace();
- } finally {
- IOUtil.closeQuietly(ois);
- IOUtil.closeQuietly(oos);
- IOUtil.closeQuietly(socket);
- }
- return response;
- }
- }
MyClientNIOImpl.java
- package com.googlecode.garbagecan.test.socket.sample10;
- import com.googlecode.garbagecan.test.socket.SerializableUtil;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.SocketAddress;
- import java.nio.ByteBuffer;
- import java.nio.channels.SocketChannel;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- public class MyClientNIOImpl implements MyClient {
- private final static Logger logger = Logger.getLogger(MyClientNIOImpl.class.getName());
- private String host;
- private int port;
- public MyClientNIOImpl(String host, int port) {
- this.host = host;
- this.port = port;
- }
- @Override
- public <T> T execute(MyRequest request, MyResponseHandler<T> handler) {
- MyResponse response = execute(request);
- return handler.handle(response);
- }
- @Override
- public MyResponse execute(MyRequest request) {
- MyResponse response = null;
- SocketChannel socketChannel = null;
- try {
- socketChannel = SocketChannel.open();
- SocketAddress socketAddress = new InetSocketAddress(host, port);
- socketChannel.connect(socketAddress);
- sendData(socketChannel, request);
- response = receiveData(socketChannel);
- } catch (Exception ex) {
- logger.log(Level.SEVERE, null, ex);
- } finally {
- try {
- socketChannel.close();
- } catch(Exception ex) {}
- }
- return response;
- }
- private void sendData(SocketChannel socketChannel, MyRequest myRequest) throws IOException {
- byte[] bytes = SerializableUtil.toBytes(myRequest);
- ByteBuffer buffer = ByteBuffer.wrap(bytes);
- socketChannel.write(buffer);
- socketChannel.socket().shutdownOutput();
- }
- private MyResponse receiveData(SocketChannel socketChannel) throws IOException {
- MyResponse myResponse = null;
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
- byte[] bytes;
- int count = 0;
- while ((count = socketChannel.read(buffer)) >= 0) {
- buffer.flip();
- bytes = new byte[count];
- buffer.get(bytes);
- baos.write(bytes);
- buffer.clear();
- }
- bytes = baos.toByteArray();
- Object obj = SerializableUtil.toObject(bytes);
- myResponse = (MyResponse) obj;
- socketChannel.close();
- } finally {
- try {
- baos.close();
- } catch(Exception ex) {}
- }
- return myResponse;
- }
- }
4. 接下来是MyRequest和MyResponse和MyResponseHandler接口,其中MyRequest接口中定义了四个方法,分别用来获取远程业务逻辑实现类,方法名,参数类型列表和参数列表。MyResponse接口定义了一个方法用来从response类中获取结果。MyResponseHandler接口使用范型的方式来获取最终的结果对象。
MyRequest.java
- package com.googlecode.garbagecan.test.socket.sample10;
- import java.io.Serializable;
- public interface MyRequest extends Serializable {
- Class<?> getRequestClass();
- String getRequestMethod();
- Class<?>[] getRequestParameterTypes();
- Object[] getRequestParameterValues();
- }
MyResponse.java
- package com.googlecode.garbagecan.test.socket.sample10;
- import java.io.Serializable;
- public interface MyResponse extends Serializable {
- Object getResult();
- }
MyResponseHandler.java
- package com.googlecode.garbagecan.test.socket.sample10;
- public interface MyResponseHandler<T> {
- T handle(MyResponse response);
- }
这几个接口的实现类分别对应MyGenericRequest,MyGenericResponse和MyGenericResponseHandler。
另外这里由于使用的反射类来在服务器端生成service实例,所以目前这里有个限制就是服务器端的Service实现类必须有默认构造函数。当然这是可以重构使其支持更多的方式。比如说支持从工厂方法获取实例,或者根据名字在服务器端直接获取已经创建好的,由于这个例子只是一个简单的原型,所以就不做具体深入的说明和实现了。
MyGenericRequest.java
- package com.googlecode.garbagecan.test.socket.sample10;
- public class MyGenericRequest implements MyRequest {
- private static final long serialVersionUID = 1L;
- private Class<?> requestClass;
- private String requestMethod;
- private Class<?>[] requestParameterTypes;
- private Object[] requestParameterValues;
- public MyGenericRequest(Class<?> requestClass, String requestMethod, Class<?>[] requestParameterTypes, Object[] requestParameterValues) {
- this.requestClass = requestClass;
- this.requestMethod = requestMethod;
- this.requestParameterTypes = requestParameterTypes;
- this.requestParameterValues = requestParameterValues;
- }
- @Override
- public Class<?> getRequestClass() {
- return requestClass;
- }
- @Override
- public String getRequestMethod() {
- return requestMethod;
- }
- @Override
- public Class<?>[] getRequestParameterTypes() {
- return requestParameterTypes;
- }
- @Override
- public Object[] getRequestParameterValues() {
- return requestParameterValues;
- }
- }
MyGenericResponse.java
- package com.googlecode.garbagecan.test.socket.sample10;
- public class MyGenericResponse implements MyResponse {
- private Object obj = null;
- public MyGenericResponse(Object obj) {
- this.obj = obj;
- }
- @Override
- public Object getResult() {
- return obj;
- }
- }
MyGenericResponseHandler.java
- package com.googlecode.garbagecan.test.socket.sample10;
- public class MyGenericResponseHandler<T> implements MyResponseHandler<T> {
- @Override
- public T handle(MyResponse response) {
- return (T) response.getResult();
- }
- }
5. 下面是两个辅助类
SerializableUtil.java
- package com.googlecode.garbagecan.test.socket;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- public class SerializableUtil {
- public static byte[] toBytes(Object object) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = null;
- try {
- oos = new ObjectOutputStream(baos);
- oos.writeObject(object);
- byte[] bytes = baos.toByteArray();
- return bytes;
- } catch(IOException ex) {
- throw new RuntimeException(ex.getMessage(), ex);
- } finally {
- try {
- oos.close();
- } catch (Exception e) {}
- }
- }
- public static Object toObject(byte[] bytes) {
- ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
- ObjectInputStream ois = null;
- try {
- ois = new ObjectInputStream(bais);
- Object object = ois.readObject();
- return object;
- } catch(IOException ex) {
- throw new RuntimeException(ex.getMessage(), ex);
- } catch(ClassNotFoundException ex) {
- throw new RuntimeException(ex.getMessage(), ex);
- } finally {
- try {
- ois.close();
- } catch (Exception e) {}
- }
- }
- }
IOUtil.java
- package com.googlecode.garbagecan.test.socket;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.Socket;
- public class IOUtil {
- public static void closeQuietly(InputStream is) {
- try {
- is.close();
- } catch (Exception e) {
- }
- }
- public static void closeQuietly(OutputStream os) {
- try {
- os.close();
- } catch (Exception e) {
- }
- }
- public static void closeQuietly(Socket socket) {
- try {
- socket.close();
- } catch (Exception e) {
- }
- }
- }
6. 最后是一个测试类,其中包含了两种实现的测试test1()和test2()。其中只是创建server和client的部分不同。下面说一下客户端怎样发送请求并获取服务器端响应。
首先创建一个MyRequest的实例,在其中告诉服务器端希望服务器端使用那个类的那个方法来处理这个相应,然后是参数类型列表和参数列表。 然后使用MyClient的execute()方法来发送上面创建的请求,并获取服务器端响应。也可以使用MyGenericResponseHandler接口来直接获取相应中的结果。
- package com.googlecode.garbagecan.test.socket.sample10;
- import java.util.List;
- public class Test {
- private static int port = 10000;
- public static void main(String[] args) throws Exception {
- //test1();
- test2();
- }
- public static void test1() throws Exception {
- MyServer myServer = new MyServerSimpleImpl(port);
- myServer.startup();
- Thread.sleep(3000);
- MyClient myClient = new MyClientSimpleImpl("localhost", port);
- MyRequest request = null;
- MyResponse response = null;
- request = new MyGenericRequest(MyUserServiceImpl.class, "list", new Class<?>[]{int.class}, new Object[]{2});
- response = myClient.execute(request);
- System.out.println(response.getResult());
- List<User> users = myClient.execute(request, new MyGenericResponseHandler<List<User>>());
- System.out.println(users);
- request = new MyGenericRequest(MyUserServiceImpl.class, "findByName", new Class<?>[]{String.class}, new Object[]{"kongxx"});
- response = myClient.execute(request);
- System.out.println(response.getResult());
- User user = myClient.execute(request, new MyGenericResponseHandler<User>());
- System.out.println(user);
- response = myClient.execute(new MyGenericRequest(MyUserServiceImpl.class, "test", new Class<?>[]{}, new Object[]{}));
- System.out.println(response.getResult());
- }
- public static void test2() throws Exception {
- MyServer myServer = new MyServerNIOImpl(port);
- myServer.startup();
- Thread.sleep(3000);
- MyClient myClient = new MyClientNIOImpl("localhost", port);
- MyRequest request = null;
- MyResponse response = null;
- request = new MyGenericRequest(MyUserServiceImpl.class, "list", new Class<?>[]{int.class}, new Object[]{2});
- response = myClient.execute(request);
- System.out.println(response.getResult());
- List<User> users = myClient.execute(request, new MyGenericResponseHandler<List<User>>());
- System.out.println(users);
- request = new MyGenericRequest(MyUserServiceImpl.class, "findByName", new Class<?>[]{String.class}, new Object[]{"kongxx"});
- response = myClient.execute(request);
- System.out.println(response.getResult());
- User user = myClient.execute(request, new MyGenericResponseHandler<User>());
- System.out.println(user);
- response = myClient.execute(new MyGenericRequest(MyUserServiceImpl.class, "test", new Class<?>[]{}, new Object[]{}));
- System.out.println(response.getResult());
- }
- }