zoukankan      html  css  js  c++  java
  • 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参考了以下大神博文及资料:

  • 相关阅读:
    重新想象 Windows 8 Store Apps (15) 控件 UI: 字体继承, Style, ControlTemplate, SystemResource, VisualState, VisualStateManager
    重新想象 Windows 8 Store Apps (12) 控件之 GridView 特性: 拖动项, 项尺寸可变, 分组显示
    返璞归真 asp.net mvc (10) asp.net mvc 4.0 新特性之 Web API
    与众不同 windows phone (29) Communication(通信)之与 OData 服务通信
    与众不同 windows phone (33) Communication(通信)之源特定组播 SSM(Source Specific Multicast)
    与众不同 windows phone (27) Feature(特性)之搜索的可扩展性, 程序的生命周期和页面的生命周期, 页面导航, 系统状态栏
    与众不同 windows phone (30) Communication(通信)之基于 Socket TCP 开发一个多人聊天室
    返璞归真 asp.net mvc (12) asp.net mvc 4.0 新特性之移动特性
    重新想象 Windows 8 Store Apps (2) 控件之按钮控件: Button, HyperlinkButton, RepeatButton, ToggleButton, RadioButton, CheckBox, ToggleSwitch
    重新想象 Windows 8 Store Apps (10) 控件之 ScrollViewer 特性: Chaining, Rail, Inertia, Snap, Zoom
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/7717412.html
Copyright © 2011-2022 走看看