是什么导致我的图像在使用 FTP 通过蓝牙 RFCOMM 传输期间损坏?

2023-11-30

我正在开发两个独立的应用程序,用于使用 Obex 文件传输协议通过蓝牙 RFCOMM 进行数据传输。一方面,在 PC 上运行的 Windows C# 控制台应用程序侦听传入的蓝牙连接,并在客户端发出请求时发送图像。另一方面,移动设备上运行的 Android 应用程序会扫描附近的蓝牙设备,查找服务器并接收图像。

在大多数情况下,一切正常,图像传输没有问题。有时 - 不是很常见,我仍然不知道如何重现错误 - 图像在传输过程中被损坏,因为从 Android 应用程序接收到的一些字节与原始缓冲区不匹配(我计算了接收缓冲区并与原始缓冲区进行比较以检查图像是否已发送成功)。

这是一个例子:

Original

Original,

Received

Received

这种“故障”图像只是一个例子,每次出现问题时,接收到的图像都会有不同的“故障效果”。

我试图解决这个问题的几件事:

  • 更改 UUID,但 OOP UUID 和自定义 UUID 似乎都不起作用,因为出现了完全相同的问题。
  • 我运行客户端应用程序的智能手机(小米红米 Note 8T)的内部存储可用空间几乎为零,因此我绝望地尝试释放一些内存,看看是否由于某种原因导致了错误(是的,它没有没有多大意义,但值得一提)。起初它有效,我认为以某种方式解决了问题,但随后错误又像以前一样出现了。
  • 使用ACK系统来控制从服务器发送到客户端的每个数据子数组,类似于:PC发送第一个数据子数组,然后等待智能手机发送ACK以确认子数组的接收,并且只有在此之后,它才会继续发送下一个数据子数组,依此类推,直到缓冲区末尾。不用说,这个选项都不起作用(同样的错误和损坏的数据)。
  • 我还尝试查看尝试连接到我的智能手机的其他设备是否会导致该问题,但事实并非如此。

CODE

服务器端

这是我在中的监听器的实现C# 控制台应用程序在 Windows 10 上运行。我拿了该服务器示例作为参考。

// Initialize the provider for the hosted RFCOMM service
_provider = await RfcommServiceProvider.CreateAsync(
        RfcommServiceId.ObexFileTransfer);       // Use Obex FTP protocol
        // UUID is 00001106-0000-1000-8000-00805F9B34FB


// Create a listener for this service and start listening
StreamSocketListener listener = new StreamSocketListener();
listener.ConnectionReceived += OnConnectionReceivedAsync;
await listener.BindServiceNameAsync(
    _provider.ServiceId.AsString(),
    SocketProtectionLevel
        .BluetoothEncryptionAllowNullAuthentication);

// Set the SDP attributes and start advertising
InitializeServiceSdpAttributes(_provider);
_provider.StartAdvertising(listener);

InitializeServiceSdpAttributes功能:

const uint SERVICE_VERSION_ATTRIBUTE_ID = 0x0300;
const byte SERVICE_VERSION_ATTRIBUTE_TYPE = 0x0A;   // UINT32
const uint SERVICE_VERSION = 200;

void InitializeServiceSdpAttributes(RfcommServiceProvider provider)
{
    Windows.Storage.Streams.DataWriter writer = new Windows.Storage.Streams.DataWriter();

    // First write the attribute type
    writer.WriteByte(SERVICE_VERSION_ATTRIBUTE_TYPE);
    // Then write the data
    writer.WriteUInt32(MINIMUM_SERVICE_VERSION);

    IBuffer data = writer.DetachBuffer();
    provider.SdpRawAttributes.Add(SERVICE_VERSION_ATTRIBUTE_ID, data);
}

每当检测到新的连接尝试时,OnConnectionReceivedAsync函数停止广告,处理监听器并创建一个新的StreamSocket目的。此时,我设置输入和输出流,将图像转换为字节数组,并通过套接字将缓冲区长度发送到远程设备。一旦 Android 应用程序收到缓冲区的长度,它就会发送一个 ACK​​,这意味着它已准备好接收实际数据。

// Create input and output stream
DataWriter writer = new DataWriter(_socket.OutputStream);

// Convert image to array of bytes
byte[] imageByteArray;
using (var inputStream = await file.OpenSequentialReadAsync())
{
    var readStream = inputStream.AsStreamForRead();

    imageByteArray = new byte[readStream.Length];
    await readStream.ReadAsync(imageByteArray, 0, imageByteArray.Length);
}

// Write length of data
writer.WriteBytes(intToByteArray(imageByteArray.Length));
await writer.StoreAsync();

// Wait for ACK ...

最后,我发送图像:

// Write bytes and send
writer.WriteBytes(imageByteArray);
await writer.StoreAsync();

// Wait for ACK ...

发送图像后,应用程序会在收到所有数据后收到来自远程设备的 ACK,然后关闭连接。

客户端

首先,安卓应用程序创建一个BluetoothSocket使用从服务器应用程序指定的相同 UUID 的对象:

// Scan devices and find the remote Server by specifying the target MAC address
// ...

// targetDevice is the Server device 
BluetoothSocket socket = targetDevice.createInsecureRfcommSocketToServiceRecord(
       UUID.fromString("00001106-0000-1000-8000-00805F9B34FB")         // FTP
);

// Connect the server
socket.connect();

最后,它从套接字读取传入的数据InputStream。首先,它读取传入缓冲区的长度并发送 ACK 以确认已准备好接收图像。然后等待每个子数组,直到所有缓冲区都完成。此时,它发送最终的 ACK 并关闭连接。

// Get input stream
InputStream inputStream = socket.getInputStream();

// Buffer that contains the incoming data
byte[] buffer = null;
// The numOfBytes is the expected length of the buffer
int numOfBytes = 0;
// Index of the sub array within the complete buffer
int index = 0;
// flag is true if the receiver is computing the number of bytes that it has to receive
// flag is false if the receiver is actually reading the image sub arrays from the stream
int flag = true;

while(true){
    // Estimate number of incoming bytes
    if(flag){
        try{
            // inputStream.available() estimates the number of bytes that can be read
            byte[] temp = new byte[inputStream.available()];

            // Read the incoming data and store it in byte array temp (returns > 0 if successful)
            if(inputStream.read(temp) > 0){
                // Get length of expected data as array and parse it to an Integer
                String lengthString = new String(temp, StandardCharsets.UTF_8);
                numOfBytes = Integer.parseInt(lengthString);
                // Create buffer
                buffer = new byte[numOfBytes];
                // Set the flag to false (turn on read image mode)
                flag = false;

                // Send ACK
            }
        }
        catch (IOException e){
            // ...
        }
    }
    // Read image sub arrays
    else {
        try{
            byte[] data = new byte[inputStream.available()];
            // Read sub array and store it
            int numbers = inputStream.read(data);

            if(numbers <= 0 && index < numOfBytes)
                continue;

            // Copy sub array into the full image byte array
            System.arraycopy(data, 0, buffer, index, numbers);
            // Update index
            index = index + numbers;

            // Reached the end of the buffer (received all the data)
            if(index == numOfBytes){

                // Send ACK (Transfer success)
                // ...

                // Decode buffer and create image from byte array
                Bitmap bmp = BitmapFactory.decodeByteArray(buffer, 0, numOfBytes);
                // Store output image
                outputImage = bmp;

                // Dismiss the bluetooth manager (close socket, exit waiting loop...)
                dismiss();

                // Return the image
                return bmp;
            }    
        }
        catch (IOException e){
            // ...
        }
    }
}

None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是什么导致我的图像在使用 FTP 通过蓝牙 RFCOMM 传输期间损坏? 的相关文章

随机推荐

  • 绑定到工具提示的 wpf 自定义控件依赖属性?

    我有一个在 WPF 中编写的自定义控件 它具有布尔依赖属性 public static readonly DependencyProperty IsAlertProperty DependencyProperty Register IsAl
  • 如何使用带有事件监听器的 HTML5 颜色选择器

    我正在寻找一种使用事件监听器的方法 并通过将鼠标移到调色板上来获取每个颜色的变化 它仅在没有事件侦听器的情况下有效 我想知道为什么 我的错误在哪里 如果我使用以下代码 它会起作用 将鼠标移到调色板上 会触发每种颜色的变化 HTML
  • 复选框不会使用 Javascript 在 IE7 中检查,但没有错误

    好吧 我对此完全感到困惑 我有一个脚本 它从 JSON 对象接收一堆值并创建一堆复选框 并根据它们的值选中或取消选中这些复选框 该脚本在 IE8 Firefox3 等中正常工作 等等 然而 在 IE7 中 脚本无法选中复选框 它没有显示任何
  • 如何通过 Eclipse 菜单中的“Eclipse Marketplace”或“安装新软件”选项安装 Eclipse 源代码?

    我正在开发 Eclipse RCP 应用程序 我对 RCP 开发和插件开发非常陌生 我想在我的 eclipse Kepler 4 3 2 中附加 eclipse 源代码 请一步步建议我 如何在我当前的eclipse中附加源代码 使用 帮助
  • Android 卡牌游戏,需要帮助才能启动

    我刚刚开始从事android开发 我想做一种基于卡牌的游戏 我只是在寻找关于如何开始的建议 我知道我必须制作菜单布局和视图 然后使用其中的 Intent 进入主游戏视图 我的症结是 将会有一副纸牌 这将是一个固定的数字 所以我想我可以将每张
  • 如何使用 PHAsset 从文件中获取 NSData

    我的路径有文件 file var mobile Media DCIM 100APPLE IMG 0197 mov 但是当我尝试这段代码时 NSError error NSData data NSData dataWithContentsOf
  • 如何在不同的mysql服务器上更新另一个表时自动更新一个表?

    假设我有两个数据库 db1 和 db2 分别位于不同的 mysql 服务器 A 和 B 上 我想每6小时检查一次 db2 中的 table1 是否有任何更新 然后 db1 中的 table1 将自动更新 我如何使用触发器或 cron 作业来
  • 更改 ANR(应用程序无响应)对话框的自定义对话框 (Android)

    我正在编写一个应用程序启动器 如主屏幕应用程序 我想知道是否可以拦截某些应用程序在 5 秒内没有响应时出现的默认对话框 我想要的是 启动器将显示另一个对话框 而不是显示默认的警报对话框 我在谷歌上搜索了一段时间 但没有找到任何东西 谁能告诉
  • 为什么git分支不显示任何内容?

    我正在使用 github 开发一个存储库 当我执行时git branch 我的屏幕上没有显示任何内容 见下图 git分支 当我执行时git status 据说我在分行 git 状态 所以有一些分支机构 我正在与他们合作 你知道如何解决这个问
  • 如何使用Json解析?

    以下是我的 Json 文件 Restaurants 8 Res name Purple Cafe and Wine Bar foodtype American Wine city Seattle state WA latitude 0 lo
  • 使用 awk 的最近邻

    这就是我尝试使用 AWK 语言做的事情 我主要在步骤 2 上遇到问题 我已经显示了一个示例数据集 但原始数据集由 100 个字段和 2000 条记录组成 算法 1 初始化精度 0 2 对于每条记录r Find the closest oth
  • 如何检测蓝牙设备是否已连接

    在 android 中 我的 Activity 如何知道蓝牙 A2DP 设备是否已连接到我的设备 有广播接收器吗 这个广播接收器怎么写 从 API 11 Android 3 0 开始 您可以使用蓝牙适配器发现连接到特定蓝牙配置文件的设备 我
  • 在引擎中使用观察者

    我创建了一个基本上用于我们所有项目的引擎 现在我想做的是添加一个before create回调该引擎中的所有模型 经过一番搜索后 我发现观察者是最佳选择 所以 我创建了这个观察者 app models baco auth auth obse
  • Angular 7 - build --prod 失败并出现错误:无法解析所有参数

    我使用 Angular 7 2 10 当我尝试使用命令构建生产项目时 ng b prod 我收到错误 ERROR in Can t resolve all parameters for ApiService in 我有一个带有 3 个参数的
  • WebView 即使应用程序处于后台/关闭时也如何运行(前台服务处于活动状态)

    我正在构建一个应用程序 它将从网站上抓取一些数据 并在满足某些条件时显示通知 当应用程序打开时 因为正在渲染 WebView 一切都运行良好 没有问题 但是当我关闭应用程序时 WebView 被禁用 因此我无法再使用它来抓取数据 抓取代码位
  • 为什么复制构造函数参数是 const?

    Vector const Vector other Copy constructor x other x y other y 为什么参数是常量 您得到的答案提到确保 ctor 无法更改正在复制的内容 他们是对的 将 const 放在那里确实
  • 使用 C# 进行远程 HTTP Post [重复]

    这个问题在这里已经有答案了 如何在 C 中执行远程 HTTP Post 请求 这是我曾经编写的一个小应用程序的代码 用于将包含值的表单发布到 URL 它应该非常坚固 formValues 是一个 Dictionary 包含要发布的变量及其值
  • 在 Android 上禁用屏幕旋转 [重复]

    这个问题在这里已经有答案了 当我按下按钮时 我想禁用屏幕旋转关于我的所有活动 我怎样才能做到这一点 顺便说一句 当用户单击按钮时 手机可以处于横向或纵向位置 setRequestedOrientation ActivityInfo SCRE
  • 关于hadoop hdfs文件系统重命名

    我正在将大量数据存储到 hdfs 中 我需要将文件从一个文件夹移动到另一个文件夹 请问文件系统的rename方法的成本一般是多少 假设我必须移动 TB 级的数据 非常感谢 重命名是 HDFS 中的仅元数据操作 因此 它也像普通 POSIX
  • 是什么导致我的图像在使用 FTP 通过蓝牙 RFCOMM 传输期间损坏?

    我正在开发两个独立的应用程序 用于使用 Obex 文件传输协议通过蓝牙 RFCOMM 进行数据传输 一方面 在 PC 上运行的 Windows C 控制台应用程序侦听传入的蓝牙连接 并在客户端发出请求时发送图像 另一方面 移动设备上运行的