Android之Socket檔案及訊息傳輸
阿新 • • 發佈:2019-01-03
socket傳輸檔案時,只能通過流去讀取訊息。當socket接收檔案、訊息等不同的資料時,如何區別是字串還是流是很重要的。為了保證接收到的資料型別統一(資料是字串還是流),需要定義協議。定義協議的方式有很多種:
1.傳送一個握手訊號。 根據握手訊號來確定傳送的是字串還是流
2.定義了Header(頭)和Body(實體),頭是固定大小的,用來告訴接收者資料的格式、用途、長度等資訊,接收者根據Header 來接受Body。
3.自定義協議。
這裡採用自定義協議,統一傳輸JSON資料,根據欄位標識傳輸的是字串還是流,接收者根據標識去解析資料即可。
協議的實體類(Transmission):
public class Transmission implements Parcelable { /** * 檔名稱 */ public String fileName; /** *檔案長度 */ public long fileLength; /** *傳輸型別 */ public int transmissionType; /** *傳輸內容 */ public String content; /** *傳輸長度 */ public long transLength; public Transmission(){ } protected Transmission(Parcel in){ this.fileName = in.readString(); this.fileLength = in.readLong(); this.transmissionType = in.readInt(); this.content = in.readString(); this.transLength = in.readLong(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.fileName); dest.writeLong(this.fileLength); dest.writeInt(this.transmissionType); dest.writeString(this.content); dest.writeLong(this.transLength); } public static final Creator<Transmission> CREATOR = new Creator<Transmission>() { @Override public Transmission createFromParcel(Parcel in) { return new Transmission(in); } @Override public Transmission[] newArray(int size) { return new Transmission[size]; } }; }
根據欄位transmissionType去標識傳輸(序列化)或者接收(發序列化)的型別。傳輸的過程中始終都是以JSON的格式存在的。在傳輸檔案時需要把流轉換成字串(Base64加密與解密)。
客戶端(ClientThread)
public class RemoteClientSocket extends Thread { private Gson mGson; private PrintWriter mPrintWriter; private Socket mSocket; private Handler mWriteHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(msg.what == Constants.TRANSFER_STR){ transferStr(msg.obj); }else if(msg.what == Constants.TRANSFER_FILE){ transferFile(msg.obj.toString()); } } }; public RemoteClientSocket(){ mGson = new Gson(); } @Override public void run() { super.run(); try { mPrintWriter = new PrintWriter(mSocket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); try { if (mPrintWriter != null) { mPrintWriter.close(); } if (mSocket != null) { mSocket.close(); } } catch (IOException e1) { e1.printStackTrace(); } } } /** * 標點資訊內容 * @param object */ private void transferStr(Object object){ new Thread(new Runnable() { @Override public void run() { Transmission trans = new Transmission(); trans.transmissionType = Constants.TRANSFER_STR; trans.fileName = ""; trans.fileLength = 0; trans.content = XXXXXXXX; trans.transLength = XXXXX; mPrintWriter.write(mGson.toJson(trans)); mPrintWriter.flush(); } }).start(); } /** * 檔案路徑 * @param filePath */ private void transferFile(String filePath){ new Thread(new Runnable() { @Override public void run() { FileInputStream fileInputStream = null; File file = new File(filePath); try { fileInputStream = new FileInputStream(file); Transmission trans = new Transmission(); trans.transmissionType = Constants.TRANSFER_FILE; trans.fileName = file.getName(); trans.fileLength = file.length(); trans.transLength = 0; byte[] bytes = new byte[1024]; int length = 0; while ((length = fileInputStream.read(bytes, 0, bytes.length)) != -1){ trans.transLength += length; trans.content = Base64Utils.encode(bytes); mPrintWriter.write(mGson.toJson(trans)); mPrintWriter.flush(); } } catch (IOException e) { e.printStackTrace(); mPrintWriter.close(); } finally { if(fileInputStream != null){ try { fileInputStream.close(); }catch (IOException e1){ e1.printStackTrace(); } } } } }).start(); } public Handler getWriteHandler() { return mWriteHandler; } public void setWriteHandler(Handler writeHandler) { mWriteHandler = writeHandler; } public void connectLocalServer(){ try { mSocket = new Socket(Constants.HOST, Constants.PORT); } catch (IOException e) { e.printStackTrace(); } } public boolean isConnectedToLocalServer(){ return mSocket.isConnected(); } public void disConnectToLocalServer(){ try { if(mSocket != null) { mSocket.close(); } } catch (IOException e) { e.printStackTrace(); } } public boolean isClosedToLocalServer(){ return mSocket.isClosed(); } }
public class Constants {
public static final String HOST = "XXX.XXX.XXX.XXX";
public static final int PORT = XXXX;
//連線成功
public static final int CONNECTED_SUCCESS = 0;
//連線失敗
public static final int CONNECTED_FAILED = 1;
//收到重新命名訊息
public static final int RECEIVE_RENAME_MSG = 0;
//傳送重新命名訊息
public static final int TRANSFER_RENAME_MSG = 1;
//收到刪除訊息
public static final int RECEIVE_CANCEL_MSG = 2;
//傳送刪除訊息
public static final int TRANSFER_CANCEL_MSG = 3;
//收到檔案
public static final int RECEIVE_FILE = 4;
//傳送檔案
public static final int TRANSFER_FILE = 5;
//收到字串
public static final int RECEIVE_STR = 6;
//傳輸字串
public static final int TRANSFER_STR = 7;
//傳送
public static final int TRANSFER = 1;
//接收
public static final int RECEIVE = 2;
}
把位元組流轉換成字串傳輸Base64Utils.encode(bytes),接收方把字串解析成位元組流並寫入檔案。
public class Base64Utils {
public static byte[] decode(final byte[] bytes) {
return Base64.decodeBase64(bytes);
}
/**
* 二進位制資料編碼為BASE64字串
*
* @param bytes
* @return
* @throws Exception
*/
public static String encode(final byte[] bytes) {
return new String(Base64.encodeBase64(bytes));
}
}
服務端(ServerThread)
public class LocalServerSocket implements Runnable{
private DataBaseDao mDataBaseDao = DataBaseDao.getInstance();
Socket mSocket;
JsonReader mJsonReader;
Gson mGson;
boolean mCreateFile = true;
public LocalServerSocket(Socket socket) throws IOException {
mSocket = socket;
mJsonReader = new JsonReader(new InputStreamReader(mSocket.getInputStream(), "utf-8"));
mJsonReader.setLenient(true);
mGson = new Gson();
}
@Override
public void run() {
readMessage();
}
//讀取資料
private void readMessage() {
String content = null;
FileOutputStream fileOutputStream = null;
try {
while (mJsonReader.hasNext()) {
Transmission trans = mGson.fromJson(mJsonReader, Transmission.class);
if (trans.transmissionType == Constants.TRANSFER_STR) {
try {
//TODO
}
} catch (JSONException e) {
e.printStackTrace();
}
} else if (trans.transmissionType == Constants.TRANSFER_FILE) {
long fileLength = trans.fileLength;
long transLength = trans.transLength;
if (mCreateFile) {
mCreateFile = false;
fileOutputStream = new FileOutputStream(new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/XXX/XXX/" + trans.fileName));
}
byte[] b = Base64Utils.decode(trans.content.getBytes());
fileOutputStream.write(b, 0, b.length);
if (transLength == fileLength) {
mCreateFile = true;
fileOutputStream.flush();
fileOutputStream.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
if (mSocket != null) {
mSocket.close();
}
if (mJsonReader != null) {
mJsonReader.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
new Thread(new Runnable() {
@Override
public void run() {
try {
mServerSocket = new ServerSocket(Constants.PORT);
while (true){
//此行程式碼會阻塞,將一直等待別人的連線
mSocket = mServerSocket.accept();
//每當客戶端連線後啟動一條ServerThread執行緒為該客戶端服務
new Thread(new LocalServerSocket(mSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();