如何从Android设备向通过Android热点(AP)连接的pc发送套接字数据

2024-04-29 22:30:17 发布

您现在位置:Python中文网/ 问答频道 /正文

没有答案的相关问题Using DatagramSocket and python socket with devices connected to hotspot

我的目标是实现类似syncthing的东西,这样我就可以在本地网络上通过设备传输文件。(它是双向的,即Android到PC、PC到Android、PC到PC、Android到Android)

(我的设置是一台笔记本电脑和一台安卓10设备,我用它作为热点,在笔记本电脑中访问互联网)

我一直在尝试使用jmdns实现网络设备发现,只有当两个设备连接到同一个WIFI时,Jmdns才能够发现本地服务,但当将其用作热点时,它在Android设备上不起作用

我最终能够通过使用Android NSD使它工作,并且它能够在作为热点使用时发现我的pc。在这里,我运行了一个简单的zeroconf python脚本,宣布服务器使用os分配的端口

pip install zeroconf==0.25.0 ifaddr==0.1.6

服务器代码

以下代码将允许PC到PC的消息传递,以便可以完成文件下载。这也允许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()

我在应用程序中实现了安卓到安卓的消息传递,当两台设备都连接到同一个网络(甚至其中一台是热点)时,它就能工作

现在我一直在尝试从python服务器访问我的Android设备IP,从Android应用程序访问我的笔记本电脑

zeroconf连接实例在Android端有host字段,即PC的IP和port。以及python服务器端的addressport

因此,我在Android应用程序中打开地址host:port(PC或其他Android设备的主机和端口)的套接字,并在python服务器中从Android应用程序或其他PC的address:port接收的zeroconf连接的IP上打开套接字。并尝试从Android端读取/写入套接字

它现在只适用于Android到Android和PC到PC的消息传递。但是从pc到Android都拒绝连接。(提醒一下,我正在使用Android设备的热点连接)

Android代码位于in this github repository

代码的套接字部分是


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

在我的电脑上

λ 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要么无法访问,要么我什么都不知道


Tags: nameinfromimportipinfogetport