1. 程式人生 > >Android之Socket檔案及訊息傳輸

Android之Socket檔案及訊息傳輸

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