Java Socket实战之八 socket提升

本文介绍了一种通过 Java Socket 进行高效通信的方法。利用自定义请求和响应对象,结合多线程与 NIO 技术,实现了高性能的远程服务调用。通过具体的代码示例,展示了如何构建客户端和服务端,并进行数据交换。

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;
	}
	
}

MyUserService.java
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);
	}
}

3. 客户端类,主要有三个类MyClient,MyClientSimpleImpl和MyClientNIOImpl。MyClient是客户端接口类,用来向Server端发送请求并获取相应;MyClientSimpleImpl和MyClientNIOImpl是两个实现类,其中MyClientSimpleImpl是使用简单Socket实现的,MyClientNIOImpl是使用java nio包里的类实现的,这个实现会有更好的性能。

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());
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值