Android 局域网udp图像传输

接收端:

public class ReceiveThread {

    private static final String TAG = "ReceiveThread";
    private final Handler mHandler;
    private final SurfaceHolder mHolder;
    private final String mIp;
    private final Activity mContext;
    private DatagramSocket mSocket;
    private Thread thread;
    /**
     * @description 图片数据接收完全的标识
     */
    public final static String PACKET_END = "#stream_end#";
    /**
     * @description
     */
    public ReceiveThread(Activity context,Handler handler, SurfaceHolder holder, String ip) {
        this.mHandler = handler;
        this.mHolder = holder;
        this.mIp = ip;
        this.mContext = context;
    }

    public void  startReceiveThread() {
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 错误信息,默认为"错误"
                String errMsg = "错误";

                // 用于接收图像数据的字节数组输出流
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                try {
                    // 结束标记的长度(结合日期时间)
                    int endlen = (PACKET_END + getLocalDateTime()).getBytes().length;
                    if (mSocket == null) {
                        // 创建一个无绑定状态的DatagramSocket(UDP套接字)
                        mSocket = new DatagramSocket(null);
                        // 允许地址重用  不用这个会很卡  因为一次用完不是直接清空的  会等待一会才清空socket
                        mSocket.setReuseAddress(true);
                        // 绑定本地地址和端口号 监听这个ip和端口的数据
                        mSocket.bind(new InetSocketAddress(Constants.FIND_CAMERA_PORT));
                        // 设置套接字阻塞超时时间为3秒
                        mSocket.setSoTimeout(3000);
                        MlogUtil.e(TAG, "发送请求相机连接..... " +endlen);
                        // 通过套接字发送请求,并将接收到的数据写入字节数组输出流baos中
                        sendRet(mSocket, baos);
                        // 重置字节数组输出流
                        baos.reset();
                        MlogUtil.e(TAG, "等待接收... "+endlen);
                        // 拿数据  循环接收数据,直到套接字为空
                        while (mSocket != null) {
                            // 接收数据的缓冲区
                            byte[] buffer = new byte[1024];
                            // 创建用于接收数据的数据报包
                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                            try {
                                // 接收数据 // 通过套接字接收数据,并将接收到的数据填充到数据报包中
                                mSocket.receive(packet);
                            } catch (SocketTimeoutException te) {
                                te.printStackTrace();
                                MlogUtil.e(TAG, "连接超时... "+endlen);
                                // 再次发送请求
                                sendRet(mSocket, baos);
                                baos.reset();
                                // 重新接收
                                continue;
                            }
                            // 判断一帧图片是否接收完成
                            if (packet.getLength() == endlen) {
                                // 获取接收到的数据并转换为字符串  这个数据是获取时间
                                String end = new String(packet.getData(), 0, endlen);
                                // 判断结束标记是否匹配
                                if (end.startsWith(PACKET_END)) {
                                    // 拆分结束标记
                                    String[] args = end.split("#");
                                    String time = "--:--";
                                    if (args.length == 3) {
                                        time = args[2]; // 获取时间信息
                                    }
                                    updateViewDisplay(baos, time); // 更新图像视图的显示
                                    Thread.sleep(150); // 等待一段时间,准备接收下一帧图像数据
                                    baos.flush(); // 刷新字节数组输出流
                                    // 再次发送请求
                                    sendRet(mSocket, baos);
                                    baos.reset(); // 重置字节数组输出流
                                    MlogUtil.e(TAG, "发送请求相机连接..... " +       getLocalDateTime());
                                    continue;
                                }
                            }
                            baos.write(packet.getData(), 0, packet.getLength()); // 将接收到的数据写入字节数组输出流
                        }
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                    errMsg = e.getMessage(); // 获取Socket异常的错误信息
                } catch (IOException e) {
                    e.printStackTrace();
                    errMsg = e.getMessage(); // 获取IO异常的错误信息
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    errMsg = e.getMessage(); // 获取中断异常的错误信息
                } catch (Exception e) {
                    e.printStackTrace();
                    errMsg = e.getMessage(); // 获取其他异常的错误信息
                } finally {
                    try {
                        baos.close(); // 关闭字节数组输出流
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    cancelThread(errMsg); // 取消线程并传递错误信息
                }
            }
        });
        thread.start(); // 启动线程开始接收图像数据
    }




    public void cancelThread(String errMsg) {
        if(mSocket != null) {
            if(mSocket.isConnected()) mSocket.disconnect();
            if(!mSocket.isClosed()) this.mSocket.close();
            mSocket = null;
        }
        if(thread != null) {
            thread.interrupt();
            thread = null;
        }
        if (errMsg!=null && errMsg.isEmpty()!=true) {
            MlogUtil.i(">>>BaseThread", "cancelThread "+ errMsg);
        }
    }

    /**
     * @description 接收图像后 显示图像
     */
    private void updateViewDisplay(final ByteArrayOutputStream baos, final String time) {
        byte[] data = baos.toByteArray();
        //把数据转成bitmap
        final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        if(bitmap == null) {
            Log.i(TAG, "数据为空.....");
            return;
        }

       mContext.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Canvas canvas = mHolder.lockCanvas(null);
                if (canvas == null) {
                    return;
                }
                Bitmap bitmap2 = bitmap.createScaledBitmap(bitmap, canvas.getWidth(), canvas.getHeight(), true);
               /* bitmap2= rotateBitmap(bitmap2, 90);
                canvas.drawBitmap(bitmap2, (canvas.getWidth()-canvas.getHeight())/2, 0, null);*/
                canvas.drawBitmap(bitmap2, 0, 0, null);
                Paint p = new Paint();
                p.setTextSize(24);
                p.setColor(Color.WHITE);
                /*canvas.drawText(time, 20, 30, p);*/
                mHolder.unlockCanvasAndPost(canvas);

//              baos.flush();
                baos.reset();
            }

        });
    }

    /**
     * @description 发送数据
     */
    private void sendRet(DatagramSocket dSocket, ByteArrayOutputStream baos) throws IOException, Exception {
        if (dSocket==null) {
            throw new Exception("socket is null");
        }
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(new Integer(Constants.RET_CAMERA_IP));
        byte[] data = baos.toByteArray();
        InetAddress address = InetAddress.getByName(mIp);
        DatagramPacket pack = new DatagramPacket(data, data.length, address, Constants.FIND_CAMERA_PORT);
        dSocket.send(pack);
        oos.flush();
        baos.flush();
    }

    /**
     * @description 获取当前本地时间
     */
    public String getLocalDateTime() {
        return (new Date()).toLocaleString();
    }
}

发送端:

public class SenderThread {
    private static final String TAG = "SenderThread";  // 日志标签
    private final Activity mContext;  // 上下文对象
    private int cameraCount = 0;  // 摄像头数量
    public boolean isSending = false;  // 是否正在发送图像
    private DeviceInfo info;  // 设备信息
    private YuvImage image = null;  // 图像对象
    private Thread thread;  // 线程对象
    private DatagramSocket mSocket;  // 数据报套接字

    public SenderThread(Activity context, DeviceInfo info, Handler handler) {
        this.info = info;
        this.mContext = context;
    }

    public String getDeviceName() {
        return info.getName();  // 获取设备名称
    }

    public String getLocalIp() {
        return info.getLocalIp();  // 获取本地IP地址
    }

    public int getCameraCount() {
        return cameraCount;  // 获取摄像头数量
    }

    public void setCameraCount(int cameraCount) {
        this.cameraCount = cameraCount;  // 设置摄像头数量
    }

    public void canFind(boolean isFind) {
        if (isFind == true && thread == null) {
            this.thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    String errMsg = "未知错误";
                    try {
                        mSocket = new DatagramSocket(null);
                        mSocket.setReuseAddress(true);
                        // 监听本地端口
                        mSocket.bind(new InetSocketAddress(Constants.FIND_CAMERA_PORT));
                        do {
                            MlogUtil.d(TAG, "等待中... ");
                            DatagramPacket pack = new DatagramPacket(new byte[1028], 1028);
                            try {
                                mSocket.receive(pack);  // 接收数据报包
                            } catch (SocketTimeoutException e) {
                                e.printStackTrace();
                                continue;
                            } catch (Exception e) {
                                e.printStackTrace();
                                throw e;
                            }
                            SocketAddress sendAddress = pack.getSocketAddress();  // 获取发送方的地址
                            ObjectInputStream ois = null;
                            Integer code = 0;
                            try {
                                ois = new ObjectInputStream(new ByteArrayInputStream(pack.getData()));
                                code = (Integer) ois.readObject();  // 从数据包中读取操作码
                            } catch (StreamCorruptedException e) {
                                e.printStackTrace();
                                MlogUtil.e(TAG, "连接被断开 " + e);
                            } catch (Exception e) {
                                e.printStackTrace();
                                MlogUtil.e(TAG, "连接被断开 " + e);
                            } finally {
                                if (ois != null) {
                                    try {
                                        ois.close();
                                    } catch (Exception ex) {
                                        throw ex;
                                    }
                                }
                            }
                            if (mSocket == null) {
                                // 如果mSocket为null,跳出循环
                                break;
                            }
                            switch (code) {
                                case Constants.RET_CAMERA_IP:  // 操作码为RET_CAMERA_IP
                                {
                                    isSending = true;
                                    String sendTime = getLocalDateTime();  // 获取当前本地时间
                                    sendImage(image, sendAddress, sendTime);  // 发送图像
                                    MlogUtil.e(TAG, "等待中...已发送 ");
                                    isSending = false;
                                }
                                break;
                                default:
                            }
                        } while (!thread.isInterrupted() && mSocket != null);
                    } catch (Exception e) {
                        e.printStackTrace();
                        errMsg = e.getMessage();
                    } finally {
                        cancelThread(errMsg);  // 取消线程
                    }
                }
            });
            this.thread.start();  // 启动线程
        } else {
            cancelThread("");  // 取消线程
        }
    }

    public void cancelThread(String errMsg) {
        if (mSocket != null) {
            if (mSocket.isConnected()) mSocket.disconnect();
            if (!mSocket.isClosed()) this.mSocket.close();
            mSocket = null;
        }
        if (thread != null) {
            thread.interrupt();
            thread = null;
        }
        if (errMsg != null && errMsg.isEmpty() != true) {
            MlogUtil.i(">>>BaseThread", "cancelThread " + errMsg);
        }
    }

    /**
     * @description 获取当前本地时间
     */
    public String getLocalDateTime() {
        return (new Date()).toLocaleString();  // 返回当前本地时间的字符串表示
    }

    private synchronized void sendImage(YuvImage image, SocketAddress sendAddress, String sendTime) throws Exception {
        if (mSocket == null) {
            throw new异常("Socket为空");  // 抛出异常,表示套接字为空
        }
        if (image == null) {
            throw new Exception("获取图片失败");  // 抛出异常,表示获取图像失败
        }
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        try {
            image.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 80, outStream);  // 将图像压缩为JPEG格式并写入输出流
            byte[] buffer = new byte[1024];
            ByteArrayInputStream bais = new ByteArrayInputStream(outStream.toByteArray());

            outStream.flush();
            outStream.close();

            try {
                int len;
                DatagramPacket pack;
                while ((len = bais.read(buffer, 0, buffer.length)) != -1) {
                    pack = new DatagramPacket(buffer, len, sendAddress);  // 创建数据报包,并指定发送方的地址
                    mSocket.send(pack);  // 发送数据报包
                }
                byte[] end = (PACKET_END + sendTime).getBytes();  // 组装结束标志和发送时间的字节数组
                pack = new DatagramPacket(end, end.length, sendAddress);  // 创建包含结束标志和发送时间的数据报包
                mSocket.send(pack);  // 发送数据报包
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                bais.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            outStream.close();
            image = null;
        }
    }

    public void setSendCameraImage(YuvImage image) {
        if (isSending()) {
            return;
        }
        this.image = image;  // 设置要发送的图像
    }

    public boolean isSending() {
        return isSending;  // 返回是否正在发送图像
    }
}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
这两天下了一个使用UDP传输目录内文件的程序,发出来给大家一起看看,共同进步。有问题请指教。 由于udp丢包比较厉害,因此使用了自定义的内部协议,进行双方的确认。 程序跑起来后,看网络状况,有时候会一卡一卡的。 以下是程序说明: * 本程序集成了数据导出端(服务器端)和数据导入端(客户端),使用UDP进行文件传递 * 服务器端的文件来源目录,见Tools中SOURCEPATH的设置 * 客户端的文件保存目录,见Tools中DESTINATIONPATH的设置,可以根据自己需要进行调整 * * 由于UDP存在丢包问题,因此Server和Client的通讯需要来回包进行确认,协议包头如下: * 1. "55 aa 99 01",表示客户端发起广播请求,请求服务器响应 * 2. "55 aa 99 02 + 服务器的设备名称",表示服务器接收到广播后,响应客户端请求,把此包指定IP发送客户端(此指定IP地址可以UDP广播信息包中获取) * 3. "55 aa 99 03",表示客户端接收到服务器的响应,接着向服务器指定IP请求:需要传递的文件总数目和文件总容量(单位为KB) * 4. "55 aa 99 04 + 4字节文件总数目和4字节的文件总容量",表示服务器接收到客户端的0x03请求,统计SOURCEPATH中的所有文件数目和文件总容量,发送指定IP地址的客户端 * 5. "55 aa 99 05",客户端接收到文件总数目和文件总容量,请求服务器发送文件具体内容 * 6. "55 aa 99 10 + 文件名称",服务器发送文件名称 * 7. "55 aa 99 11",客户端响应,表示接收到服务器发送的0x10包 * 8. "55 aa 99 12+文件内容",服务器端发送具体文件内容 * 9. "55 aa 99 13",客户端响应,表示接收到服务器发送的0x12包 * 10."55 aa 99 14",服务端高速客户端发送完毕 * * 注意:服务器发送0x10包后,收到客户端的0x11响应包,将把文件具体内容拆分成N个0x12包,每个包的大小见Tools.byteSize的设置,目前设置为10K, * 服务器没收到一个0x13响应包,才能继续发下一个0x12包,已放置UDP的丢包,另外每个0x12包最多发送10次而无0x13包的响应,则发送进程结束,界面提示 * * 本程序已经封装好,调用见TransportFilesActivity.java文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码划云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值