Java Socket实战之八 socket提升
一直没时间继续写,这两天总算找了点时间把当时的一些想法简单实现了一下,比较初略,主要是记下自己的想法,下次有机会了再慢慢细化吧。
对于Socket编程来说,通常我们遇到的最大的麻烦就是要定义自己的协议,用来在server端和client端处理请求和响应,当socket处理的请求对象越来越多以后,如果规则定义不清楚就会导致代码急剧膨胀,并且维护性变差,所以这里我想了一个简单的方式来处理这种情况。
下面大概说一下我的想法
1. 首先会有几个和业务相关的类,User,MyUserService和MyUserServiceImpl。User就是我们通常的实体类;MyUserService是我们针对User实体类提供的业务逻辑接口,比较简单就写了三个方法;MyUserServiceImpl是业务逻辑实现类。
- packagecom.googlecode.garbagecan.test.socket.sample10;
- publicclassUserimplementsjava.io.Serializable{
- privatestaticfinallongserialVersionUID=1L;
- privateStringname;
- privateStringpassword;
- publicUser(){
- }
- publicUser(Stringname,Stringpassword){
- this.name=name;
- this.password=password;
- }
- publicStringgetName(){
- returnname;
- }
- publicvoidsetName(Stringname){
- this.name=name;
- }
- publicStringgetPassword(){
- returnpassword;
- }
- publicvoidsetPassword(Stringpassword){
- this.password=password;
- }
- }
MyUserService.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importjava.util.List;
- publicinterfaceMyUserService{
- List<User>list(intsize);
- UserfindByName(Stringname);
- voidtest();
- }
MyUserServiceImpl.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importjava.util.ArrayList;
- importjava.util.List;
- publicclassMyUserServiceImplimplementsMyUserService{
- @Override
- publicList<User>list(intsize){
- List<User>users=newArrayList<User>();
- for(inti=0;i<size;i++){
- users.add(newUser("user_"+i,"password_"+i));
- }
- returnusers;
- }
- @Override
- publicUserfindByName(Stringname){
- returnnewUser(name,null);
- }
- @Override
- publicvoidtest(){
- //donothing
- }
- }
2. 服务器端类,主要有三个类MyServer,MyServerSimpleImpl和MyServerNIOImpl。MyServer是服务器端接口类,用来启动Socket server;MyServerSimpleImpl和MyServerNIOImpl是两个实现类,其中MyServerSimpleImpl是使用简单的Socket实现的,MyServerNIOImpl是使用java nio包里的类实现的,这个实现会有更好的性能。
MyServer.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- publicinterfaceMyServer{
- publicvoidstartup()throwsException;
- publicvoidshutdown()throwsException;
- }
MyServerSimpleImpl.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importcom.googlecode.garbagecan.test.socket.IOUtil;
- importjava.io.ObjectInputStream;
- importjava.io.ObjectOutputStream;
- importjava.lang.reflect.Method;
- importjava.net.ServerSocket;
- importjava.net.Socket;
- publicclassMyServerSimpleImplimplementsMyServer{
- privateintport;
- publicMyServerSimpleImpl(intport){
- this.port=port;
- }
- publicvoidstartup()throwsException{
- newThread(newRunnable(){
- @Override
- publicvoidrun(){
- try{
- ServerSocketserver=newServerSocket(port);
- while(true){
- Socketsocket=server.accept();
- invoke(socket);
- }
- }catch(Exceptionex){
- ex.printStackTrace();
- }
- }
- }).start();
- }
- @Override
- publicvoidshutdown()throwsException{
- //Implementme
- }
- privatevoidinvoke(finalSocketsocket){
- newThread(newRunnable(){
- publicvoidrun(){
- ObjectInputStreamois=null;
- ObjectOutputStreamoos=null;
- try{
- ois=newObjectInputStream(socket.getInputStream());
- oos=newObjectOutputStream(socket.getOutputStream());
- Objectobj=ois.readObject();
- MyRequestrequest=(MyRequest)obj;
- MyResponseresponse=execute(request);
- oos.writeObject(response);
- oos.flush();
- }catch(Exceptionex){
- ex.printStackTrace();
- }finally{
- IOUtil.closeQuietly(ois);
- IOUtil.closeQuietly(oos);
- IOUtil.closeQuietly(socket);
- }
- }
- }).start();
- }
- privateMyResponseexecute(MyRequestrequest)throwsException{
- Classclazz=request.getRequestClass();
- StringmethodName=request.getRequestMethod();
- Class<?>[]parameterTypes=request.getRequestParameterTypes();
- Methodmethod=clazz.getDeclaredMethod(methodName,parameterTypes);
- Object[]parameterValues=request.getRequestParameterValues();
- finalObjectobj=method.invoke(clazz.newInstance(),parameterValues);
- returnnewMyGenericResponse(obj);
- }
- }
MyServerNIOImpl.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importcom.googlecode.garbagecan.test.socket.SerializableUtil;
- importjava.io.ByteArrayOutputStream;
- importjava.io.IOException;
- importjava.lang.reflect.Method;
- importjava.net.InetSocketAddress;
- importjava.nio.ByteBuffer;
- importjava.nio.channels.SelectionKey;
- importjava.nio.channels.Selector;
- importjava.nio.channels.ServerSocketChannel;
- importjava.nio.channels.SocketChannel;
- importjava.util.Iterator;
- importjava.util.logging.Level;
- importjava.util.logging.Logger;
- publicclassMyServerNIOImplimplementsMyServer{
- privatefinalstaticLoggerlogger=Logger.getLogger(MyServerNIOImpl.class.getName());
- privateintport;
- publicMyServerNIOImpl(intport){
- this.port=port;
- }
- publicvoidstartup()throwsException{
- newThread(newRunnable(){
- @Override
- publicvoidrun(){
- Selectorselector=null;
- ServerSocketChannelserverSocketChannel=null;
- try{
- selector=Selector.open();
- serverSocketChannel=ServerSocketChannel.open();
- serverSocketChannel.configureBlocking(false);
- serverSocketChannel.socket().setReuseAddress(true);
- serverSocketChannel.socket().bind(newInetSocketAddress(port));
- serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
- while(selector.select()>0){
- try{
- Iterator<SelectionKey>it=selector.selectedKeys().iterator();
- while(it.hasNext()){
- SelectionKeyreadyKey=it.next();
- it.remove();
- invoke((ServerSocketChannel)readyKey.channel());
- }
- }catch(Exceptionex){
- logger.log(Level.SEVERE,ex.getMessage(),ex);
- }
- }
- }catch(Exceptionex){
- logger.log(Level.SEVERE,ex.getMessage(),ex);
- }finally{
- try{
- selector.close();
- }catch(Exceptionex){}
- try{
- serverSocketChannel.close();
- }catch(Exceptionex){}
- }
- }
- }).start();
- }
- @Override
- publicvoidshutdown()throwsException{
- //Implementme
- }
- privatevoidinvoke(ServerSocketChannelserverSocketChannel)throwsException{
- SocketChannelsocketChannel=null;
- try{
- socketChannel=serverSocketChannel.accept();
- MyRequestmyRequest=receiveData(socketChannel);
- MyResponsemyResponse=execute(myRequest);
- sendData(socketChannel,myResponse);
- }finally{
- try{
- socketChannel.close();
- }catch(Exceptionex){}
- }
- }
- privateMyResponseexecute(MyRequestrequest)throwsException{
- Classclazz=request.getRequestClass();
- StringmethodName=request.getRequestMethod();
- Class<?>[]parameterTypes=request.getRequestParameterTypes();
- Methodmethod=clazz.getDeclaredMethod(methodName,parameterTypes);
- Object[]parameterValues=request.getRequestParameterValues();
- finalObjectobj=method.invoke(clazz.newInstance(),parameterValues);
- returnnewMyGenericResponse(obj);
- }
- privateMyRequestreceiveData(SocketChannelsocketChannel)throwsIOException{
- MyRequestmyRequest=null;
- ByteArrayOutputStreambaos=newByteArrayOutputStream();
- ByteBufferbuffer=ByteBuffer.allocate(1024);
- try{
- byte[]bytes;
- intsize=0;
- while((size=socketChannel.read(buffer))>=0){
- buffer.flip();
- bytes=newbyte[size];
- buffer.get(bytes);
- baos.write(bytes);
- buffer.clear();
- }
- bytes=baos.toByteArray();
- Objectobj=SerializableUtil.toObject(bytes);
- myRequest=(MyRequest)obj;
- }finally{
- try{
- baos.close();
- }catch(Exceptionex){}
- }
- returnmyRequest;
- }
- privatevoidsendData(SocketChannelsocketChannel,MyResponsemyResponse)throwsIOException{
- byte[]bytes=SerializableUtil.toBytes(myResponse);
- ByteBufferbuffer=ByteBuffer.wrap(bytes);
- socketChannel.write(buffer);
- }
- }
3. 客户端类,主要有三个类MyClient,MyClientSimpleImpl和MyClientNIOImpl。MyClient是客户端接口类,用来向Server端发送请求并获取相应;MyClientSimpleImpl和MyClientNIOImpl是两个实现类,其中MyClientSimpleImpl是使用简单Socket实现的,MyClientNIOImpl是使用java nio包里的类实现的,这个实现会有更好的性能。
MyClient.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- publicinterfaceMyClient{
- public<T>Texecute(MyRequestrequest,MyResponseHandler<T>handler);
- publicMyResponseexecute(MyRequestrequest);
- }
MyClientSimpleImpl.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importcom.googlecode.garbagecan.test.socket.IOUtil;
- importjava.io.IOException;
- importjava.io.ObjectInputStream;
- importjava.io.ObjectOutputStream;
- importjava.net.InetSocketAddress;
- importjava.net.Socket;
- importjava.net.SocketAddress;
- publicclassMyClientSimpleImplimplementsMyClient{
- privateStringhost;
- privateintport;
- publicMyClientSimpleImpl(Stringhost,intport){
- this.host=host;
- this.port=port;
- }
- public<T>Texecute(MyRequestrequest,MyResponseHandler<T>handler){
- MyResponseresponse=execute(request);
- returnhandler.handle(response);
- }
- publicMyResponseexecute(MyRequestrequest){
- MyResponseresponse=null;
- Socketsocket=null;
- ObjectOutputStreamoos=null;
- ObjectInputStreamois=null;
- try{
- socket=newSocket();
- SocketAddresssocketAddress=newInetSocketAddress(host,port);
- socket.connect(socketAddress,10*1000);
- oos=newObjectOutputStream(socket.getOutputStream());
- oos.writeObject(request);
- oos.flush();
- ois=newObjectInputStream(socket.getInputStream());
- Objectobj=ois.readObject();
- if(obj!=null){
- response=(MyResponse)obj;
- }
- }catch(IOExceptionex){
- ex.printStackTrace();
- }catch(ClassNotFoundExceptionex){
- ex.printStackTrace();
- }finally{
- IOUtil.closeQuietly(ois);
- IOUtil.closeQuietly(oos);
- IOUtil.closeQuietly(socket);
- }
- returnresponse;
- }
- }
MyClientNIOImpl.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importcom.googlecode.garbagecan.test.socket.SerializableUtil;
- importjava.io.ByteArrayOutputStream;
- importjava.io.IOException;
- importjava.net.InetSocketAddress;
- importjava.net.SocketAddress;
- importjava.nio.ByteBuffer;
- importjava.nio.channels.SocketChannel;
- importjava.util.logging.Level;
- importjava.util.logging.Logger;
- publicclassMyClientNIOImplimplementsMyClient{
- privatefinalstaticLoggerlogger=Logger.getLogger(MyClientNIOImpl.class.getName());
- privateStringhost;
- privateintport;
- publicMyClientNIOImpl(Stringhost,intport){
- this.host=host;
- this.port=port;
- }
- @Override
- public<T>Texecute(MyRequestrequest,MyResponseHandler<T>handler){
- MyResponseresponse=execute(request);
- returnhandler.handle(response);
- }
- @Override
- publicMyResponseexecute(MyRequestrequest){
- MyResponseresponse=null;
- SocketChannelsocketChannel=null;
- try{
- socketChannel=SocketChannel.open();
- SocketAddresssocketAddress=newInetSocketAddress(host,port);
- socketChannel.connect(socketAddress);
- sendData(socketChannel,request);
- response=receiveData(socketChannel);
- }catch(Exceptionex){
- logger.log(Level.SEVERE,null,ex);
- }finally{
- try{
- socketChannel.close();
- }catch(Exceptionex){}
- }
- returnresponse;
- }
- privatevoidsendData(SocketChannelsocketChannel,MyRequestmyRequest)throwsIOException{
- byte[]bytes=SerializableUtil.toBytes(myRequest);
- ByteBufferbuffer=ByteBuffer.wrap(bytes);
- socketChannel.write(buffer);
- socketChannel.socket().shutdownOutput();
- }
- privateMyResponsereceiveData(SocketChannelsocketChannel)throwsIOException{
- MyResponsemyResponse=null;
- ByteArrayOutputStreambaos=newByteArrayOutputStream();
- try{
- ByteBufferbuffer=ByteBuffer.allocateDirect(1024);
- byte[]bytes;
- intcount=0;
- while((count=socketChannel.read(buffer))>=0){
- buffer.flip();
- bytes=newbyte[count];
- buffer.get(bytes);
- baos.write(bytes);
- buffer.clear();
- }
- bytes=baos.toByteArray();
- Objectobj=SerializableUtil.toObject(bytes);
- myResponse=(MyResponse)obj;
- socketChannel.close();
- }finally{
- try{
- baos.close();
- }catch(Exceptionex){}
- }
- returnmyResponse;
- }
- }
4. 接下来是MyRequest和MyResponse和MyResponseHandler接口,其中MyRequest接口中定义了四个方法,分别用来获取远程业务逻辑实现类,方法名,参数类型列表和参数列表。MyResponse接口定义了一个方法用来从response类中获取结果。MyResponseHandler接口使用范型的方式来获取最终的结果对象。
MyRequest.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importjava.io.Serializable;
- publicinterfaceMyRequestextendsSerializable{
- Class<?>getRequestClass();
- StringgetRequestMethod();
- Class<?>[]getRequestParameterTypes();
- Object[]getRequestParameterValues();
- }
MyResponse.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importjava.io.Serializable;
- publicinterfaceMyResponseextendsSerializable{
- ObjectgetResult();
- }
MyResponseHandler.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- publicinterfaceMyResponseHandler<T>{
- Thandle(MyResponseresponse);
- }
这几个接口的实现类分别对应MyGenericRequest,MyGenericResponse和MyGenericResponseHandler。
另外这里由于使用的反射类来在服务器端生成service实例,所以目前这里有个限制就是服务器端的Service实现类必须有默认构造函数。当然这是可以重构使其支持更多的方式。比如说支持从工厂方法获取实例,或者根据名字在服务器端直接获取已经创建好的,由于这个例子只是一个简单的原型,所以就不做具体深入的说明和实现了。
MyGenericRequest.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- publicclassMyGenericRequestimplementsMyRequest{
- privatestaticfinallongserialVersionUID=1L;
- privateClass<?>requestClass;
- privateStringrequestMethod;
- privateClass<?>[]requestParameterTypes;
- privateObject[]requestParameterValues;
- publicMyGenericRequest(Class<?>requestClass,StringrequestMethod,Class<?>[]requestParameterTypes,Object[]requestParameterValues){
- this.requestClass=requestClass;
- this.requestMethod=requestMethod;
- this.requestParameterTypes=requestParameterTypes;
- this.requestParameterValues=requestParameterValues;
- }
- @Override
- publicClass<?>getRequestClass(){
- returnrequestClass;
- }
- @Override
- publicStringgetRequestMethod(){
- returnrequestMethod;
- }
- @Override
- publicClass<?>[]getRequestParameterTypes(){
- returnrequestParameterTypes;
- }
- @Override
- publicObject[]getRequestParameterValues(){
- returnrequestParameterValues;
- }
- }
MyGenericResponse.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- publicclassMyGenericResponseimplementsMyResponse{
- privateObjectobj=null;
- publicMyGenericResponse(Objectobj){
- this.obj=obj;
- }
- @Override
- publicObjectgetResult(){
- returnobj;
- }
- }
MyGenericResponseHandler.java
- packagecom.googlecode.garbagecan.test.socket.sample10;
- publicclassMyGenericResponseHandler<T>implementsMyResponseHandler<T>{
- @Override
- publicThandle(MyResponseresponse){
- return(T)response.getResult();
- }
- }
5. 下面是两个辅助类
SerializableUtil.java
- packagecom.googlecode.garbagecan.test.socket;
- importjava.io.ByteArrayInputStream;
- importjava.io.ByteArrayOutputStream;
- importjava.io.IOException;
- importjava.io.ObjectInputStream;
- importjava.io.ObjectOutputStream;
- publicclassSerializableUtil{
- publicstaticbyte[]toBytes(Objectobject){
- ByteArrayOutputStreambaos=newByteArrayOutputStream();
- ObjectOutputStreamoos=null;
- try{
- oos=newObjectOutputStream(baos);
- oos.writeObject(object);
- byte[]bytes=baos.toByteArray();
- returnbytes;
- }catch(IOExceptionex){
- thrownewRuntimeException(ex.getMessage(),ex);
- }finally{
- try{
- oos.close();
- }catch(Exceptione){}
- }
- }
- publicstaticObjecttoObject(byte[]bytes){
- ByteArrayInputStreambais=newByteArrayInputStream(bytes);
- ObjectInputStreamois=null;
- try{
- ois=newObjectInputStream(bais);
- Objectobject=ois.readObject();
- returnobject;
- }catch(IOExceptionex){
- thrownewRuntimeException(ex.getMessage(),ex);
- }catch(ClassNotFoundExceptionex){
- thrownewRuntimeException(ex.getMessage(),ex);
- }finally{
- try{
- ois.close();
- }catch(Exceptione){}
- }
- }
- }
IOUtil.java
- packagecom.googlecode.garbagecan.test.socket;
- importjava.io.InputStream;
- importjava.io.OutputStream;
- importjava.net.Socket;
- publicclassIOUtil{
- publicstaticvoidcloseQuietly(InputStreamis){
- try{
- is.close();
- }catch(Exceptione){
- }
- }
- publicstaticvoidcloseQuietly(OutputStreamos){
- try{
- os.close();
- }catch(Exceptione){
- }
- }
- publicstaticvoidcloseQuietly(Socketsocket){
- try{
- socket.close();
- }catch(Exceptione){
- }
- }
- }
6. 最后是一个测试类,其中包含了两种实现的测试test1()和test2()。其中只是创建server和client的部分不同。下面说一下客户端怎样发送请求并获取服务器端响应。
首先创建一个MyRequest的实例,在其中告诉服务器端希望服务器端使用那个类的那个方法来处理这个相应,然后是参数类型列表和参数列表。 然后使用MyClient的execute()方法来发送上面创建的请求,并获取服务器端响应。也可以使用MyGenericResponseHandler接口来直接获取相应中的结果。
- packagecom.googlecode.garbagecan.test.socket.sample10;
- importjava.util.List;
- publicclassTest{
- privatestaticintport=10000;
- publicstaticvoidmain(String[]args)throwsException{
- //test1();
- test2();
- }
- publicstaticvoidtest1()throwsException{
- MyServermyServer=newMyServerSimpleImpl(port);
- myServer.startup();
- Thread.sleep(3000);
- MyClientmyClient=newMyClientSimpleImpl("localhost",port);
- MyRequestrequest=null;
- MyResponseresponse=null;
- request=newMyGenericRequest(MyUserServiceImpl.class,"list",newClass<?>[]{int.class},newObject[]{2});
- response=myClient.execute(request);
- System.out.println(response.getResult());
- List<User>users=myClient.execute(request,newMyGenericResponseHandler<List<User>>());
- System.out.println(users);
- request=newMyGenericRequest(MyUserServiceImpl.class,"findByName",newClass<?>[]{String.class},newObject[]{"kongxx"});
- response=myClient.execute(request);
- System.out.println(response.getResult());
- Useruser=myClient.execute(request,newMyGenericResponseHandler<User>());
- System.out.println(user);
- response=myClient.execute(newMyGenericRequest(MyUserServiceImpl.class,"test",newClass<?>[]{},newObject[]{}));
- System.out.println(response.getResult());
- }
- publicstaticvoidtest2()throwsException{
- MyServermyServer=newMyServerNIOImpl(port);
- myServer.startup();
- Thread.sleep(3000);
- MyClientmyClient=newMyClientNIOImpl("localhost",port);
- MyRequestrequest=null;
- MyResponseresponse=null;
- request=newMyGenericRequest(MyUserServiceImpl.class,"list",newClass<?>[]{int.class},newObject[]{2});
- response=myClient.execute(request);
- System.out.println(response.getResult());
- List<User>users=myClient.execute(request,newMyGenericResponseHandler<List<User>>());
- System.out.println(users);
- request=newMyGenericRequest(MyUserServiceImpl.class,"findByName",newClass<?>[]{String.class},newObject[]{"kongxx"});
- response=myClient.execute(request);
- System.out.println(response.getResult());
- Useruser=myClient.execute(request,newMyGenericResponseHandler<User>());
- System.out.println(user);
- response=myClient.execute(newMyGenericRequest(MyUserServiceImpl.class,"test",newClass<?>[]{},newObject[]{}));
- System.out.println(response.getResult());
- }
- }