• Android连接热点的Socket文件传输


    最近把测试丢过来的种种BUG解决后,终于有时间去研究研究Socket通信,再加上以前做的WiFi连接和热点开启,于是有了现在的这篇博文:创建热点发送文件,让另一台手机连接热点接收文件。

    效果图:

    两台设备是如何传输文件的:

    • 发送端->创建WiFi热点
    • 接收端->连接热点
    • 发送端->发送文件列表
    • 接收端->收到后展示文件列表,选择要接收的文件发送给发送端
    • 发送端->发送所选文件
    • 接收端->开始接收…

    发送端->创建WiFi热点:

    由于Android没有直接开启热点的API,所以我们这里采用反射。

    /**
     * 开启便携热点
     * @param context 上下文
     * @param SSID 便携热点SSID
     * @param password 便携热点密码
     * @return
     */
    public static boolean openAp(Context context, String SSID, String password) {
        if(TextUtils.isEmpty(SSID)) {
            return false;
        }
        WifiManager wifiManager = (WifiManager) context.getSystemService(context.WIFI_SERVICE);
        if (wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(false);
        }
        WifiConfiguration wifiConfiguration = getApConfig(SSID, password);
        try {
            if(wifiManager.isWifiEnabled()) {
                //关闭WiFi
                wifiManager.setWifiEnabled(false);
            }
            if(isApOn(context)) {
                //关闭热点
                closeAp(context);
            }
            //使用反射开启Wi-Fi热点
            Method method = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
            method.invoke(wifiManager, wifiConfiguration, true);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    接收端->连接热点:

    连接热点前先开启WiFi广播监听事件,然后开启WiFi,扫描周围可用WiFi列表,拿到SSID再进行连接,最后在WiFi广播监听事件中比较已连接WIFI的SSID是否正确。

    WiFi广播监听事件:

    /**
     * 注册监听WiFi操作的系统广播
     */
    private void registerWifiReceiver() {
        IntentFilter filter = new IntentFilter();
        //监听WiFi开启与关闭
        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        //监听扫描周围可用WiFi列表结果
        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
        //监听WiFi连接与断开
        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        //注册广播
        registerReceiver(mWifiBroadcaseReceiver, filter);
    }
    /**
     * 反注册WiFi相关的系统广播
     */
    private void unregisterWifiReceiver() {
        if (mWifiBroadcaseReceiver != null) {
            unregisterReceiver(mWifiBroadcaseReceiver);
            mWifiBroadcaseReceiver = null;
        }
    }

    WiFi广播接收器:

    public abstract class WifiBroadcaseReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent != null) {
                if(intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                    //监听WiFi开启/关闭事件
                    int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0);
                    if(wifiState == WifiManager.WIFI_STATE_ENABLED) {
                        //WiFi已开启
                        onWifiEnabled();
                    } else if(wifiState == WifiManager.WIFI_STATE_DISABLED) {
                        //WiFi已关闭
                        onWifiDisabled();
                    }
                } else if(intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                    WifiMgr wifiMgr = new WifiMgr(context);
                    List<ScanResult> scanResults = wifiMgr.getScanResults();
                    if(wifiMgr.isWifiEnabled() && scanResults != null && scanResults.size() > 0) {
                        //成功扫描
                        onScanResultsAvailable(scanResults);
                    }
                } else if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                    //网络状态改变的广播
                    NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                    if (info != null) {
                        if (info.getState().equals(NetworkInfo.State.CONNECTED)) {
                            //WiFi已连接
                            WifiMgr wifiMgr = new WifiMgr(context);
                            String connectedSSID = wifiMgr.getConnectedSSID();
                            onWifiConnected(connectedSSID);
                        } else if (info.getState().equals(NetworkInfo.State.DISCONNECTED)) {
                            //WiFi已断开连接
                            onWifiDisconnected();
                        }
                    }
                }
            }
        }
    
        public abstract void onWifiEnabled();
    
        public abstract void onWifiDisabled();
    
        public abstract void onScanResultsAvailable(List<ScanResult> scanResults);
    
        public abstract void onWifiConnected(String connectedSSID);
    
        public abstract void onWifiDisconnected();
    }

    打开和关闭WiFi:

    mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    
    /**
     * 打开WiFi
     */
    public void openWifi() {
        if (!mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(true);
        }
    }
    /**
     * 关闭WiFi
     */
    public void closeWifi() {
        if (mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(false);
        }
    
    /**
     * 当前WiFi是否开启
     */
    public boolean isWifiEnabled() {
        return mWifiManager.isWifiEnabled();
    }

    扫描周围可用WiFi列表:

    /**
     * 扫描周围可用WiFi
     * @return
     */
    public boolean startScan() {
        if(isWifiEnabled()) {
            return mWifiManager.startScan();
        }
        return false;
    }

    拿到WiFi扫描结果并且连接热点,当接收端连接成功后,会发一个UDP广播告知局域网内设备连接热点成功:

    /**
     * WiFi广播接收器
     */
    private WifiBroadcaseReceiver mWifiBroadcaseReceiver = new WifiBroadcaseReceiver() {
        @Override
        public void onWifiEnabled() {
            //WiFi已开启,开始扫描可用WiFi
            mWifiMgr.startScan();
        }
        @Override
        public void onWifiDisabled() {
            //WiFi已关闭,清除可用WiFi列表
            mSelectedSSID = "";
            mScanResults.clear();
            setupWifiAdapter();
        }
        @Override
        public void onScanResultsAvailable(List<ScanResult> scanResults) {
            //扫描周围可用WiFi成功,设置可用WiFi列表
            mScanResults.clear();
            mScanResults.addAll(scanResults);
            setupWifiAdapter();
        }
        @Override
        public void onWifiConnected(String connectedSSID) {
            //判断指定WiFi是否连接成功
            if (connectedSSID.equals(mSelectedSSID) && !mIsSendInitOrder) {
                //连接成功
                //告知发送端,接收端初始化完毕
                mHandler.sendEmptyMessage(MSG_FILE_RECEIVER_INIT_SUCCESS);
                mIsSendInitOrder = true;
            } else {
                  //连接成功的不是设备WiFi,清除该WiFi,重新扫描周围WiFi
                  LogUtils.e("连接到错误WiFi,正在断开重连...");
                  mWifiMgr.disconnectWifi(connectedSSID);
                  mWifiMgr.startScan();
            }
        }
        @Override
        public void onWifiDisconnected() {
        }
    };

    发送端->发送文件列表:

    当发送端收到初始化完毕指令时,将用UDP广播发送文件列表。

    /**
     * 等待接收端发送初始化完成指令,向其发送文件列表
     * @param serverPort
     * @throws Exception
     */
    private void receiveInitSuccessOrder(int serverPort) throws Exception {
        //确保WiFi连接后获取正确IP地址
        int tryCount = 0;
        String localIpAddress = ApMgr.getHotspotLocalIpAddress(getContext());
        while (localIpAddress.equals(Consts.DEFAULT_UNKNOW_IP) && tryCount < Consts.DEFAULT_TRY_COUNT) {
            Thread.sleep(1000);
            localIpAddress = ApMgr.getHotspotLocalIpAddress(getContext());
            tryCount ++;
        }
        /** 这里使用UDP发送和接收指令 */
        mDatagramSocket = new DatagramSocket(serverPort);
        while (true) {
            byte[] receiveData = new byte[1024];
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            mDatagramSocket.receive(receivePacket);
            String response = new String(receivePacket.getData()).trim();
            if(isNotEmptyString(response)) {
                LogUtils.e("接收到的消息 -------->>>" + response);
                if(response.equals(Consts.MSG_FILE_RECEIVER_INIT_SUCCESS)) {
                    //初始化成功指令
                    mHandler.sendEmptyMessage(MSG_FILE_RECEIVER_INIT_SUCCESS);
                    //发送文件列表
                    InetAddress inetAddress = receivePacket.getAddress();
                    int port = receivePacket.getPort();
                    //通过UDP发送文件列表给接收端
                    sendFileInfoListToFileReceiverWithUdp(inetAddress, port);
                } else if(response.equals(Consts.MSG_START_SEND)) {
                    //开始发送指令
                    initSenderServer();
                } else {
                    //接收端发来的待发送文件列表
                    parseFileInfo(response);
                }
            }
        }
    }
    
    /**
     * 通过UDP发送文件列表给接收端
     * @param ipAddress IP地址
     * @param serverPort 端口号
     */
    private void sendFileInfoListToFileReceiverWithUdp(InetAddress ipAddress, int serverPort) {
        if(!isEmptyList(mAllFileInfos)) {
            String jsonStr = FileInfo.toJsonStr(mAllFileInfos);
            DatagramPacket sendFileInfoPacket = new DatagramPacket(jsonStr.getBytes(), jsonStr.getBytes().length, ipAddress, serverPort);
            try {
                //发送文件列表
                mDatagramSocket.send(sendFileInfoPacket);
                LogUtils.i("发送消息 --------->>>" + jsonStr + "=== Success!");
                mHandler.obtainMessage(MSG_SET_STATUS, "成功发送文件列表...").sendToTarget();
            } catch (IOException e) {
                e.printStackTrace();
                LogUtils.i("发送消息 --------->>>" + jsonStr + "=== 失败!");
            }
        }
    }

    接收端->选择文件,并告知发送端开始发送:

    收到文件列表后,接收端会将文件列表展示在RecyclerView控件,通过选择需要接收的文件,点击“开始发送”按钮,将发送“开始发送”指令到发送端,开启端口进行文件接收。

    /**
     * 设置接收文件列表适配器
     */
    private void setupReceiveFilesAdapter() {
        List<Map.Entry<String, FileInfo>> fileInfos = AppContext.getAppContext().getReceiverFileInfoMap();
        Collections.sort(fileInfos, Consts.DEFAULT_COMPARATOR);
        //设置适配器
        mReceiveFilesAdapter = new CommonAdapter<Map.Entry<String, FileInfo>>(getContext(), R.layout.item_files_selector, fileInfos) {
            @Override
            protected void convert(ViewHolder holder, Map.Entry<String, FileInfo> fileInfoMap, int position) {
                final FileInfo fileInfo = fileInfoMap.getValue();
                //文件路径
                holder.setText(R.id.tv_item_files_selector_file_path, fileInfo.getFilePath());
                //文件大小
                holder.setText(R.id.tv_item_files_selector_size, FileUtils.FormetFileSize(fileInfo.getSize()));
                //文件接收状态
                if(fileInfo.getProgress() >= 100) {
                    holder.setText(R.id.tv_item_files_selector_status, "接收完毕");
                } else if(fileInfo.getProgress() == 0) {
                    holder.setText(R.id.tv_item_files_selector_status, "准备接收");
                } else if(fileInfo.getProgress() < 100) {
                    holder.setText(R.id.tv_item_files_selector_status, "正在接收");
                } else {
                    holder.setText(R.id.tv_item_files_selector_status, "接收失败");
                }
                //文件接收进度
                ProgressBar progressBar = holder.getView(R.id.pb_item_files_selector);
                progressBar.setProgress(fileInfo.getProgress());
                //选中文件
                CheckBox checkBox = holder.getView(R.id.cb_item_files_selector);
                checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        if(isChecked) {
                            mSendFileInfos.add(fileInfo);
                        } else {
                            mSendFileInfos.remove(fileInfo);
                        }
                        //选中的文件个数大于零才可点击底部按钮
                        btnSendFileList.setEnabled(mSendFileInfos.size() > 0);
                    }
                });
            }
        };
        mReceiveFilesRecyclerView.setAdapter(mReceiveFilesAdapter);
        //设置ListView样式
        mReceiveFilesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        //分割线
        mReceiveFilesRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
    }
    
    /**
     * 发送选中的文件列表给发送端
     */
    private void sendFileListToFileSender() {
        new Thread() {
            @Override
            public void run() {
                try {
                    //获取IP地址
                    String serverIp = mWifiMgr.getIpAddressFromHotspot();
                    if(mDatagramSocket == null) {
                        //解决:java.net.BindException: bind failed: EADDRINUSE (Address already in use)
                        mDatagramSocket = new DatagramSocket(null);
                        mDatagramSocket.setReuseAddress(true);
                        mDatagramSocket.bind(new InetSocketAddress(Consts.DEFAULT_SERVER_UDP_PORT));
                    }
                    //发送选中的文件列表
                    InetAddress ipAddress = InetAddress.getByName(serverIp);
                    String jsonStr = FileInfo.toJsonStr(mSendFileInfos);
                    DatagramPacket sendPacket = new DatagramPacket(jsonStr.getBytes(), jsonStr.getBytes().length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT);
                    mDatagramSocket.send(sendPacket);
                    LogUtils.i("发送消息 ------->>>" + jsonStr);
                    //发送开始发送文件指令
                    byte[] sendData = Consts.MSG_START_SEND.getBytes(BaseTransfer.UTF_8);
                    DatagramPacket sendPacket2 = new DatagramPacket(sendData, sendData.length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT);
                    mDatagramSocket.send(sendPacket2);
                    LogUtils.i("发送消息 ------->>>" + sendData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
    
    /**
     * ServerSocket启动线程
     */
    private class ReceiveServerRunnable implements Runnable {
        @Override
        public void run() {
            try {
                //开始接收文件
                String serverIp = mWifiMgr.getIpAddressFromHotspot();
                List<Map.Entry<String, FileInfo>> fileInfoList = AppContext.getAppContext().getReceiverFileInfoMap();
                Collections.sort(fileInfoList, Consts.DEFAULT_COMPARATOR);
                for(final Map.Entry<String, FileInfo> fileInfoMap : fileInfoList) {
                    //连接发送端,逐个文件进行接收
                    final int position = fileInfoList.indexOf(fileInfoMap);
                    mClientSocket = new Socket(serverIp, Consts.DEFAULT_FILE_RECEIVE_SERVER_PORT);
                    FileReceiver fileReceiver = new FileReceiver(mClientSocket, fileInfoMap.getValue());
                    fileReceiver.setOnReceiveListener(new FileReceiver.OnReceiveListener() {
                        @Override
                        public void onStart() {
                            mHandler.obtainMessage(MSG_SET_STATUS, "开始接收"+ FileUtils.getFileName(fileInfoMap.getValue().getFilePath())).sendToTarget();
                        }
                        @Override
                        public void onProgress(FileInfo fileInfo, long progress, long total) {
                            //更新接收进度视图
                            int i_progress = (int) (progress * 100 / total);
                            LogUtils.e("正在接收:" + fileInfo.getFilePath() + "
    当前进度:" + i_progress);
                            Message msg = new Message();
                            msg.what = MSG_UPDATE_PROGRESS;
                            msg.arg1 = position;
                            msg.arg2 = i_progress;
                            mHandler.sendMessage(msg);
                        }
                        @Override
                        public void onSuccess(FileInfo fileInfo) {
                            //接收成功
                            mHandler.obtainMessage(MSG_SET_STATUS, "文件:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收成功").sendToTarget();
                            fileInfo.setResult(FileInfo.FLAG_SUCCESS);
                            AppContext.getAppContext().updateReceiverFileInfo(fileInfo);
                            Message msg = new Message();
                            msg.what = MSG_UPDATE_PROGRESS;
                            msg.arg1 = position;
                            msg.arg2 = 100;
                            mHandler.sendMessage(msg);
                        }
                        @Override
                        public void onFailure(Throwable throwable, FileInfo fileInfo) {
                            if(fileInfo != null) {
                                //接收失败
                                mHandler.obtainMessage(MSG_SET_STATUS, "文件:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收失败").sendToTarget();
                                fileInfo.setResult(FileInfo.FLAG_FAILURE);
                                AppContext.getAppContext().updateReceiverFileInfo(fileInfo);
                                Message msg = new Message();
                                msg.what = MSG_UPDATE_PROGRESS;
                                msg.arg1 = position;
                                msg.arg2 = -1;
                                mHandler.sendMessage(msg);
                            }
                        }
                    });
                    //加入线程池执行
                    mFileReceiverList.add(fileReceiver);
                    AppContext.getAppContext().MAIN_EXECUTOR.execute(fileReceiver);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    文件接收线程:

    public class FileReceiver extends BaseTransfer implements Runnable {
    
        /**
         * 接收文件的Socket的输入输出流
         */
        private Socket mSocket;
        private InputStream mInputStream;
    
        /**
         * 待接收的文件数据
         */
        private FileInfo mFileInfo;
    
        /**
         * 用来控制线程暂停、恢复
         */
        private final Object LOCK = new Object();
        private boolean mIsPaused = false;
    
        /**
         * 设置未执行线程的不执行标识
         */
        private boolean mIsStop;
    
        /**
         * 该线程是否执行完毕
         */
        private boolean mIsFinish;
    
        /**
         * 文件接收监听事件
         */
        private OnReceiveListener mOnReceiveListener;
    
    
        public FileReceiver(Socket socket, FileInfo fileInfo) {
            mSocket = socket;
            mFileInfo = fileInfo;
        }
    
        /**
         * 设置接收监听事件
         * @param onReceiveListener
         */
        public void setOnReceiveListener(OnReceiveListener onReceiveListener) {
            mOnReceiveListener = onReceiveListener;
        }
    
        @Override
        public void run() {
            if(mIsStop) {
                return;
            }
    
            //初始化
            try {
                if(mOnReceiveListener != null) {
                    mOnReceiveListener.onStart();
                }
                init();
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.i("FileReceiver init() ------->>> occur expection");
                if(mOnReceiveListener != null) {
                    mOnReceiveListener.onFailure(e, mFileInfo);
                }
            }
    
            //发送文件实体数据
            try {
                parseBody();
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.i("FileReceiver parseBody() ------->>> occur expection");
                if(mOnReceiveListener != null) {
                    mOnReceiveListener.onFailure(e, mFileInfo);
                }
            }
    
            //文件传输完毕
            try {
                finishTransfer();
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.i("FileReceiver finishTransfer() ------->>> occur expection");
                if(mOnReceiveListener != null) {
                    mOnReceiveListener.onFailure(e, mFileInfo);
                }
            }
        }
    
        @Override
        public void init() throws Exception {
            if(mSocket != null) {
                mInputStream = mSocket.getInputStream();
            }
        }
    
        @Override
        public void parseBody() throws Exception {
            if(mFileInfo == null) {
                return;
            }
    
            long fileSize = mFileInfo.getSize();
            OutputStream fos = new FileOutputStream(FileUtils.gerateLocalFile(mFileInfo.getFilePath()));
    
            byte[] bytes = new byte[BYTE_SIZE_DATA];
            long total = 0;
            int len = 0;
    
            long sTime = System.currentTimeMillis();
            long eTime = 0;
            while ((len = mInputStream.read(bytes)) != -1) {
                synchronized (LOCK) {
                    if(mIsPaused) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                    //写入文件
                    fos.write(bytes, 0, len);
                    total = total + len;
    
                    //每隔200毫秒返回一次进度
                    eTime = System.currentTimeMillis();
                    if(eTime - sTime > 200) {
                        sTime = eTime;
                        if(mOnReceiveListener != null) {
                            mOnReceiveListener.onProgress(mFileInfo, total, fileSize);
                        }
                    }
                }
            }
    
            //文件接收成功
            if(mOnReceiveListener != null) {
                mOnReceiveListener.onSuccess(mFileInfo);
            }
            mIsFinish = true;
        }
    
        @Override
        public void finishTransfer() throws Exception {
            if(mInputStream != null) {
                try {
                    mInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            if(mSocket != null && mSocket.isConnected()) {
                try {
                    mSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 暂停接收线程
         */
        public void pause() {
            synchronized (LOCK) {
                mIsPaused = true;
                LOCK.notifyAll();
            }
        }
    
        /**
         * 恢复接收线程
         */
        public void resume() {
            synchronized (LOCK) {
                mIsPaused = false;
                LOCK.notifyAll();
            }
        }
    
        /**
         * 设置当前的接收任务不执行
         */
        public void stop() {
            mIsStop = true;
        }
    
        /**
         * 文件是否在接收中
         * @return
         */
        public boolean isRunning() {
            return !mIsFinish;
        }
    
        /**
         * 文件接收监听事件
         */
        public interface OnReceiveListener {
            void onStart();
            void onProgress(FileInfo fileInfo, long progress, long total);
            void onSuccess(FileInfo fileInfo);
            void onFailure(Throwable throwable, FileInfo fileInfo);
        }
    }

    发送端->发送所选文件:

    收到接收端发来的文件列表和“开始发送”指令后,发送端将会把所选文件逐个发送给接收端。

    /**
     * 初始化发送端服务,开始发送文件
     */
    private void initSenderServer() {
        mSenderServerRunnable = new SenderServerRunnable();
        new Thread(mSenderServerRunnable).start();
    }
    
    /**
     * 文件发送线程
     */
    private class SenderServerRunnable implements Runnable {
        private ServerSocket mServerSocket;
        @Override
        public void run() {
            try {
                //获取待发送的文件列表数据,按position索引排序
                List<Map.Entry<String, FileInfo>> fileInfoList = AppContext.getAppContext().getSendFileInfoMap();
                Collections.sort(fileInfoList, Consts.DEFAULT_COMPARATOR);
                mServerSocket = new ServerSocket(Consts.DEFAULT_FILE_RECEIVE_SERVER_PORT);
                //逐个文件进行发送
                for(final Map.Entry<String, FileInfo> fileInfoMap : fileInfoList) {
                    final FileInfo fileInfo = fileInfoMap.getValue();
                    Socket socket = mServerSocket.accept();
                    FileSender fileSender = new FileSender(socket, fileInfo);
                    fileSender.setOnSendListener(new FileSender.OnSendListener() {
                        @Override
                        public void onStart() {
                            mHandler.obtainMessage(MSG_SET_STATUS, "开始发送"+ FileUtils.getFileName(fileInfo.getFilePath())).sendToTarget();
                        }
                        @Override
                        public void onProgress(long progress, long total) {
                            //更新发送进度视图
                            int i_progress = (int) (progress * 100 / total);
                            LogUtils.e("正在发送:" + fileInfo.getFilePath() + "
    当前进度:" + i_progress);
                            Message msg = new Message();
                            msg.what = MSG_UPDATE_PROGRESS;
                            msg.arg1 = fileInfo.getPosition();
                            msg.arg2 = i_progress;
                            mHandler.sendMessage(msg);
                        }
                        @Override
                        public void onSuccess(FileInfo fileInfo) {
                            //发送成功
                            mHandler.obtainMessage(MSG_SET_STATUS, "文件:" + FileUtils.getFileName(fileInfo.getFilePath()) + "发送成功").sendToTarget();
                            fileInfo.setResult(FileInfo.FLAG_SUCCESS);
                            AppContext.getAppContext().updateSendFileInfo(fileInfo);
                            Message msg = new Message();
                            msg.what = MSG_UPDATE_PROGRESS;
                            msg.arg1 = fileInfo.getPosition();
                            msg.arg2 = 100;
                            mHandler.sendMessage(msg);
                        }
                        @Override
                        public void onFailure(Throwable throwable, FileInfo fileInfo) {
                            //发送失败
                            mHandler.obtainMessage(MSG_SET_STATUS, "文件:" + FileUtils.getFileName(fileInfo.getFilePath()) + "发送失败").sendToTarget();
                            fileInfo.setResult(FileInfo.FLAG_FAILURE);
                            AppContext.getAppContext().updateSendFileInfo(fileInfo);
                            Message msg = new Message();
                            msg.what = MSG_UPDATE_PROGRESS;
                            msg.arg1 = fileInfo.getPosition();
                            msg.arg2 = -1;
                            mHandler.sendMessage(msg);
                        }
                    });
                    //添加到线程池执行
                    mFileSenderList.add(fileSender);
                    AppContext.FILE_SENDER_EXECUTOR.execute(fileSender);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /**
         * 关闭Socket连接
         */
        public void closeServerSocket() {
            if(mServerSocket != null) {
                try {
                    mServerSocket.close();
                    mServerSocket = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    文件发送线程:

    public class FileSender extends BaseTransfer implements Runnable {
    
        /**
         * 待发送的文件数据
         */
        private FileInfo mFileInfo;
    
        /**
         * 传送文件的Socket输入输出流
         */
        private Socket mSocket;
        private OutputStream mOutputStream;
    
        /**
         * 用来控制线程暂停、恢复
         */
        private final Object LOCK = new Object();
        private boolean mIsPause;
    
        /**
         * 该线程是否执行完毕
         */
        private boolean mIsFinish;
    
        /**
         * 设置未执行线程的不执行标识
         */
        private boolean mIsStop;
    
        /**
         * 文件传送监听事件
         */
        private OnSendListener mOnSendListener;
    
    
        public FileSender(Socket socket, FileInfo fileInfo) {
            mSocket = socket;
            mFileInfo = fileInfo;
        }
    
        /**
         * 设置发送监听事件
         * @param onSendListener
         */
        public void setOnSendListener(OnSendListener onSendListener) {
            mOnSendListener = onSendListener;
        }
    
        @Override
        public void run() {
            if(mIsStop) {
                return;
            }
    
            //初始化
            try {
                if(mOnSendListener != null) {
                    mOnSendListener.onStart();
                }
                init();
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.i("FileSender init() ------->>> occur expection");
                if(mOnSendListener != null) {
                    mOnSendListener.onFailure(e, mFileInfo);
                }
            }
    
            //发送文件实体数据
            try {
                parseBody();
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.i("FileSender parseBody() ------->>> occur expection");
                if(mOnSendListener != null) {
                    mOnSendListener.onFailure(e, mFileInfo);
                }
            }
    
            //文件传输完毕
            try {
                finishTransfer();
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.i("FileSender finishTransfer() ------->>> occur expection");
                if(mOnSendListener != null) {
                    mOnSendListener.onFailure(e, mFileInfo);
                }
            }
        }
    
        @Override
        public void init() throws Exception {
            mSocket.setSoTimeout(30 * 1000);
            OutputStream os = mSocket.getOutputStream();
            mOutputStream = new BufferedOutputStream(os);
        }
    
        @Override
        public void parseBody() throws Exception {
            long fileSize = mFileInfo.getSize();
            File file = new File(mFileInfo.getFilePath());
            InputStream fis = new FileInputStream(file);
    
            int len = 0;
            long total = 0;
            byte[] bytes = new byte[BYTE_SIZE_DATA];
    
            long sTime = System.currentTimeMillis();
            long eTime = 0;
            while ((len = fis.read(bytes)) != -1) {
                synchronized (LOCK) {
                    if(mIsPause) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
    
                    //写入文件
                    mOutputStream.write(bytes, 0, len);
                    total += len;
    
                    //每隔200毫秒返回一次进度
                    eTime = System.currentTimeMillis();
                    if(eTime - sTime > 200) {
                        sTime = eTime;
                        if(mOnSendListener != null) {
                            mOnSendListener.onProgress(total, fileSize);
                        }
                    }
                }
            }
    
            //关闭Socket输入输出流
            mOutputStream.flush();
            mOutputStream.close();
            //文件发送成功
            if(mOnSendListener != null) {
                mOnSendListener.onSuccess(mFileInfo);
            }
            mIsFinish = true;
        }
    
        @Override
        public void finishTransfer() throws Exception {
            if(mOutputStream != null) {
                try {
                    mOutputStream.close();
                } catch (IOException e) {
    
                }
            }
    
            if(mSocket != null && mSocket.isConnected()) {
                try {
                    mSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 暂停发送线程
         */
        public void pause() {
            synchronized (LOCK) {
                mIsPause = true;
                LOCK.notifyAll();
            }
        }
    
        /**
         * 恢复发送线程
         */
        public void resume() {
            synchronized (LOCK) {
                mIsPause = false;
                LOCK.notifyAll();
            }
        }
    
        /**
         * 设置当前的发送任务不执行
         */
        public void stop() {
            mIsStop = true;
        }
    
        /**
         * 文件是否在发送中
         * @return
         */
        public boolean isRunning() {
            return !mIsFinish;
        }
    
        public interface OnSendListener {
            void onStart();
            void onProgress(long progress, long total);
            void onSuccess(FileInfo fileInfo);
            void onFailure(Throwable throwable, FileInfo fileInfo);
        }
    }

    因为懒,以上列出的只是部分核心代码,选择文件的功能也没去做,草草地在Activity中写死了几个文件去上传,具体代码可去Github下载运行,参见SendFilesActivity类,哈哈!

    Github源码:https://github.com/WhoIsAA/SocketDemo

    总结:

    貌似第一次在博客中贴那么长的代码,关于Socket的知识还要学许多许多,而我懂的也只不过是入门的皮毛,以上Demo参考了以下大神博文及资料:

  • 相关阅读:
    Python将文本生成二维码
    Python 发送邮件
    北京地铁月度消费总金额计算(Python版)
    将w3cplus网站中的文章页面提取并导出为pdf文档
    [开发笔记]-MarkDown语法
    linux多版本php安装+采坑指南
    浏览器跨域暴力解决
    php7使用xhprof测试php性能
    vscode使用xdebug断点调试php代码(无论win还是linux)
    ghostscript之pdf处理
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/7717412.html
Copyright © 2020-2023  润新知