首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 藍牙通信實戰:基於 32feet.NET 實現 PC 與手機雙向文件傳輸

     編輯:關於C語言

前言

藍牙作為短距無線通信標準,廣泛應用於 PC 與手機、嵌入式設備間的數據交換。.NET 原生不支援藍牙協議棧,業界通用方案是 32feet.NET(核心組件 InTheHand.Net.Personal.dll),它封裝了 Windows 藍牙堆疊的 OBEX 協議,支援文件傳輸、序列埠、設備發現等功能。本文提供一個完整的 WinForm 範例,涵蓋適配器檢測、設備配對選擇、文件發送與接收監聽,代碼可直接落地。

環境準備

  1. NuGet 安裝:Install-Package 32feet.NET(或手動引用 InTheHand.Net.Personal.dll)。
  2. 目標框架:.NET Framework 4.6+ / .NET Core 3.1+ / .NET 5+(需 Windows 運行時)。
  3. 硬體:內建或 USB 藍牙適配器,驅動正常,已與目標手機配對。

核心類別速覽

類別命名空間用途
BluetoothRadioInTheHand.Net.Bluetooth本機藍牙無線電控制(開關、可發現模式、資訊查詢)
SelectBluetoothDeviceDialogInTheHand.Windows.Forms系統級設備選擇對話框(已配對/可發現/未知)
ObexWebRequestInTheHand.NetOBEX 客戶端,發送文件至遠端設備
ObexListenerInTheHand.NetOBEX 伺服器,監聽並接收遠端推送的文件
BluetoothAddressInTheHand.Net.Bluetooth藍牙 MAC 位址封裝

完整 WinForm 實作:Form1.cs

using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using InTheHand.Net;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using InTheHand.Windows.Forms;

public partial class Form1 : Form
{
    BluetoothRadio radio = null;
    string sendFileName = null;
    BluetoothAddress sendAddress = null;
    ObexListener listener = null;
    string recDir = null;
    Thread listenThread, sendThread;

    public Form1()
    {
        InitializeComponent();
        radio = BluetoothRadio.PrimaryRadio;
        CheckForIllegalCrossThreadCalls = false; // 簡化跨執行緒 UI 更新,生產請用 Invoke

        if (radio == null)
        {
            MessageBox.Show("未偵測到本機藍牙適配器!", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        recDir = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        lblRecDir.Text = recDir;
    }

    // 選擇遠端設備
    private void btnSelectDevice_Click(object sender, EventArgs e)
    {
        var dlg = new SelectBluetoothDeviceDialog
        {
            ShowRemembered = true,
            ShowAuthenticated = true,
            ShowUnknown = true
        };
        if (dlg.ShowDialog() == DialogResult.OK)
        {
            sendAddress = dlg.SelectedDevice.DeviceAddress;
            lblAddress.Text = $"位址:{sendAddress}  名稱:{dlg.SelectedDevice.DeviceName}";
        }
    }

    // 選擇發送文件
    private void btnSelectFile_Click(object sender, EventArgs e)
    {
        using (var dlg = new OpenFileDialog())
        {
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                sendFileName = dlg.FileName;
                lblFilePath.Text = Path.GetFileName(sendFileName);
            }
        }
    }

    // 發送文件
    private void btnSend_Click(object sender, EventArgs e)
    {
        if (sendAddress == null || sendFileName == null)
        {
            MessageBox.Show("請先選擇設備與文件");
            return;
        }
        sendThread = new Thread(SendFile) { IsBackground = true };
        sendThread.Start();
    }

    void SendFile()
    {
        try
        {
            btnSend.Enabled = false;
            lblStatus.Text = "發送中...";

            var req = new ObexWebRequest(sendAddress, Path.GetFileName(sendFileName));
            req.ReadFile(sendFileName); // 同步讀取並發送
            using (var resp = req.GetResponse())
            {
                // 可檢查 resp.StatusCode
            }
            lblStatus.Text = "發送完成!";
        }
        catch (Exception ex)
        {
            MessageBox.Show($"發送失敗:{ex.Message}");
            lblStatus.Text = "發送失敗";
        }
        finally
        {
            btnSend.Enabled = true;
        }
    }

    // 選擇接收目錄
    private void btnSelectRecDir_Click(object sender, EventArgs e)
    {
        using (var dlg = new FolderBrowserDialog { Description = "選擇接收文件儲存目錄" })
        {
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                recDir = dlg.SelectedPath;
                lblRecDir.Text = recDir;
            }
        }
    }

    // 開始/停止監聽
    private void btnListen_Click(object sender, EventArgs e)
    {
        if (listener == null || !listener.IsListening)
        {
            radio.Mode = RadioMode.Discoverable; // 關鍵:讓手機能搜尋到 PC
            listener = new ObexListener(ObexTransport.Bluetooth);
            listener.Start();

            if (listener.IsListening)
            {
                btnListen.Text = "停止";
                lblRecStatus.Text = "監聽中...";
                listenThread = new Thread(ReceiveLoop) { IsBackground = true };
                listenThread.Start();
            }
        }
        else
        {
            listener.Stop();
            btnListen.Text = "監聽";
            lblRecStatus.Text = "已停止";
        }
    }

    void ReceiveLoop()
    {
        while (listener?.IsListening == true)
        {
            var ctx = listener.GetContext(); // 封鎖等待連入
            if (ctx == null) break;

            var req = ctx.Request;
            string fileName = Path.GetFileName(Uri.UnescapeDataString(req.RawUrl).TrimStart('/'));
            string savePath = Path.Combine(recDir, fileName);

            req.WriteFile(savePath); // 接收並存檔
            lblRecStatus.Text = $"收到:{fileName}";
        }
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        sendThread?.Abort();
        listenThread?.Abort();
        listener?.Stop();
        base.OnFormClosed(e);
    }
}

關鍵流程圖解

藍牙適配器資訊
設備選擇對話框
程式介面

常見坑點與解決方案

現象原因對策
BluetoothRadio.PrimaryRadio 回傳 null驅動未安裝、適配器被禁用、虛擬機無藍牙直通裝置管理員確認、BIOS 開啟、宿主機開啟藍牙直通
手機搜尋不到 PCPC 未設為可發現模式監聽前務必 radio.Mode = RadioMode.Discoverable
發送失敗 / 逾時未配對、OBEX 服務未啟動、防火牆攔截先在系統藍牙設定完成配對、關閉防火牆測試
接收檔名亂碼URL 編碼未正確解碼使用 Uri.UnescapeDataString + TrimStart('/')
大文件傳輸極慢OBEX 協議本身開銷大、藍牙 2.x 頻寬低改用 BLE GATT 或 Wi-Fi Direct 傳大文件

進階擴展方向

  • 進度條顯示:ObexWebRequest 無進度事件,需自行分塊讀取並發送,或改用 ObexClient 底層流控制。
  • 多文件批次傳輸:打包成 ZIP 再傳,接收端自動解壓。
  • 服務化部署:將監聽邏輯封裝為 Windows Service,開機自動啟動,系統托盤顯示狀態。
  • 跨平台:.NET 6+ 可嘗試 BluetoothLE (Windows) / Android.Bluetooth (MAUI) 統一抽象層。

總結

32feet.NET 讓 C# 開發者以極低成本接入藍牙 OBEX 文件傳輸。本範例涵蓋「發送」與「接收」兩大核心場景,代碼結構清晰、異常處理完善,可直接整合至專案。需注意藍牙頻寬限制,僅適用於小文件(聯繫人、圖片、配置檔)傳輸;大文件請改用 Wi-Fi Direct 或傳統熱點方案。掌握此技能,讓你的桌面應用輕鬆打通與手機的最後一米。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved