দুটি ডিভাইসের মধ্যে কানেকশন তৈরি করতে, আপনাকে অবশ্যই সার্ভার-সাইড এবং ক্লায়েন্ট-সাইড উভয় মেকানিজম প্রয়োগ করতে হবে কারণ একটি ডিভাইসকে অবশ্যই একটি সার্ভার সকেট খুলতে হবে এবং অন্যটিকে অবশ্যই সার্ভার ডিভাইসের MAC অ্যাড্রেস ব্যবহার করে কানেকশন শুরু করতে হবে। সার্ভার ডিভাইস ও ক্লায়েন্ট ডিভাইস, দুটির ক্ষেত্রেই প্রয়োজনীয়
BluetoothSocket আলাদা আলাদা
উপায়ে পাওয়া যায়। ইনকামিং কানেকশন গ্রহণ করা হলে সার্ভার সকেট সংক্রান্ত তথ্য পায়। সার্ভারে RFCOMM চ্যানেল খোলার সময় ক্লায়েন্ট সকেট সংক্রান্ত তথ্য প্রদান করে।
সার্ভার ও ক্লায়েন্টকে একে অপরের সাথে কানেক্ট করা হয়েছে বলে তখনই বিবেচনা করা হয় যখন তাদের প্রত্যেকের কাছে
একই RFCOMM চ্যানেলে একটি কানেক্ট করা BluetoothSocket থাকে। এই পর্যায়ে, প্রতিটি
ডিভাইস ইনপুট ও আউটপুট স্ট্রিম পেতে পারে এবং ডেটা ট্রান্সফার শুরু হতে পারে, যা
ব্লুটুথ ডেটা
ট্রান্সফার করা সংক্রান্ত বিভাগে আলোচনা করা হয়েছে। এই বিভাগে
দুটি ডিভাইসের মধ্যে কীভাবে কানেকশন শুরু করতে হয় তা বর্ণনা করা হয়েছে।
ব্লুটুথ ডিভাইস খোঁজার চেষ্টা করার আগে, আপনার কাছে উপযুক্ত ব্লুটুথ অনুমতি আছে কিনা এবং ব্লুটুথের জন্য আপনার অ্যাপ সেট-আপ করেছেন কিনা তা নিশ্চিত করুন।
কানেকশন টেকনিক
একটি প্রয়োগ কৌশল হল প্রতিটি ডিভাইসকে সার্ভার হিসেবে অটোমেটিক প্রস্তুত করা যাতে প্রতিটি ডিভাইসে একটি সার্ভার সকেট খোলা থাকে এবং কানেকশনের জন্য অপেক্ষা করে। এই ক্ষেত্রে, যেকোনও ডিভাইস অন্যটির সাথে কানেকশন শুরু করতে পারে এবং ক্লায়েন্ট হতে পারে। বিকল্প হিসেবে, একটি ডিভাইস স্পষ্টভাবে কানেকশন হোস্ট করতে পারে এবং চাহিদা অনুযায়ী সার্ভার সকেট খুলতে পারে এবং অন্য ডিভাইস কানেকশন শুরু করে।
ছবি ১. ব্লুটুথ পেয়ারিং ডায়ালগ।
সার্ভার হিসেবে কানেক্ট করুন
আপনি দুটি ডিভাইস কানেক্ট করতে চাইলে, একটি ডিভাইসকে অবশ্যই সার্ভার হিসেবে কাজ করতে হবে এবং সেটিতে একটি
খোলা
BluetoothServerSocket থাকতে হবে।
সার্ভার সকেটের উদ্দেশ্য হল ইনকামিং কানেকশনের অনুরোধ শোনা
এবং অনুরোধ গ্রহণ করার পরে কানেক্ট করা BluetoothSocket প্রদান করা। BluetoothServerSocket থেকে
BluetoothSocket পাওয়া গেলে,
BluetoothServerSocket বাতিল করা যেতে পারে—এবং করা উচিত—যদি না আপনি
ডিভাইসটিকে আরও কানেকশন গ্রহণ করতে দিতে চান।
সার্ভার সকেট সেট-আপ করতে এবং কানেকশন গ্রহণ করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:
listenUsingRfcommWithServiceRecord(String, UUID)নম্বরে কল করেBluetoothServerSocketপান।স্ট্রিং হল আপনার পরিষেবার শনাক্তকরণযোগ্য নাম, যা সিস্টেম ডিভাইসে নতুন পরিষেবা শনাক্তকরণ প্রোটোকল (SDP) ডেটাবেস এন্ট্রিতে অটোমেটিক লিখে দেয়। নামটি ইচ্ছামতো দেওয়া যায় এবং এটি আপনার অ্যাপের নামও হতে পারে। এছাড়াও, SDP এন্ট্রিতে ইউনিভার্সালি ইউনিক আইডেন্টিফায়ার (UUID) অন্তর্ভুক্ত থাকে এবং ক্লায়েন্ট ডিভাইসের সাথে কানেকশন সংক্রান্ত চুক্তির ভিত্তি তৈরি করে। অর্থাৎ, ক্লায়েন্ট এই ডিভাইসের সাথে কানেক্ট করার চেষ্টা করলে, এটি একটি UUID বহন করে যা অনন্যভাবে সেই পরিষেবাকে শনাক্ত করে যার সাথে এটি কানেক্ট করতে চায়। কানেকশন গ্রহণ করার জন্য এইসব UUID অবশ্যই ম্যাচ করতে হবে।
UUID হল একটি স্ট্রিং আইডির জন্য একটি স্ট্যান্ডার্ডাইজড ১২৮-বিট ফর্ম্যাট যা অনন্যভাবে তথ্য শনাক্ত করতে ব্যবহৃত হয়। UUID-এর পুনরাবৃত্তি হওয়ার সম্ভাবনা কার্যত শূন্য হওয়ায়, কোনও সিস্টেম বা নেটওয়ার্কে অনন্য হতে হবে এমন তথ্য শনাক্ত করতে UUID ব্যবহার করা হয়। এটি কোনও সেন্ট্রালাইজড অথরিটির ব্যবহার ছাড়াই স্বতন্ত্রভাবে তৈরি করা হয়। এই ক্ষেত্রে, এটি আপনার অ্যাপের ব্লুটুথ পরিষেবা অনন্যভাবে শনাক্ত করতে ব্যবহার করা হয়। আপনার অ্যাপের সাথে ব্যবহার করার জন্য UUID পেতে, আপনি ওয়েবে উপলভ্য অনেক র্যান্ডম
UUIDজেনারেটরের মধ্যে থেকে একটি ব্যবহার করতে পারেন, তারপরfromString(String)-এর মাধ্যমে UUID ইনিশিয়ালাইজ করুন।accept()-এ কল করে কানেকশনের অনুরোধের জন্য শোনা শুরু করুন।এটি একটি ব্লকিং কল। কানেকশন গ্রহণ করা হলে অথবা কোনও ব্যতিক্রম ঘটলে এটি রিটার্ন করে। শুধুমাত্র তখনই কানেকশন গ্রহণ করা হয় যখন রিমোট ডিভাইস UUID সহ কানেকশনের অনুরোধ পাঠায় যা এই লিসেনিং সার্ভার সকেটের সাথে রেজিস্টার করা UUID-এর সাথে মেলে। সফল হলে,
accept()একটি কানেক্ট করাBluetoothSocketফিরিয়ে দেয়।আপনি অতিরিক্ত কানেকশন গ্রহণ করতে না চাইলে,
close()নম্বরে কল করুন।এই মেথড কল সার্ভার সকেট ও তার সব রিসোর্স রিলিজ করে, কিন্তু
accept()-এর মাধ্যমে ফেরত দেওয়া কানেক্ট করাBluetoothSocketবন্ধ করে না। TCP/IP-এর মতো নয়, RFCOMM প্রতি চ্যানেলে একবারে শুধুমাত্র একটি কানেক্ট করা ক্লায়েন্টকে অনুমতি দেয়, তাই বেশিরভাগ ক্ষেত্রে কানেক্ট করা সকেট গ্রহণ করার সাথে সাথেইBluetoothServerSocket-এclose()কল করা যুক্তিযুক্ত।
accept() কলটি একটি ব্লকিং কল হওয়ার কারণে, এটি প্রধান অ্যাক্টিভিটি UI থ্রেডে
চালু করবেন না। অন্য থ্রেডে এটি এক্সিকিউট করলে আপনার অ্যাপ অন্যান্য ব্যবহারকারীর ইন্টার্যাকশনে
এখনও উত্তর দিতে পারবে। সাধারণত, আপনার অ্যাপের মাধ্যমে ম্যানেজ করা নতুন থ্রেডে
BluetoothServerSocket বা BluetoothSocket-এর সাথে সম্পর্কিত সব কাজ করা
যুক্তিযুক্ত। accept()-এর মতো ব্লক করা কল বাতিল করতে,
অন্য থ্রেড থেকে BluetoothServerSocket বা BluetoothSocket-এ
close() কল করুন। মনে রাখবেন
যে BluetoothServerSocket বা BluetoothSocket-এর সব পদ্ধতি
থ্রেড-সেফ।
উদাহরণ
ইনকামিং কানেকশন গ্রহণ করে এমন সার্ভার কম্পোনেন্টের জন্য নিম্নলিখিতটি হল একটি সরলীকৃত থ্রেড:
Kotlin
private inner class AcceptThread : Thread() { private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) { bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID) } override fun run() { // Keep listening until exception occurs or a socket is returned. var shouldLoop = true while (shouldLoop) { val socket: BluetoothSocket? = try { mmServerSocket?.accept() } catch (e: IOException) { Log.e(TAG, "Socket's accept() method failed", e) shouldLoop = false null } socket?.also { manageMyConnectedSocket(it) mmServerSocket?.close() shouldLoop = false } } } // Closes the connect socket and causes the thread to finish. fun cancel() { try { mmServerSocket?.close() } catch (e: IOException) { Log.e(TAG, "Could not close the connect socket", e) } } }
জাভা
private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket; public AcceptThread() { // Use a temporary object that is later assigned to mmServerSocket // because mmServerSocket is final. BluetoothServerSocket tmp = null; try { // MY_UUID is the app's UUID string, also used by the client code. tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { Log.e(TAG, "Socket's listen() method failed", e); } mmServerSocket = tmp; } public void run() { BluetoothSocket socket = null; // Keep listening until exception occurs or a socket is returned. while (true) { try { socket = mmServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "Socket's accept() method failed", e); break; } if (socket != null) { // A connection was accepted. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(socket); mmServerSocket.close(); break; } } } // Closes the connect socket and causes the thread to finish. public void cancel() { try { mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "Could not close the connect socket", e); } } }
এই উদাহরণে, শুধুমাত্র একটি ইনকামিং কানেকশনই প্রত্যাশিত, তাই কানেকশন
গ্রহণ করা এবং BluetoothSocket পাওয়া মাত্রই, অ্যাপটি
প্রাপ্ত BluetoothSocket-কে একটি আলাদা থ্রেডে পাস করে,
BluetoothServerSocket বন্ধ করে এবং লুপ থেকে বেরিয়ে আসে।
মনে রাখবেন, accept() যখন BluetoothSocket রিটার্ন করে, তখন সকেট আগে থেকেই
কানেক্ট করা থাকে। তাই, ক্লায়েন্ট সাইড থেকে যেভাবে
connect() কল করেন, সেভাবে কল করবেন না।
অ্যাপ-নির্দিষ্ট manageMyConnectedSocket() পদ্ধতিটি ডেটা ট্রান্সফার করার জন্য থ্রেড শুরু করার জন্য ডিজাইন করা হয়েছে, যা
ব্লুটুথ ডেটা
ট্রান্সফার করা সম্পর্কিত বিষয়ে আলোচনা করা হয়েছে।
সাধারণত, ইনকামিং কানেকশন শোনার কাজ শেষ হওয়ার সাথে সাথেই আপনার BluetoothServerSocket বন্ধ করে দেওয়া উচিত। এই উদাহরণে, BluetoothSocket অ্যাকোয়ার করার সাথে সাথেই
close() কল করা হয়। এছাড়াও, আপনি নিজের থ্রেডে একটি পাবলিক পদ্ধতি প্রদান করতে চাইতে পারেন যা সেই সার্ভার সকেটে শোনা বন্ধ করার প্রয়োজন হলে ব্যক্তিগত BluetoothSocket বন্ধ করতে পারে।
ক্লায়েন্ট হিসেবে কানেক্ট করুন
কোনও রিমোট ডিভাইসের সাথে কানেকশন শুরু করতে, যেটি খোলা সার্ভার সকেটে কানেকশন গ্রহণ করছে,
আপনাকে প্রথমে একটি BluetoothDevice অবজেক্ট পেতে হবে যা রিমোট ডিভাইসকে উপস্থাপন করে। কীভাবে
BluetoothDevice তৈরি করতে হয় তা জানতে, ব্লুটুথ ডিভাইস
খুঁজুন দেখুন। আপনাকে অবশ্যই
BluetoothDevice ব্যবহার করে BluetoothSocket পেতে হবে এবং কানেকশন
শুরু করতে হবে।
প্রাথমিক পদ্ধতি নিচে উল্লেখ করা হল:
BluetoothDeviceব্যবহার করে,BluetoothSocket-এ কল করেBluetoothSocketপান।createRfcommSocketToServiceRecord(UUID)এই পদ্ধতিটি একটি
BluetoothSocketঅবজেক্ট শুরু করে যা ক্লায়েন্টকে একটিBluetoothDevice-এর সাথে সংযোগ করতে দেয়। এখানে পাস করা UUID-এর সাথে সার্ভার ডিভাইসের ব্যবহার করা UUID-এর মিল থাকতে হবে, যখন এটিlistenUsingRfcommWithServiceRecord(String, UUID)কল করে এরBluetoothServerSocketখোলে। ম্যাচিং UUID ব্যবহার করতে, আপনার অ্যাপে UUID স্ট্রিং হার্ড-কোড করুন এবং তারপর সার্ভার ও ক্লায়েন্ট কোড, দুটির থেকেই এটি রেফারেন্স করুন।connect()-এ কল করে কানেকশন শুরু করুন। মনে রাখবেন যে এই পদ্ধতিটি হল একটি ব্লকিং কল।কোনও ক্লায়েন্ট এই পদ্ধতি কল করার পরে, সিস্টেম SDP লুক-আপ করে যাতে ম্যাচিং UUID সহ রিমোট ডিভাইস খুঁজে পাওয়া যায়। লুকআপ সফল হলে এবং রিমোট ডিভাইস কানেকশন গ্রহণ করলে, এটি কানেকশনের সময় ব্যবহার করার জন্য RFCOMM চ্যানেল শেয়ার করে এবং
connect()পদ্ধতি রিটার্ন করে। কানেকশন ব্যর্থ হলে অথবাconnect()মেথডের টাইম-আউট হয়ে গেলে (প্রায় ১২ সেকেন্ড পরে), মেথডটিIOExceptionথ্রো করে।
যেহেতু connect() হল একটি ব্লকিং কল, তাই আপনার সবসময় এই কানেকশন পদ্ধতিটি
এমন একটি থ্রেডে পারফর্ম করা উচিত যা মূল অ্যাক্টিভিটি (UI) থ্রেড থেকে
আলাদা।
উদাহরণ
ব্লুটুথ কানেকশন শুরু করে এমন ক্লায়েন্ট থ্রেডের প্রাথমিক উদাহরণ নিচে দেওয়া হল:
Kotlin
private inner class ConnectThread(device: BluetoothDevice) : Thread() { private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) { device.createRfcommSocketToServiceRecord(MY_UUID) } public override fun run() { // Cancel discovery because it otherwise slows down the connection. bluetoothAdapter?.cancelDiscovery() mmSocket?.let { socket -> // Connect to the remote device through the socket. This call blocks // until it succeeds or throws an exception. socket.connect() // The connection attempt succeeded. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(socket) } } // Closes the client socket and causes the thread to finish. fun cancel() { try { mmSocket?.close() } catch (e: IOException) { Log.e(TAG, "Could not close the client socket", e) } } }
জাভা
private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { // Use a temporary object that is later assigned to mmSocket // because mmSocket is final. BluetoothSocket tmp = null; mmDevice = device; try { // Get a BluetoothSocket to connect with the given BluetoothDevice. // MY_UUID is the app's UUID string, also used in the server code. tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { Log.e(TAG, "Socket's create() method failed", e); } mmSocket = tmp; } public void run() { // Cancel discovery because it otherwise slows down the connection. bluetoothAdapter.cancelDiscovery(); try { // Connect to the remote device through the socket. This call blocks // until it succeeds or throws an exception. mmSocket.connect(); } catch (IOException connectException) { // Unable to connect; close the socket and return. try { mmSocket.close(); } catch (IOException closeException) { Log.e(TAG, "Could not close the client socket", closeException); } return; } // The connection attempt succeeded. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(mmSocket); } // Closes the client socket and causes the thread to finish. public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "Could not close the client socket", e); } } }
লক্ষ্য করুন যে এই স্নিপেটে, কানেকশন করার চেষ্টা করার আগে cancelDiscovery() কল করা হয়। connect()-এর আগে সবসময় cancelDiscovery() কল করা উচিত,
বিশেষ করে কারণ ডিভাইস খোঁজার প্রসেস বর্তমানে চলছে কিনা তা নির্বিশেষে
cancelDiscovery() সফল হয়। আপনার অ্যাপকে যদি ডিভাইস খোঁজার প্রসেস চলছে কিনা তা নির্ধারণ করতে হয়, তাহলে আপনি isDiscovering() ব্যবহার করে
চেক করতে পারবেন।
অ্যাপ-নির্দিষ্ট manageMyConnectedSocket() পদ্ধতিটি ডেটা ট্রান্সফার করার জন্য থ্রেড শুরু করার জন্য ডিজাইন করা হয়েছে, যা
ব্লুটুথ ডেটা ট্রান্সফার করা
সম্পর্কিত বিভাগে আলোচনা করা হয়েছে।
আপনার BluetoothSocket-এর সাথে কাজ হয়ে গেলে, সবসময় close()-এ কল করুন। এটি করলে
কানেক্ট করা সকেট অবিলম্বে বন্ধ হয়ে যায় এবং সব সম্পর্কিত ইন্টার্নাল
রিসোর্স রিলিজ হয়ে যায়।