android开发之socket快传文件以及消息返回

应用场景:

两台android机器:一台自建wifi热点,另一台搜索到,连接该wifi热点。之后再通过socket传递消息,文件等,当服务器端接收到消息之后会返回对应的应答消息;

注意点:接收到消息之后不能直接拔dos关闭了,这样会造成socket关闭;

socket关闭只需要一端关闭即可;

这里发送没有使用缓存发送,每一次writeInt的时候度会直接发送出去,,因此可以不需要flush();

每一次的发送端的 write 和 接收端的read  必须对应,否则异常出错!

服务器端socket代码:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import com.carspeak.client.entity.NetTransEntity;
import com.carspeak.terminal.utils.AppUtils;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * 服务器端完整socket接收代码(接收客户端的图片)
 * @author huqiang
 *
 */
public class SocketService implements Runnable{
    private static final String TAG = "SocketService";

    private ServerSocket serviceSocket;
    private boolean SCAN_FLAG = false; // 接收扫描标识
    private boolean REV_FLAG = false; // 接收标识

    private static String mfilePath = null; // 存放接收文件的路径

    private static Context mContext;
    private static SocketService instance; // 唯一实例
    private Thread mThread;
    private boolean IS_THREAD_STOP = false; // 是否线程开始标志

    private static Handler mHandler;
    public SocketService()
    {
        try
        {
            serviceSocket = new ServerSocket(4700);
            Log.d(TAG, "建立监听服务器ServerSocket成功");
        } catch (IOException e)
        {
            Log.d(TAG, "建立监听服务器ServerSocket失败");
            e.printStackTrace();
        }
        mThread = new Thread(this);
    }

    /**
     * <p>
     * 获取TcpService实例
     * <p>
     * 单例模式,返回唯一实例
     */
    public static SocketService getInstance(Context context,Handler handler,String filepath)
    {
        mContext = context;
        mHandler = handler;
        mfilePath = filepath;
        if (instance == null)
        {
            instance = new SocketService();
        }
        return instance;
    }

    public void setSavePath(String fileSavePath)
    {
        Log.d(TAG, "设置存储路径成功,路径为" + fileSavePath);
        this.mfilePath = fileSavePath;
        // REV_FLAG=true;
    }

    public SocketService(Context context)
    {
        this();
        mContext = context;
    }

    private void scan_recv()
    {
        try
        {
            Socket socket = serviceSocket.accept(); // 接收UDP数据报
//            socket.setSoTimeout(10*1000); // 设置掉线时间
            Log.d(TAG, "客户端连接成功");
            //通过子线程来循环读取socket的消息
            ListenClientSocket ls = new ListenClientSocket(socket);
            ls.start();

        } catch (IOException e)
        {
            e.printStackTrace();
            Log.d(TAG, "客户端连接失败");
            SCAN_FLAG = false;
        }
    }

    @Override
    public void run()
    {
        Log.d(TAG, "TCP_Service线程开启");
        while (!IS_THREAD_STOP)
        {
            if (SCAN_FLAG)
            {
                scan_recv();
            }
        }
    }

    public void release()
    {
        if (null != serviceSocket && !serviceSocket.isClosed())
            try
            {
                serviceSocket.close();
                serviceSocket = null;
                Log.d(TAG, "关闭socket成功");
            } catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        while (SCAN_FLAG == true)
            ;// 直到SCAN_FLAG为false的时候退出循环
        SCAN_FLAG = false;
        IS_THREAD_STOP = true;
    }

    public void startReceive()
    {
        SCAN_FLAG = true; // 使能扫描接收标识
        if (!mThread.isAlive())
            mThread.start(); // 开启线程
    }

    public void stopReceive()
    {
        while (SCAN_FLAG == true)
            ;
        SCAN_FLAG = false; // 失能扫描接收标识
    }

    /**
     * 监听客户端发送的消息
     * @author huqiang
     *
     */
    class ListenClientSocket implements Runnable
    {
        private boolean IsListening = false;
        private Socket clientSocket;
        private Thread thread;
        public ListenClientSocket(Socket s)
        {
            this.clientSocket = s;
            this.thread = new Thread(this);
        }
        @Override
        public void run() {
            while(clientSocket!=null&&!clientSocket.isClosed()&&IsListening)
            {
                readMessage(this.clientSocket);
            }
        }
        public void start()
        {
            IsListening = true;
            thread.start();
        }
        public void release()
        {
            IsListening = false;
            if(!clientSocket.isClosed())
            {
                try {
                    clientSocket.close();
                    clientSocket = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
        public void readMessage(Socket socket)
        {
            NetTransEntity entity = new NetTransEntity();
            try
            {
                //分批次接收socket
                InputStream in = socket.getInputStream();
                DataInputStream dis = new DataInputStream(in);
                entity.RequestCode = dis.readInt();
                entity.params = dis.readUTF();
                switch(entity.RequestCode)
                {
                case 100://
                case 500://请求升级
                    Log.v(TAG, "请求图片");
                    entity.fileLength = dis.readLong();
                    Log.v(TAG, "接收到文件长度:"+entity.fileLength);
                    if(entity.RequestCode==100)
                    {
                        entity.params = "screen.png";
                    }
                    FileOutputStream fos =new FileOutputStream(new File(mfilePath,entity.params));

                    byte[] sendBytes =new byte[1024];
                    int transLen =0;
                    Log.v(TAG, "----开始接收文件<" + entity.params +">,文件大小为<" + entity.fileLength +">----");
                    while(true){
                        int read =0;
                        read = dis.read(sendBytes);
                        Log.v(TAG, "read="+read);
                        if(read == -1)
                            break;
                        transLen += read;
                        Log.v(TAG, "接收文件进度" +100 * transLen/entity.fileLength +"%...");
                        fos.write(sendBytes,0, read);
                        fos.flush();
                    }
                    Log.v(TAG, "----接收文件<" + entity.params +">成功-------1");
                    entity.filePath = mfilePath + entity.params; //将下载下来的文件名字赋值给entity.filePath
                    Log.v(TAG, "----接收文件<" + entity.params +">成功-------2");
                    break;
                case 101://请求终止投影
                case 102://请求待机(超时自动待机)
                case 103://请求高速自动待机
                    break;
                case 501://请求固件版本信息
                    break;
                default://其他请求
                    break;
                }
//                dis.close();//会造成关闭socket
//                socket.shutdownInput();
                //发送返回消息
                Log.v(TAG, "----开始发送返回消息-------");
                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                Log.v(TAG, "发送返回消息 1");
                dos.writeInt(200);
                dos.flush();
                Log.v(TAG, "发送返回消息 2");
                switch(entity.RequestCode)
                {
                case 501: //请求固件版本信息
                    //返回版本号
                    Log.v(TAG, "发送返回消息-返回版本号");
                    String versionCode = AppUtils.getVersionCode(mContext)+"";
                    dos.writeUTF(versionCode);
                    dos.flush();
                    break;
                case 100:
                case 101:
                case 102:
                case 103:
                default:
                    dos.writeUTF("success");
                    dos.flush();
                    break;
                }
//                dos.close();
//                socket.close();
            }
            catch(Exception ex)
            {
                Log.e(TAG, "Exception:"+ex.getMessage());

            }
            finally
            {
                Message msg = new Message();
                msg.what = 1;  //说明有socket消息
                msg.obj = entity;
                mHandler.sendMessage(msg);
                release();
            }
        }

    }
}

对应的客户端socket代码:

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

import com.carspeak.client.config.SPConfig;
import com.carspeak.client.entity.CusMessage;
import com.carspeak.client.entity.NetTransEntity;
import com.carspeak.client.util.SPUtils;
import com.carspeak.client.util.WifiUtils;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * 连接到终端设备
 *
 * @author huqiang
 *
 */
public class ClientService implements Runnable{

//    AppConfig app;
//    String filePath;
    NetTransEntity mEntity;
    String ServerIP;
    WifiUtils mWifiUtils;
    private final int SERVER_PORT = 4700;  //服务器端口
    private final int TIME_OUT = 6*1000;  //SOCKET超时时间
    private Context mContext;
    private Handler mHandler;
    public ClientService(Context context,Handler handler,NetTransEntity entity) {
//        app = (AppConfig) context.getApplicationContext();
//        this.filePath = filePath;
        this.mEntity = entity;
        mContext = context;
        this.mHandler = handler;
        mWifiUtils = WifiUtils.getInstance(context);
        ServerIP = (mWifiUtils.getServerIPAddress()==null)?"192.168.43.1":mWifiUtils.getServerIPAddress();
    }

    /**
     * 向终端发送数据
     * @param path
     * @throws IOException
     * @throws Exception
     */
    private void sendMessage(NetTransEntity entity) throws IOException, Exception {
        // 1.连接服务器
        Socket socket = new Socket();
        Log.v("ConnectTerminalServer", "开始准备socket连接,ServerIP=" + ServerIP+ ";SERVER_PORT=" + SERVER_PORT + ";TIME_OUT=" + TIME_OUT+";当前连接状态:getSSID"+mWifiUtils.getSSID());
        socket.connect(new InetSocketAddress(ServerIP, SERVER_PORT), TIME_OUT);

        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

        Log.v("ClientService", "开始写入请求码:"+entity.RequestCode);
        dos.writeInt(entity.RequestCode); //写入请求码
//        dos.flush();
        switch(entity.RequestCode)
        {
        case 100://请求图片
        case 500://请求固件升级,发送apk文件
            File file = new File(entity.filePath);
            Log.v("ClientService", "开始写入文件名 file.getName()="+file.getName());
            dos.writeUTF(file.getName());  //第二次写入参数信息
            dos.flush();
            Log.v("ClientService", "开始写入文件长度 file.length()="+file.length());
            dos.writeLong(file.length());  //第三次写入文件的长度
            dos.flush();
            //第四次写入文件
            //传输文件
            FileInputStream fis =new FileInputStream(file);
            byte[] sendBytes =new byte[1024];
            int length =0;
            while((length = fis.read(sendBytes,0, sendBytes.length)) >0){
                dos.write(sendBytes,0, length);
                dos.flush();
            }
            fis.close();
            break;
        case 101://请求终止
        case 102://请求待机(超时自动待机)
        case 103://请求高速自动待机
            Log.v("ClientService", "开始写入参数="+(entity.params==null?"params is null":entity.params));
            dos.writeUTF(entity.params==null?" ":entity.params);
            dos.flush();
            break;
        case 501://请求固件版本信息
            dos.writeUTF(" ");
            dos.flush();
            break;
        default://其他请求
            break;
        }

        Log.v("ClientService", "即将关闭dos");
//        dos.close();  //注意这里dos关闭会造成socket关闭,应该使用shutdownOutput
        socket.shutdownOutput();
        Log.v("ClientService", "shutdownOutput");
        /****************接收返回参数******************/
        DataInputStream dis = new DataInputStream(socket.getInputStream());
        CusMessage cms = new CusMessage();
        Log.v("ClientService", "接收消息 1   dis="+dis);
        cms.code = dis.readInt();
        Log.v("ClientService", "接收消息 2");
        cms.message = dis.readUTF();
        Log.v("ClientService", "接收到终端返回消息,code="+cms.code + ";message="+cms.message);
        switch(entity.RequestCode)
        {
        case 501: //请求固件版本信息
            if(cms.code==200)
            {
                int terminalVersionCode = Integer.parseInt(cms.message);
                SPUtils.put(mContext, SPConfig.TerminalVersionCode, -1);
                Message msg = new Message();
                msg.what = 501;
                msg.obj = terminalVersionCode;
                mHandler.sendMessage(msg);
                Log.v("ClientService", "接收到终端的消息返回,版本号:"+terminalVersionCode);
            }
            break;
        case 100:
        case 101:
        case 102:
        case 103:
        default:
            break;
        }
        dos.close();
        dis.close();
        socket.close();
    }

    @Override
    public void run() {
        try
        {
            sendMessage(this.mEntity);
        }
        catch(IOException ex){
            Log.v("ConnectTerminalServer", ex.getMessage());
        }
        catch (Exception ex) {
            Log.v("ConnectTerminalServer", ex.getMessage());
        }
    }

}
时间: 2024-11-10 04:30:52

android开发之socket快传文件以及消息返回的相关文章

【转】Android 开发之旅:深入分析布局文件&amp;又是“Hello World!”

引言 上篇可以说是一个分水岭,它标志着我们从Android应用程序理论进入实践,我们拿起手术刀对默认的“Hello World!”程序进行了3个手术,我们清楚了“Hello world!”是如何实现显示在屏幕上的,而且我们知道不仅可以根据布局文件main.xml来初始化屏幕,还可编程地进行.以后基本我们都会以实践的方式来深入Android开发.我们这次深入分析Android应用程序的布局文件,主要内容如下: 1.用户界面及视图层次 2.Android中布局定义方法 3.编写XML布局文件及加载X

Android 开发之旅:深入分析布局文件&amp;又是“Hello World!”

http://www.cnblogs.com/skynet/archive/2010/05/20/1740277.html 引言 上篇可以说是一个分水岭,它标志着我们从Android应用程序理论进入实践,我们拿起手术刀对默认的“Hello World!”程序进行了3个手术,我们清楚了“Hello world!”是如何实现显示在屏幕上的,而且我们知道不仅可以根据布局文件main.xml来初始化屏幕,还可编程地进行.以后基本我们都会以实践的方式来深入Android开发.我们这次深入分析Android

Android开发之InstanceState详解

Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceState(),并且在介绍这两个方法之后,再分别来实现使用InstanceState保存和恢复数据功能.Android实现屏幕旋转异步下载效果这样两个示例. 首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Andr

Android开发之IPC进程间通信-AIDL介绍及实例解析

一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信:   2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身:linux除了支持Unix早期

Android开发之InstanceState详解(转)---利用其保存Activity状态

Android开发之InstanceState详解 本文介绍Android中关于Activity的两个神秘方法:onSaveInstanceState() 和 onRestoreInstanceState(),并且在介绍这两个方法之后,再分别来实现使用InstanceState保存和恢复数据功能.Android实现屏幕旋转异步下载效果这样两个示例. 首先来介绍onSaveInstanceState() 和 onRestoreInstanceState() .关于这两个方法,一些朋友可能在Andr

Android开发之Tween(补间动画)完全解析(下)

欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/51980734 在上一篇文章中,我们详细讨论了Tween动画的xml的实现以及interpolator的使用,相信通过上篇文章大家对Tween动画的xml属性的配置会有一个详细的理解,当然这篇文章也是承接上篇文章,所以强烈建议先阅读上篇文章:Android开发之Tween(补间动画)完全解析(上),这篇文章将从代码的角度实现上篇文章的效果.如有疑问请留言,如有谬误欢迎批评指正. T

Android开发之Fragment详解

Android开发之Fragment学习 1.简介: Fragment是Android 3.0引入的新API. Fragment代表了 Activity的子模块,因此可以把Fragment理解成Activity片段.Fragment用于自己的生命周期,也可以接受它自己的输入事件. Fragment必须被"嵌入" Activity中使用,因此虽然Fragment也拥有自己的生命周期,但Fragment的生命周期会受它所在的Activity的生命周期的控制.例如,当Activity暂停时,

Android开发之Handler的使用方法(源代码分享)

Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI.. 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发.比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作.  如果此时需要一个耗时的操作,例如: 联网读取数据或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,,会收到An

【转】Android 开发之旅:view的几种布局方式及实践

引言 通过前面两篇: Android 开发之旅:又见Hello World! Android 开发之旅:深入分析布局文件&又是“Hello World!” 我们对Android应用程序运行原理及布局文件可谓有了比较深刻的认识和理解,并且用“Hello World!”程序来实践证明了.在继续深入Android开发之旅之前,有必要解决前两篇中没有介绍的遗留问题:View的几种布局显示方法,以后就不会在针对布局方面做过多的介绍.View的布局显示方式有下面几种:线性布局(Linear Layout).