问题
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