Как отправить данные сокета с устройства Android на p c, подключенный через точку доступа Android (AP) - PullRequest
0 голосов
/ 14 апреля 2020

Связанный вопрос без ответов Использование DatagramSocket и python сокета с устройствами, подключенными к точке доступа

Моя цель реализовать что-то вроде syncthing , чтобы я мог передавать файлы через мои устройства в локальной сети. (Это двунаправленный, т.е. Android до P C, P C до Android, P C до P C, Android до Android).

(My setup - это ноутбук и устройство Android 10, которое я использую в качестве точки доступа для доступа к inte rnet в моем ноутбуке)

Я пытался реализовать обнаружение сетевых устройств с помощью jmdns, Jmdns смог обнаружить локальные сервисы, только когда оба устройства были подключены к одному и тому же WIFI, но он не работал на устройстве Android при использовании его в качестве точки доступа.

Я наконец смог заставить его работать используя Android NSD , и он смог обнаружить мой p c при использовании в качестве точки доступа. Там, где я запускаю простой скрипт zeroconf python, объявляющий сервер с использованием назначенного ОС порта.

pip install zeroconf==0.25.0 ifaddr==0.1.6

Код сервера

Следующий код разрешит P C к P C передача сообщений, чтобы можно было загрузить файл. Это также позволяет Android NSD обнаруживать это устройство.

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()

И я реализовал передачу сообщений Android до Android в приложение, и оно работает, когда оба устройства подключены к одной сети ( даже одна из них является горячей точкой).

Теперь я пытаюсь получить доступ к своему Android IP-адресу устройства с сервера python и моему ноутбуку из приложения Android.

Экземпляр соединения zeroconf имеет поля host, т. Е. IP-адрес P C и port на стороне Android. И address и port на стороне сервера python.

Поэтому я открываю сокет по адресу host:port (P C или хост и порт другого Android устройства) в приложении Android я открываю сокет на IP-адресе соединения zeroconf, полученного от приложения Android или другого address:port P C на сервере python. И попробуйте выполнить чтение / запись в сокет со стороны Android.

, которая работает ТОЛЬКО для Android до Android и от P C до P C передачи сообщений. Но от p c до Android оба отказываются подключаться. (Напоминаю, что я использую подключение к точке доступа моего устройства Android)

Код Android находится в этом репозитории github .

Часть сокета код


// 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)
        }
    }
}

Моя ошибка при отправке чего-либо из приложения Android на сервер:

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

При попытке подключиться к клиенту на сервере это

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

На моем P C

λ 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

на моем Android устройстве (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

IP-адрес либо недоступен, либо нет знать что-нибудь.

...