How to send socket data from android device to pc connected via Android's Hotspot(AP)

帅比萌擦擦* 提交于 2020-04-18 03:55:29

问题


Related question with no answers Using DatagramSocket and python socket with devices connected to hotspot

My goal to implement something like syncthing so I can transfer files across my devices on a local network. (It is bidirectional i.e. android to PC, PC to android, PC to PC, android to android).

(My setup is a laptop and an android 10 device which I use as a hotspot to access the internet in my laptop)

I've been trying to implement network device discovery using jmdns, Jmdns was able to discover local services only when both the devices were connected to the same WIFI but it doesn't work on the android device when using it as a hotspot.

After searching SO profoundly for a few days, I was finally able to make it work by using android NSD and it was able to discover my pc when using as a Hotspot. Where I run a simple zeroconf python script announcing the server using an os assigned port.

pip install zeroconf==0.25.0 ifaddr==0.1.6

Server code

The following code will allow PC to PC message passing so file download can be done. This also allows the android NSD to discover this device.

import random
from contextlib import closing
from socket import *
from threading import Thread
from typing import List

import ifaddr
from zeroconf import (ServiceBrowser, ServiceInfo, ServiceListener, Zeroconf,
                      ZeroconfServiceTypes)

class MyListener:

    def remove_service(self, zeroconf, type, name):
        print("Service {} of type {} removed".format(name, type))

    def add_service(self, zeroconf, type, name):
        info = zeroconf.get_service_info(type, name)
        print("Service %s added, service info: %s" % (name, info))

        # https://stackoverflow.com/a/51596612/8608146
        print("Address", inet_ntoa(info.address), info.port)

        Thread(target=client_handler, args=(info,)).start()


def client_handler(info: ServiceInfo):
    with closing(socket(AF_INET, SOCK_DGRAM)) as s:
        print(info.address, info.port)
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        s.bind(('', info.port))
        while True:
            print("Waiting..", s.getsockname())
            m = s.recvfrom(1024)
            print(m)

# https://stackoverflow.com/a/45690594/8608146


def find_free_port():
    with closing(socket(AF_INET, SOCK_STREAM)) as s:
        s.bind(('', 0))
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        return s.getsockname()[1]

# https://github.com/p-sanches/somabits/blob/d581abaab6f045d65a774a78fbb43e232cf6f8da/somoserver/SomoServer/ZeroConf.py#L42


def get_all_addresses() -> List[str]:
    return list(set(
        addr.ip
        for iface in ifaddr.get_adapters()
        for addr in iface.ips
        # Host only netmask 255.255.255.255
        if addr.is_IPv4 and addr.network_prefix != 32
    ))


def get_local_ip(starts_with="192"):
    list_ip = get_all_addresses()
    local_ip = [i for i in list_ip if i.startswith(starts_with)]
    return local_ip[0]


print(get_all_addresses())
print(get_local_ip())

print(gethostname())
print(gethostbyname(gethostname()))

zeroconf = Zeroconf()

send_port = find_free_port()
local_ip = get_local_ip()

# assign a random name to this service
name = "pc-" + str(random.randint(0, 100))

# register a service
zeroconf.register_service(ServiceInfo(
    "_coolapp._udp.local.",
    "{}._coolapp._udp.local.".format(name),
    inet_aton(local_ip), send_port, 0, 0,
    # this is the txt record
    properties={"data": "device"}
))

listener = MyListener()
browser = ServiceBrowser(zeroconf, "_coolapp._udp.local.", listener)


try:
    std_response = ''
    s = socket(AF_INET, SOCK_DGRAM)
    s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
    while std_response != 'q':
        std_response = input("Press q to exit...\n\n")
        # print(x)
        s.sendto(std_response.encode('utf8'), ('255.255.255.255', send_port))

finally:
    zeroconf.close()

And I implemented android to android message passing in the app and it works when both devices are connected to the same network (even one of them being a hotspot).

Now I've been trying to access my android device IP from the python server and my laptop from the android app.

The zeroconf connection instance has the fields host i.e PC's IP and port on the android side. And address and port on the python server's side.

So I open a socket to the address host:port (PC's or another android device's host and port) in the android app and I open a socket on the IP of the zeroconf connection received from the android app or the other PC's address:port in the python server. And try to read/write in the socket from the android side.

Which is working now ONLY for android to android and PC to PC message passing. But from pc to android both refuse to connect. (A reminder, I'm using my android device 's hotspot connection)

The android code is located in this github repository.

The socket part of the code is


// NSD stuff gives address and port
...
//
// to receive messages from other devices
private inner class ReceivingThread : Runnable {
    override fun run() {
        ...
        val s = Socket(address, port)
        // The error is here in the next line
        // which is simply a timeout Exception
        val inputStream = BufferedInputStream(s.getInputStream())
        try {
            while (!Thread.currentThread().isInterrupted && !s.isClosed) {
                ...
                Log.d(TAG, "[Client RT] receive: $message")
            }

        } catch (e: Exception) {
            Log.d(TAG, "[Client RT] run: something went wrong ${this@Client}", e)
        }
    }
}


private inner class SendingThread(private val message: String) : Runnable {
    override fun run() {
        val s = socket ?: return
        val outputStream = BufferedOutputStream(s.getOutputStream() ?: return)
        Log.d(TAG, "run: outputStream = $outputStream")
        try {
            outputStream.write(message)
            outputStream.flush()
            ...
            Log.d(TAG, "[Client ST] send: $message")

        } catch (e: Exception) {
            Log.d(TAG, "[Client ST] run: something went wrong $this", e)
        }
    }
}

My error when sending something from the android app to the server is

java.net.ConnectException: failed to connect to /192.168.56.1 (port 51914) from /:: (port 39748): connect failed: ETIMEDOUT (Connection timed out)

When trying to connect to the client in the server is

Traceback (most recent call last):
  File "C:\Users\Rithvij\AppData\Local\Programs\Python\Python38\lib\threading.py", line 932, in _bootstrap_inner
    self.run()
  File "C:\Users\Rithvij\AppData\Local\Programs\Python\Python38\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "ano.py", line 65, in client_handler
    s.bind((inet_ntoa(info.address), info.port))
OSError: [WinError 10049] The requested address is not valid in its context

On my PC

λ ipconfig.exe | grep IPv4
   IPv4 Address. . . . . . . . . . . : 192.168.56.1
   IPv4 Address. . . . . . . . . . . : 192.168.99.1
   IPv4 Address. . . . . . . . . . . : 192.168.43.159
   IPv4 Address. . . . . . . . . . . : 192.168.137.1

on my android device (Termux)

$ ifconfig | grep inet
Warning: cannot open /proc/net/dev (Permission denied). Limited output.
        inet 127.0.0.1  netmask 255.0.0.0
        inet 10.83.151.210  netmask 255.255.255.252
        inet 25.135.14.145  netmask 255.255.255.252
        inet 192.168.43.1  netmask 255.255.255.0  broadcast 192.168.43.255

The IP is either not reachable or I'm dumb and I don't know anything (In which case I'd like some help)

PS: Took an hour+ to type this out and willing to edit, refine and add more details and post bounty if someone can to help me achieve basic socket stuff.

来源:https://stackoverflow.com/questions/61215787/how-to-send-socket-data-from-android-device-to-pc-connected-via-androids-hotspo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!