Java Socket实战之一 单线程通信
Java Socket实战之二 多线程通信
Java Socket实战之三 传输对象
Java Socket实战之四 传输压缩对象
Java Socket实战之五 使用加密协议传输对象
Java Socket实战之六 使用NIO包实现Socket通信
Java Socket实战之七 使用Socket通信传输文件
一直没时间继续写,这两天总算找了点时间把当时的一些想法简单实现了一下,比较初略,主要是记下自己的想法,下次有机会了再慢慢细化吧。
对于Socket编程来说,通常我们遇到的最大的麻烦就是要定义自己的协议,用来在server端和client端处理请求和响应,当socket处理的请求对象越来越多以后,如果规则定义不清楚就会导致代码急剧膨胀,并且维护性变差,所以这里我想了一个简单的方式来处理这种情况。
下面大概说一下我的想法
1. 首先会有几个和业务相关的类,User,MyUserService和MyUserServiceImpl。User就是我们通常的实体类;MyUserService是我们针对User实体类提供的业务逻辑接口,比较简单就写了三个方法;MyUserServiceImpl是业务逻辑实现类。
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(); }
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 } }
MyServer.java
package com.googlecode.garbagecan.test.socket.sample10; public interface MyServer { public void startup() throws Exception; public void shutdown() throws Exception; }
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); } }
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); }
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; } }
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; } }
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(); }
package com.googlecode.garbagecan.test.socket.sample10; import java.io.Serializable; public interface MyResponse extends Serializable { Object getResult(); }
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; } }
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; } }
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) {} } } }
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()); } }