最近遇到一个java socket问题,具体业务场景不做描述,只谈谈具体的技术需求,如下所述:
1.client端从server端下载java class文件.
2.完成后动态加载该类,并调用该类的某个方法。
关键点:
1.利用java socket传递java class文件。
2.如何将某path下的java class文件加载到JVM。
测试实现:
需求很明确,没有特别的地方,如下是我写的测试代码。
DataClient: 传递类名等信息到server,从服务端下载指定的java类文件。
DataServer: 根据client端参数传递指定的类文件到client端。
MyClassLoader:以byte格式读取class文件,并加载到JVM。
SocketTool: java socket工具类。
TestClass: 被传递的java class。
package org.socket;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Supply for class file download
*
* @author chares
* @date 2013-5-9
*
*/
public class DataServer {
int port = 8821;
void start() {
Socket socket = null;
try {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
// 选择进行传输的文件
String filePath = "c:\\TestClass.class";
File fi = new File(filePath);
System.out.println("~~~~ [server] 文件长度:" + (int) fi.length());
socket = serverSocket.accept();
System.out.println("~~~~ [server] : 建立socket链接");
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
dis.readByte();
DataInputStream fis = new DataInputStream(
new BufferedInputStream(new FileInputStream(filePath)));
DataOutputStream ps = new DataOutputStream(socket.getOutputStream());
// 将文件名及长度传给客户端。这里要真正适用所有平台,例如中文名的处理,还需要加工,具体可以参见Think In Java
// 4th里有现成的代码
ps.writeUTF(fi.getName());
ps.flush();
ps.writeLong((long) fi.length());
ps.flush();
int bufferSize = 8192;
byte[] buf = new byte[bufferSize];
while (true) {
int read = 0;
if (fis != null) {
read = fis.read(buf);
// 从包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组 b
// 中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾 (end of file)
// 或抛出异常之前,此方法将一直阻塞。
}
if (read == -1) {
break;
}
ps.write(buf, 0, read);
}
ps.flush();
// 注意关闭socket链接哦,不然客户端会等待server的数据过来,
// 直到socket超时,导致数据不完整。
fis.close();
socket.close();
System.out.println("~~~~ [server] 文件传输完成");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String arg[]) {
new DataServer().start();
}
}
package org.socket;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
/**
* Get class file from DataServer.
*
* @author chares
* @date 2013-5-9
*
*/
public class DataClient {
private SocketTool cs = null;
private String ip = "localhost";// 设置成服务器IP
private int port = 8821;
private String sendMessage = "Windows";
public DataClient() {
try {
if (createConnection()) {
sendMessage();
getMessage();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private boolean createConnection() {
cs = new SocketTool(ip, port);
try {
cs.crtConnection();
System.out.print("~~~~ [client] : 连接服务器成功!" + "\n");
return true;
} catch (Exception e) {
System.out.print("~~~~ [client] : 连接服务器失败!" + "\n");
return false;
}
}
private void sendMessage() {
if (cs == null)
return;
try {
cs.sendMessage(sendMessage);
} catch (Exception e) {
System.out.print("~~~~ [client] : 发送消息失败!" + "\n");
}
}
private void getMessage() {
if (cs == null)
return;
DataInputStream inputStream = null;
try {
inputStream = cs.getMessageStream();
} catch (Exception e) {
System.out.print("~~~~ [client] : 接收消息缓存错误\n");
return;
}
try {
// 本地保存路径,文件名会自动从服务器端继承而来
String savePath = "d:\\test_java\\";
int bufferSize = 8192;
byte[] buf = new byte[bufferSize];
int passedlen = 0;
long len = 0;
savePath += inputStream.readUTF();
DataOutputStream fileOut = new DataOutputStream(
new BufferedOutputStream(new BufferedOutputStream(
new FileOutputStream(savePath))));
len = inputStream.readLong();
System.out.println("~~~~ [client] : 文件的长度为:" + len + "\n");
System.out.println("~~~~ [client] : 开始接收文件!" + "\n");
while (true) {
int read = 0;
if (inputStream != null) {
read = inputStream.read(buf);
}
passedlen += read;
if (read == -1) {
break;
}
// 下面进度条本为图形界面的prograssBar做的,这里如果是打文件,可能会重复打印出一些相同的百分比
System.out.println("~~~~ [client] : 文件接收了" + (passedlen * 100 / len) + "%\n");
fileOut.write(buf, 0, read);
}
System.out.println("~~~~ [client] : 接收完成,文件存为" + savePath + "\n");
fileOut.close();
} catch (Exception e) {
System.out.println("~~~~ [client] : 接收消息错误" + "\n");
e.printStackTrace();
}
}
public static void main(String arg[]) {
new DataClient();//download class File
new MyClassLoader().load("org.socket.TestClass","getName","");// load class
}
}
package org.socket;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
public class SocketTool {
private String ip;
private int port;
private Socket socket = null;
DataOutputStream out = null;
DataInputStream getMessageStream = null;
public SocketTool(String ip, int port) {
this.ip = ip;
this.port = port;
}
/**
* 创建socket连接
* @throws Exception
*/
public void crtConnection() throws Exception {
try {
socket = new Socket(ip, port);
} catch (Exception e) {
e.printStackTrace();
if (socket != null)
socket.close();
throw e;
} finally {
}
}
public void sendMessage(String sendMessage) throws Exception {
try {
out = new DataOutputStream(socket.getOutputStream());
if (sendMessage.equals("Windows")) {
out.writeByte(0x1);
out.flush();
return;
}
if (sendMessage.equals("Unix")) {
out.writeByte(0x2);
out.flush();
return;
}
if (sendMessage.equals("Linux")) {
out.writeByte(0x3);
out.flush();
} else {
out.writeUTF(sendMessage);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
if (out != null)
out.close();
throw e;
} finally {
}
}
public DataInputStream getMessageStream() throws Exception {
try {
getMessageStream = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
return getMessageStream;
} catch (Exception e) {
e.printStackTrace();
if (getMessageStream != null)
getMessageStream.close();
throw e;
} finally {
}
}
public void shutDownConnection() {
try {
if (out != null)
out.close();
if (getMessageStream != null)
getMessageStream.close();
if (socket != null)
socket.close();
} catch (Exception e) {
}
}
}
package org.socket;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
/**
* Custom class loader
*
* @author chares
* @date 2013-5-9
*
*/
public class MyClassLoader extends ClassLoader {
public static final String DIR_PATH = "file:d:\\test_java\\";
public static final String DONET_CLASS = ".class";
/**
* Load java class and invoke the method
*
* @param methodName
* @param params String:param1|String:param2
*
*/
public void load(String fullClassName, String methodName, String params) {
try {
Class clazz = loadClass(fullClassName, this.getClassPathAbsolute(fullClassName));
System.out.println("~~~~ clazz's classloader : "+ clazz.getClassLoader());
// Class clazz = Class.forName(CLASS_NAME);
Method[] methods = clazz.getDeclaredMethods();
if (methods != null && methods.length > 0) {
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName)) {
Object object = clazz.newInstance();
System.out.println("~~~~ object : "+ object);
Object rtnObj = methods[i].invoke(object, new Object[]{});
System.out.println("~~~~ rtnObj : "+ rtnObj);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Get java.lang.Class from a XX.class file
* @param name
* @param classUrl
* @return
* @throws ClassNotFoundException
*/
public Class loadClass(String name, String classUrl)
throws ClassNotFoundException {
if (!"org.socket.TestClass".equals(name))
return super.loadClass(name);
try {
/*
* String url = "file:C:/data/projects/tutorials/web/WEB-INF/" +
* "classes/reflection/MyObject.class";
*/
URL myUrl = new URL(classUrl);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while (data != -1) {
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass(name, classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Get absolute path from the java full class name(org.socket.TestClass)
* @param fullClassName
* @return
*/
private String getClassPathAbsolute(String fullClassName){
String[] parts = fullClassName.split("\\.");
if(parts != null && fullClassName.length() > 0){
return DIR_PATH + parts[parts.length - 1] + DONET_CLASS;
}
return "";
}
public static void main(String[] args) {
//ClassLoader parentCl = MyClassLoader.class.getClassLoader();
//System.out.println("~~~~ parentCl classloader : "+ parentCl);
new MyClassLoader().load("org.socket.TestClass","getName","");// load class
}
}
package org.socket;
/**
* @author chares
*
*/
public class TestClass {
public String name = "init";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}