背景:
最近在做一个有APP和后台Java程序通信的小项目,需求中需要保证APP和后台程序一次通信的同时能够传输文本和图片、等多种类型的信息。由于以前都没有遇到过这种问题,要么是直接传输问题,要么就是直接传输文件,同时传输的需求还是第一次碰到,所以还确实困扰了自己一段时间。以下提供两种解决方案。
解决方式一:
自定义通信协议。将传输的所有的数据都转换成byte数组,然后根据自定义的通信协议在接收端进行二次解析。
这个方式也是我在网上看到的一位大佬实现的。具体的实现和示例代码参考以下网页:
http://www.cnblogs.com/xiaoxiongbuwawa/archive/2013/01/07/2849180.html
不过这不是我主要想介绍的,因为这个方式的局限性还是蛮强的,而且解析起来很复杂,我主要想要介绍的是第二种方式。
解决方式二:
使用Java的对象流。自定义一个传输对象的类,将所有可能会出现的数据类型封装在一个对象里,在接收端收到一个对象后解析成原有的对象然后再依次读取出其中的内容。
该方式的好处显而易见。首先不需要自己定义通信协议,只需要自己新建一个传输的类,然后调用java封装好的objectStream就好了。其次接收端获取不同的值的过程很简单,简单的使用getter就能得到不同类型的值避免了复杂的定位操作。
不过使用对象流需要注意几个问题:
1.定义好的传输类必须继承 Serializable接口,保证该类可以被序列化。
2.类里面的所有成员变量都必须要是可以序列化的。例如int,String,byte[ ]等。
3.传输类可以嵌套包含其他类,但是嵌套的类必须保证继承了Serializable接口,同时内部的变量也全部都可以被序列化。
4.客户端和服务器端都需要包含传输类以及传输类里面出现过的嵌套的类,这样才能保证对象的解析正常。
关于上面的问题,我在想使用BufferedImage来存储图片并作为传输类的成员变量是就出错了,因为Bufferedimage这种对象不能被序列化,所以最后我使用的是byte[ ]来存储图片。
下面给出示例程序的代码:
(1)定义传输类:其中Picture 是我自己定义的另外一个类,用来表示传输的图片
import java.util.ArrayList;
import java.util.List;
/**
* Created by 32706 on 2017/3/12.
* 该类定义数据传送类
*/
public class ConnectionData implements java.io.Serializable{
String function=null;//功能字段
String phone=null;//用户手机号
String password=null;//用户密码
String token=null;//token数据
String state="successful";
String wrong_info=null;
List<Picture> picture_list=null;//传输的图片集合
/**设置各个参数**/
public void setFunction(String fun)
{
this.function=fun;
}
public void setPhone(String phone)
{
this.phone=phone;
}
public void setPassword(String password)
{
this.password=password;
}
public void setToken(String tk)
{
this.token=tk;
}
public void setPicture_list(List<Picture> in)
{
if(this.picture_list==null)
picture_list=new ArrayList<>();
this.picture_list.addAll(in);
}
public void setState(String in)
{
this.state=in;
}
public void setWrong_info(String info)
{
if(this.wrong_info==null)
{
this.wrong_info=info;
}
else {
this.wrong_info+=info;
}
}
/**取出各个参数**/
public String getFunction()
{
return this.function;
}
public String getPhone()
{
return this.phone;
}
public String getPassword()
{
return this.password;
}
public String getToken()
{
return this.token;
}
public List<Picture> getPicture_list()
{
return this.picture_list;
}
public String getState()
{
return this.state;
}
public String getWrong_info()
{
return this.wrong_info;
}
}
class Picture implements Serializable//图片类{
private String picture_no;
private String pic_name;
private long pic_size;
private byte[] pic_data;
public void setPicture_no(String picture_no) {
this.picture_no = picture_no;
}
public void setPic_name(String pic_name) {
this.pic_name = pic_name;
}
public void setPic_size(long pic_size) {
this.pic_size = pic_size;
}
public void setPic_data(byte[] pic_data) {
this.pic_data=new byte[pic_data.length];
for(int i=0;i<pic_data.length;i++)
{
this.pic_data[i]=pic_data[i];
}
}
public String getPicture_no()
{
return picture_no;
}
public byte[] getPic_data()
{
return pic_data;
}
public String getPic_name(){return pic_name;}
public long getPic_size()
{
return pic_size;
}
}
服务器端示例代码:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 本类测试对象传输
*/
public class MyServer {
private final static Logger logger = Logger.getLogger(MyServer.class.getName());
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(23333);
while (true) {
Socket socket = server.accept();
invoke(socket);
}
}
private static void invoke(final Socket socket) throws IOException {
new Thread(new Runnable() {
public void run() {
ObjectInputStream is = null;
ObjectOutputStream os = null;
try {
is = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
os = new ObjectOutputStream(socket.getOutputStream());
Object obj = is.readObject();
ConnectionData con=(ConnectionData)obj;
System.out.println("收到的手机号: " + con.getPhone());
System.out.println("收到的图片张数为:"+((con.getPicture_list()==null)?0:(con.getPicture_list().size()))));
con.setPhone("1");
os.writeObject(con);
os.flush();
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
} catch(ClassNotFoundException ex) {
logger.log(Level.SEVERE, null, ex);
} finally {
try {
is.close();
} catch(Exception ex) {}
try {
os.close();
} catch(Exception ex) {}
try {
socket.close();
} catch(Exception ex) {}
}
}
}).start();
}
}
客户端示例代码:
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyClient {
private final static Logger logger = Logger.getLogger(MyClient.class.getName());
public static void main(String[] args) throws Exception {
Socket socket = null;
ObjectOutputStream os = null;
ObjectInputStream is = null;
try {
socket = new Socket("localhost", 23333);
os = new ObjectOutputStream(socket.getOutputStream());
ConnectionData con=new ConnectionData();
con.setFunction("上传");
con.setToken("be55ed7101044ce72eddc8e5362de9ba");
con.setPhone("15271581490");
con.setPassword("password");
Picture tp=new Picture();
tp.setPic_data(load_picture_from_file("C:\\Users\\32706\\Pictures\\待选壁纸\\9-1.jpg"));
tp.setPic_name("test1.jpg");
List<Picture> conP=new ArrayList<>();
conP.add(tp);
con.setPicture_list(conP);
os.writeObject(con);
os.flush();
is = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
Object obj = is.readObject();
if (obj != null) {
con=(ConnectionData)obj;
System.out.println(con.getToken()+"\n"+con.getState()+"\n"+con.getWrong_info());
System.out.println("收到的手机号变为:"+con.getPhone());
if(con.getPicture_list()!=null&&con.getPicture_list().size()!=0)
{
System.out.println("成功接受到返回的图片");
}
}
} catch(IOException ex) {
logger.log(Level.SEVERE, null, ex);
} finally {
try {
is.close();
} catch(Exception ex) {}
try {
os.close();
} catch(Exception ex) {}
try {
socket.close();
} catch(Exception ex) {}
}
}
private static byte[] load_picture_from_file(String pic_url)
{
try {
File f = new File(pic_url);
InputStream is = new FileInputStream(f);
byte[] b = new byte[(int) f.length()];
is.read(b);
is.close();
return b;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
}
通过Java的对象流,我们可以将各种数据类型打包在一起这样就能方便的实现socket通信中不同格式数据同时传输。同时两端解析出数据也变得更加简单和高效。