নেটওয়ার্ক পরিষেবা ডিসকভারি ব্যবহার করুন

নেটওয়ার্ক সার্ভিস ডিসকভারি (NSD) আপনার অ্যাপকে সেইসব পরিষেবায় অ্যাক্সেস দেয় যা অন্যান্য ডিভাইস লোকাল নেটওয়ার্কে প্রদান করে। NSD কাজ করে এমন ডিভাইসের মধ্যে প্রিন্টার, ওয়েবক্যাম, HTTPS সার্ভার ও অন্যান্য মোবাইল ডিভাইস অন্তর্ভুক্ত।

NSD, DNS-ভিত্তিক পরিষেবা শনাক্তকরণ (DNS-SD) মেকানিজম প্রয়োগ করে, যা আপনার অ্যাপকে পরিষেবার ধরন এবং ডিভাইসের ইনস্ট্যান্সের নাম উল্লেখ করে পরিষেবার জন্য অনুরোধ করার অনুমতি দেয় যা কাঙ্ক্ষিত ধরনের পরিষেবা প্রদান করে। DNS-SD, Android এবং অন্যান্য মোবাইল প্ল্যাটফর্ম, উভয় ক্ষেত্রেই কাজ করে।

আপনার অ্যাপে NSD যোগ করলে, আপনার ব্যবহারকারীরা লোকাল নেটওয়ার্কে অন্যান্য ডিভাইস শনাক্ত করতে পারবেন যেগুলি আপনার অ্যাপের অনুরোধ করা পরিষেবাগুলি সাপোর্ট করে। এটি ফাইল শেয়ারিং বা মাল্টি-প্লেয়ার গেমিংয়ের মতো বিভিন্ন পিয়ার-টু-পিয়ার অ্যাপ্লিকেশনের জন্য উপযোগী। Android-এর NSD API এই ধরনের ফিচার প্রয়োগ করার জন্য আপনার প্রয়োজনীয় প্রচেষ্টাকে সহজ করে তোলে।

এই পাঠে আপনাকে দেখানো হবে যে কীভাবে এমন একটি অ্যাপ্লিকেশন তৈরি করতে হয় যা লোকাল নেটওয়ার্কে নিজের নাম ও কানেকশন সংক্রান্ত তথ্য ব্রডকাস্ট করতে পারে এবং একই কাজ করছে এমন অন্যান্য অ্যাপ্লিকেশন থেকে তথ্য স্ক্যান করতে পারে। সবশেষে, এই টিউটোরিয়াল থেকে আপনি জানতে পারবেন যে অন্য ডিভাইসে চলা একই অ্যাপ্লিকেশনের সাথে কীভাবে কানেক্ট করতে হয়।

নেটওয়ার্কে আপনার পরিষেবা রেজিস্টার করুন

মনে রাখবেন: এই ধাপটি ঐচ্ছিক। আপনি যদি লোকাল নেটওয়ার্কে আপনার অ্যাপের পরিষেবা ব্রডকাস্ট করার ব্যাপারে আগ্রহী না হন, তাহলে আপনি পরের বিভাগে এগিয়ে যেতে পারেন, নেটওয়ার্কে পরিষেবা খুঁজুন।

লোকাল নেটওয়ার্কে আপনার পরিষেবা রেজিস্টার করতে, প্রথমে একটি NsdServiceInfo অবজেক্ট তৈরি করুন। এই অবজেক্ট সেই তথ্য প্রদান করে যা নেটওয়ার্কে থাকা অন্যান্য ডিভাইস ব্যবহার করে যখন তারা আপনার পরিষেবার সাথে কানেক্ট করবে কিনা সেই বিষয়ে সিদ্ধান্ত নেয়।

Kotlin

fun registerService(port: Int) {
    // Create the NsdServiceInfo object, and populate it.
    val serviceInfo = NsdServiceInfo().apply {
        // The name is subject to change based on conflicts
        // with other services advertised on the same network.
        serviceName = "NsdChat"
        serviceType = "_nsdchat._tcp"
        setPort(port)
        ...
    }
}

জাভা

public void registerService(int port) {
    // Create the NsdServiceInfo object, and populate it.
    NsdServiceInfo serviceInfo = new NsdServiceInfo();

    // The name is subject to change based on conflicts
    // with other services advertised on the same network.
    serviceInfo.setServiceName("NsdChat");
    serviceInfo.setServiceType("_nsdchat._tcp");
    serviceInfo.setPort(port);
    ...
}

এই কোড স্নিপেট পরিষেবার নাম "NsdChat" হিসেবে সেট করে। পরিষেবার নাম হল ইনস্ট্যান্সের নাম: এটি নেটওয়ার্কে অন্যান্য ডিভাইসের কাছে দৃশ্যমান নাম। নেটওয়ার্কে থাকা যেকোনও ডিভাইস এই নাম দেখতে পাবে, যা স্থানীয় পরিষেবা খোঁজার জন্য NSD ব্যবহার করছে। মনে রাখবেন যে নেটওয়ার্কে যেকোনও পরিষেবার জন্য নাম অনন্য হতে হবে এবং Android অটোমেটিক বিরোধ সমাধান ম্যানেজ করে। যদি নেটওয়ার্কে দুটি ডিভাইসে NsdChat অ্যাপ্লিকেশন ইনস্টল করা থাকে, তাহলে তাদের মধ্যে একটি অটোমেটিক পরিষেবার নাম পরিবর্তন করে "NsdChat (1)" এর মতো কিছু করে দেয়।

দ্বিতীয় প্যারামিটার পরিষেবার ধরন সেট করে, কোন প্রোটোকল ও ট্রান্সপোর্ট লেয়ার অ্যাপ্লিকেশন ব্যবহার করে তা নির্দিষ্ট করে। সিনট্যাক্স হল "_<protocol>._<transportlayer>"। কোড স্নিপেটে, পরিষেবাটি TCP-তে চলা HTTP প্রোটোকল ব্যবহার করে। প্রিন্টার পরিষেবা (যেমন, নেটওয়ার্ক প্রিন্টার) অফার করে এমন অ্যাপ্লিকেশন পরিষেবার ধরন "_ipp._tcp" হিসেবে সেট করবে।

মনে রাখবেন: ইন্টারন্যাশনাল অ্যাসাইনড নাম্বারস অথরিটি (IANA) NSD এবং Bonjour-এর মতো পরিষেবা শনাক্তকরণ প্রোটোকল দ্বারা ব্যবহৃত পরিষেবার প্রকারের একটি কেন্দ্রীভূত, প্রামাণিক তালিকা ম্যানেজ করে। আপনি পরিষেবার নাম ও পোর্ট নম্বরের IANA তালিকা থেকে তালিকাটি ডাউনলোড করতে পারবেন। আপনি নতুন কোনও পরিষেবার ধরন ব্যবহার করতে চাইলে, IANA পোর্ট ও পরিষেবা রেজিস্ট্রেশন ফর্ম পূরণ করে সেটি রিজার্ভ করতে হবে।

আপনার পরিষেবার জন্য পোর্ট সেট করার সময়, এটি হার্ডকোড করা এড়িয়ে চলুন কারণ এটি অন্যান্য অ্যাপ্লিকেশনের সাথে কনফ্লিক্ট করে। যেমন, আপনার অ্যাপ্লিকেশন সবসময় পোর্ট 1337 ব্যবহার করে বলে ধরে নিলে, একই পোর্ট ব্যবহার করে এমন অন্যান্য ইনস্টল করা অ্যাপ্লিকেশনের সাথে সম্ভাব্য দ্বন্দ্ব তৈরি হয়। পরিবর্তে, ডিভাইসের পরবর্তী উপলভ্য পোর্ট ব্যবহার করুন। কারণ এই তথ্য অন্যান্য অ্যাপকে একটি পরিষেবা ব্রডকাস্টের মাধ্যমে প্রদান করা হয়, তাই কম্পাইল-টাইমে অন্যান্য অ্যাপ্লিকেশনের মাধ্যমে আপনার অ্যাপ্লিকেশন যে পোর্ট ব্যবহার করে তা জানার প্রয়োজন নেই। পরিবর্তে, অ্যাপ্লিকেশনগুলি আপনার পরিষেবার সাথে কানেক্ট করার ঠিক আগে, আপনার পরিষেবা ব্রডকাস্ট থেকে এই তথ্য পেতে পারে।

আপনি যদি সকেট নিয়ে কাজ করেন, তাহলে এখানে দেখানো হয়েছে যে কীভাবে আপনি সকেটকে 0-তে সেট করে যেকোনও উপলভ্য পোর্টে ইনিশিয়ালাইজ করতে পারবেন।

Kotlin

fun initializeServerSocket() {
    // Initialize a server socket on the next available port.
    serverSocket = ServerSocket(0).also { socket ->
        // Store the chosen port.
        mLocalPort = socket.localPort
        ...
    }
}

জাভা

public void initializeServerSocket() {
    // Initialize a server socket on the next available port.
    serverSocket = new ServerSocket(0);

    // Store the chosen port.
    localPort = serverSocket.getLocalPort();
    ...
}

আপনি NsdServiceInfo অবজেক্টটি সংজ্ঞায়িত করেছেন, এখন আপনাকে RegistrationListener ইন্টারফেসটি প্রয়োগ করতে হবে। এই ইন্টারফেসে কলব্যাক থাকে যা Android ব্যবহার করে আপনার অ্যাপ্লিকেশনকে পরিষেবা রেজিস্ট্রেশন এবং আনরেজিস্ট্রেশনের সাফল্য বা ব্যর্থতা সম্পর্কে সতর্ক করে।

Kotlin

private val registrationListener = object : NsdManager.RegistrationListener {

    override fun onServiceRegistered(NsdServiceInfo: NsdServiceInfo) {
        // Save the service name. Android may have changed it in order to
        // resolve a conflict, so update the name you initially requested
        // with the name Android actually used.
        mServiceName = NsdServiceInfo.serviceName
    }

    override fun onRegistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // Registration failed! Put debugging code here to determine why.
    }

    override fun onServiceUnregistered(arg0: NsdServiceInfo) {
        // Service has been unregistered. This only happens when you call
        // NsdManager.unregisterService() and pass in this listener.
    }

    override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // Unregistration failed. Put debugging code here to determine why.
    }
}

জাভা

public void initializeRegistrationListener() {
    registrationListener = new NsdManager.RegistrationListener() {

        @Override
        public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
            // Save the service name. Android may have changed it in order to
            // resolve a conflict, so update the name you initially requested
            // with the name Android actually used.
            serviceName = NsdServiceInfo.getServiceName();
        }

        @Override
        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Registration failed! Put debugging code here to determine why.
        }

        @Override
        public void onServiceUnregistered(NsdServiceInfo arg0) {
            // Service has been unregistered. This only happens when you call
            // NsdManager.unregisterService() and pass in this listener.
        }

        @Override
        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Unregistration failed. Put debugging code here to determine why.
        }
    };
}

আপনার পরিষেবা রেজিস্টার করার জন্য এখন আপনার কাছে সব তথ্য আছে। মেথড কল করুন registerService().

মনে রাখবেন যে এই পদ্ধতিটি অ্যাসিঙ্ক্রোনাস, তাই পরিষেবা রেজিস্টার করার পরে যে কোড রান করতে হবে তা অবশ্যই onServiceRegistered() পদ্ধতিতে যেতে হবে।

Kotlin

fun registerService(port: Int) {
    // Create the NsdServiceInfo object, and populate it.
    val serviceInfo = NsdServiceInfo().apply {
        // The name is subject to change based on conflicts
        // with other services advertised on the same network.
        serviceName = "NsdChat"
        serviceType = "_nsdchat._tcp"
        setPort(port)
    }

    nsdManager = (getSystemService(Context.NSD_SERVICE) as NsdManager).apply {
        registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener)
    }
}

জাভা

public void registerService(int port) {
    NsdServiceInfo serviceInfo = new NsdServiceInfo();
    serviceInfo.setServiceName("NsdChat");
    serviceInfo.setServiceType("_http._tcp.");
    serviceInfo.setPort(port);

    nsdManager = Context.getSystemService(Context.NSD_SERVICE);

    nsdManager.registerService(
            serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener);
}

নেটওয়ার্কে পরিষেবা খুঁজে দেখুন

নেটওয়ার্কটি জীবন দিয়ে পরিপূর্ণ, নেটওয়ার্ক প্রিন্টার থেকে শুরু করে নেটওয়ার্ক ওয়েবক্যাম, কাছাকাছি টিক-ট্যাক-টো প্লেয়ারদের ভয়ঙ্কর, জ্বলন্ত যুদ্ধ পর্যন্ত। আপনার অ্যাপ্লিকেশনকে এই প্রাণবন্ত ইকোসিস্টেমের কার্যকারিতা দেখার সুযোগ করে দেওয়ার মূল চাবিকাঠি হল পরিষেবা আবিষ্কার। কোন কোন পরিষেবা উপলভ্য আছে তা দেখতে আপনার অ্যাপ্লিকেশনকে নেটওয়ার্কে পরিষেবা ব্রডকাস্ট শুনতে হবে এবং অ্যাপ্লিকেশন কাজ করতে পারে না এমন কিছু ফিল্টার করতে হবে।

পরিষেবা রেজিস্ট্রেশনের মতো পরিষেবা ডিসকভারির দুটি ধাপ আছে: প্রাসঙ্গিক কলব্যাক সহ ডিসকভারি লিসেনার সেট-আপ করা এবং discoverServices()-এ একটি অ্যাসিঙ্ক্রোনাস API কল করা।

প্রথমে, একটি বেনামী ক্লাস ইনস্ট্যান্টিয়েট করুন যা NsdManager.DiscoveryListener প্রয়োগ করে। নিম্নলিখিত স্নিপেটে একটি সহজ উদাহরণ দেখানো হয়েছে:

Kotlin

// Instantiate a new DiscoveryListener
private val discoveryListener = object : NsdManager.DiscoveryListener {

    // Called as soon as service discovery begins.
    override fun onDiscoveryStarted(regType: String) {
        Log.d(TAG, "Service discovery started")
    }

    override fun onServiceFound(service: NsdServiceInfo) {
        // A service was found! Do something with it.
        Log.d(TAG, "Service discovery success$service")
        when {
            service.serviceType != SERVICE_TYPE -> // Service type is the string containing the protocol and
                // transport layer for this service.
                Log.d(TAG, "Unknown Service Type: ${service.serviceType}")
            service.serviceName == mServiceName -> // The name of the service tells the user what they'd be
                // connecting to. It could be "Bob's Chat App".
                Log.d(TAG, "Same machine: $mServiceName")
            service.serviceName.contains("NsdChat") -> nsdManager.resolveService(service, resolveListener)
        }
    }

    override fun onServiceLost(service: NsdServiceInfo) {
        // When the network service is no longer available.
        // Internal bookkeeping code goes here.
        Log.e(TAG, "service lost: $service")
    }

    override fun onDiscoveryStopped(serviceType: String) {
        Log.i(TAG, "Discovery stopped: $serviceType")
    }

    override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
        Log.e(TAG, "Discovery failed: Error code:$errorCode")
        nsdManager.stopServiceDiscovery(this)
    }

    override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
        Log.e(TAG, "Discovery failed: Error code:$errorCode")
        nsdManager.stopServiceDiscovery(this)
    }
}

জাভা

public void initializeDiscoveryListener() {

    // Instantiate a new DiscoveryListener
    discoveryListener = new NsdManager.DiscoveryListener() {

        // Called as soon as service discovery begins.
        @Override
        public void onDiscoveryStarted(String regType) {
            Log.d(TAG, "Service discovery started");
        }

        @Override
        public void onServiceFound(NsdServiceInfo service) {
            // A service was found! Do something with it.
            Log.d(TAG, "Service discovery success" + service);
            if (!service.getServiceType().equals(SERVICE_TYPE)) {
                // Service type is the string containing the protocol and
                // transport layer for this service.
                Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
            } else if (service.getServiceName().equals(serviceName)) {
                // The name of the service tells the user what they'd be
                // connecting to. It could be "Bob's Chat App".
                Log.d(TAG, "Same machine: " + serviceName);
            } else if (service.getServiceName().contains("NsdChat")){
                nsdManager.resolveService(service, resolveListener);
            }
        }

        @Override
        public void onServiceLost(NsdServiceInfo service) {
            // When the network service is no longer available.
            // Internal bookkeeping code goes here.
            Log.e(TAG, "service lost: " + service);
        }

        @Override
        public void onDiscoveryStopped(String serviceType) {
            Log.i(TAG, "Discovery stopped: " + serviceType);
        }

        @Override
        public void onStartDiscoveryFailed(String serviceType, int errorCode) {
            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            nsdManager.stopServiceDiscovery(this);
        }

        @Override
        public void onStopDiscoveryFailed(String serviceType, int errorCode) {
            Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            nsdManager.stopServiceDiscovery(this);
        }
    };
}

NSD API এই ইন্টারফেসের পদ্ধতিগুলি ব্যবহার করে আপনার অ্যাপ্লিকেশনকে জানায় যখন আবিষ্কার শুরু হয়, যখন এটি ব্যর্থ হয় এবং যখন পরিষেবাগুলি পাওয়া যায় এবং হারিয়ে যায় (হারিয়ে যাওয়া মানে "আর উপলভ্য নেই")। লক্ষ্য করুন যে কোনও পরিষেবা খুঁজে পাওয়া গেলে এই স্নিপেট একাধিক চেক করে।

  1. ডিভাইসটি তার নিজস্ব ব্রডকাস্ট (যা বৈধ) বেছে নিয়েছে কিনা তা নির্ধারণ করতে, খুঁজে পাওয়া পরিষেবার নামের সাথে স্থানীয় পরিষেবার নামের তুলনা করা হয়।
  2. পরিষেবার ধরন চেক করা হয়, যাতে আপনার অ্যাপ্লিকেশন যে ধরনের পরিষেবার সাথে কানেক্ট করতে পারে, এটি সেই ধরনের পরিষেবা কিনা তা যাচাই করা যায়।
  3. সঠিক অ্যাপ্লিকেশনের সাথে কানেকশন যাচাই করতে পরিষেবার নাম চেক করা হয়।

পরিষেবার নাম চেক করা সবসময় প্রয়োজন হয় না এবং আপনি কোনও নির্দিষ্ট অ্যাপ্লিকেশনের সাথে কানেক্ট করতে চাইলে তবেই এটি প্রাসঙ্গিক হয়। যেমন, অ্যাপ্লিকেশনটি শুধুমাত্র অন্য ডিভাইসে চলা নিজের ইনস্ট্যান্সের সাথে কানেক্ট করতে চাইতে পারে। তবে, অ্যাপ্লিকেশনটি যদি কোনও নেটওয়ার্ক প্রিন্টারের সাথে কানেক্ট করতে চায়, তাহলে পরিষেবার ধরন "_ipp._tcp" কিনা তা দেখাই যথেষ্ট।

লিসেনার সেট-আপ করার পরে, discoverServices() কল করুন, আপনার অ্যাপ্লিকেশন যে ধরনের পরিষেবা খুঁজবে, যে ডিসকভারি প্রোটোকল ব্যবহার করবে এবং আপনি যে লিসেনার তৈরি করেছেন, সেগুলি পাস করুন।

Kotlin

nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)

জাভা

nsdManager.discoverServices(
        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener);

নেটওয়ার্কে পরিষেবার সাথে কানেক্ট করুন

আপনার অ্যাপ্লিকেশন নেটওয়ার্কে কানেক্ট করার জন্য কোনও পরিষেবা খুঁজে পেলে, resolveService() পদ্ধতি ব্যবহার করে প্রথমে সেই পরিষেবার কানেকশন সংক্রান্ত তথ্য নির্ধারণ করতে হবে। এই পদ্ধতিতে পাস করার জন্য একটি NsdManager.ResolveListener প্রয়োগ করুন এবং কানেকশন সংক্রান্ত তথ্য সহ NsdServiceInfo পেতে এটি ব্যবহার করুন।

Kotlin

private val resolveListener = object : NsdManager.ResolveListener {

    override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
        // Called when the resolve fails. Use the error code to debug.
        Log.e(TAG, "Resolve failed: $errorCode")
    }

    override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
        Log.e(TAG, "Resolve Succeeded. $serviceInfo")

        if (serviceInfo.serviceName == mServiceName) {
            Log.d(TAG, "Same IP.")
            return
        }
        mService = serviceInfo
        val port: Int = serviceInfo.port
        val host: InetAddress = serviceInfo.host
    }
}

জাভা

public void initializeResolveListener() {
    resolveListener = new NsdManager.ResolveListener() {

        @Override
        public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
            // Called when the resolve fails. Use the error code to debug.
            Log.e(TAG, "Resolve failed: " + errorCode);
        }

        @Override
        public void onServiceResolved(NsdServiceInfo serviceInfo) {
            Log.e(TAG, "Resolve Succeeded. " + serviceInfo);

            if (serviceInfo.getServiceName().equals(serviceName)) {
                Log.d(TAG, "Same IP.");
                return;
            }
            mService = serviceInfo;
            int port = mService.getPort();
            InetAddress host = mService.getHost();
        }
    };
}

পরিষেবাটি সমাধান হয়ে গেলে, আপনার অ্যাপ্লিকেশন IP অ্যাড্রেস এবং পোর্ট নম্বর সহ বিস্তারিত পরিষেবার তথ্য পায়। পরিষেবার সাথে আপনার নিজস্ব নেটওয়ার্ক কানেকশন তৈরি করতে আপনার যা যা প্রয়োজন, তা এখানে দেওয়া হল।

অ্যাপ্লিকেশন বন্ধ করার সময় আপনার পরিষেবা আনরেজিস্টার করুন

অ্যাপ্লিকেশনের লাইফসাইকেল চলাকালীন প্রয়োজন অনুযায়ী NSD ফাংশন চালু ও বন্ধ করা গুরুত্বপূর্ণ। আপনার অ্যাপ্লিকেশন বন্ধ হয়ে গেলে সেটি অনিবন্ধিত করলে, অন্যান্য অ্যাপ্লিকেশন যাতে এটিকে এখনও অ্যাক্টিভ আছে বলে মনে না করে এবং এটির সাথে কানেক্ট করার চেষ্টা না করে, তা প্রতিরোধ করতে সাহায্য করে। এছাড়াও, পরিষেবা খুঁজে বের করা একটি ব্যয়বহুল অপারেশন এবং মূল অ্যাক্টিভিটি পজ করা হলে এটি বন্ধ করা উচিত এবং অ্যাক্টিভিটি আবার চালু করা হলে এটি আবার চালু করা উচিত। আপনার প্রধান অ্যাক্টিভিটির লাইফসাইকেল পদ্ধতি ওভাররাইড করুন এবং উপযুক্তভাবে পরিষেবা ব্রডকাস্ট ও ডিসকভারি শুরু ও বন্ধ করতে কোড যোগ করুন।

Kotlin

    // In your application's Activity

    override fun onPause() {
        nsdHelper?.tearDown()
        super.onPause()
    }

    override fun onResume() {
        super.onResume()
        nsdHelper?.apply {
            registerService(connection.localPort)
            discoverServices()
        }
    }

    override fun onDestroy() {
        nsdHelper?.tearDown()
        connection.tearDown()
        super.onDestroy()
    }

    // NsdHelper's tearDown method
    fun tearDown() {
        nsdManager.apply {
            unregisterService(registrationListener)
            stopServiceDiscovery(discoveryListener)
        }
    }

জাভা

    // In your application's Activity

    @Override
    protected void onPause() {
        if (nsdHelper != null) {
            nsdHelper.tearDown();
        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (nsdHelper != null) {
            nsdHelper.registerService(connection.getLocalPort());
            nsdHelper.discoverServices();
        }
    }

    @Override
    protected void onDestroy() {
        nsdHelper.tearDown();
        connection.tearDown();
        super.onDestroy();
    }

    // NsdHelper's tearDown method
    public void tearDown() {
        nsdManager.unregisterService(registrationListener);
        nsdManager.stopServiceDiscovery(discoveryListener);
    }