前言
关于开启热点,手机间通信,网上有很多相关的例子,但几乎所有的例子只在开启热点和进行socket连接部分有比较详细的描叙,实际上,这其中还有很多重要的细节,例如热点的IP如何获得, socket连接如何保证连接上等?

正文
两个Android手机要想相互间通信,可以通过蓝牙,也可以通过WIFI,相对而已WIFI的速度更快,但两个手机必须连接同一WIFI源才行。如果没有WIF源呢?Android 9提供了一个叫LocalOnlyHotspot的东西,其中一个手机开启热点,另一手机连接此热点,然后就可以通过socket通信。
实际上,这整个过程可以划分为三个独立部分开启热点、启动ServerSocket、连接热点并连接socket。但下面我是按相关技术的要点进行分段描叙的。

  1. 开启热点,获得热点名称,密码
    这部分在网上有很多介绍,我就直接贴代码了:
private void startHotApp(final Intent intent) {
        final WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        if (manager!= null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            manager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
                @Override
                public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
                    super.onStarted(reservation);
                    sReservation = reservation;
                    PendingIntent pendingIntent = intent.getParcelableExtra("pendingIntent");
                    Intent data = new Intent();
                    data.putExtra(Constants.KEY_SSID, reservation.getWifiConfiguration().SSID);
                    data.putExtra(Constants.KEY_PRESHARE, reservation.getWifiConfiguration().preSharedKey);
                    final String ipAddress = getLocalIpAddress();
                    data.putExtra(Constants.KEY_IP, ipAddress);
                    try {
                        DebugLog.info("pendingIntent.send:" + ipAddress);
                        if(pendingIntent != null) {
                            pendingIntent.send(HotService.this, 200, data);
                    } catch (PendingIntent.CanceledException e) {
                        e.printStackTrace();
                @Override
                public void onStopped() {
                    super.onStopped();
                @Override
                public void onFailed(int reason) {
                    super.onFailed(reason);
            }, null);
  1. 获得热点的IP
    这部分我要重点说说,网上很多通过WifiManager或者读取本地文件等方式来获取手机IP,其实都是不对的,我们手机连接别人的热点,才能通过这种方式获得IP,而我们手机自己开启的热点,这种方式要么获得的一直是0,要么就不对,可能是以前手机连接他人的热点形成的ip。
    首先,我们来看看热点IP是如何生成的:
    源代码网址:
    http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
198    // TODO: Refactor this in terms of calls to InterfaceController.
199    private boolean configureIPv4(boolean enabled) {
200        if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
202        // TODO: Replace this hard-coded information with dynamically selected
203        // config passed down to us by a higher layer IP-coordinating element.
204        String ipAsString = null;
205        int prefixLen = 0;
206        if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
207            ipAsString = USB_NEAR_IFACE_ADDR;
208            prefixLen = USB_PREFIX_LENGTH;
209        } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
210            ipAsString = getRandomWifiIPv4Address();
211            prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
212        } else {
213            // Nothing to do, BT does this elsewhere.
214            return true;
215        }
217        final LinkAddress linkAddr;
218        try {
219            final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
220            if (ifcg == null) {
221                mLog.e("Received null interface config");
222                return false;
223            }
225            InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
226            linkAddr = new LinkAddress(addr, prefixLen);
227            ifcg.setLinkAddress(linkAddr);
228            if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
229                // The WiFi stack has ownership of the interface up/down state.
230                // It is unclear whether the Bluetooth or USB stacks will manage their own
231                // state.
232                ifcg.ignoreInterfaceUpDownStatus();
233            } else {
234                if (enabled) {
235                    ifcg.setInterfaceUp();
236                } else {
237                    ifcg.setInterfaceDown();
238                }
239            }
240            ifcg.clearFlag("running");
241            mNMService.setInterfaceConfig(mIfaceName, ifcg);
242        } catch (Exception e) {
243            mLog.e("Error configuring interface " + e);
244            return false;
245        }
247        // Directly-connected route.
248        final RouteInfo route = new RouteInfo(linkAddr);
249        if (enabled) {
250            mLinkProperties.addLinkAddress(linkAddr);
251            mLinkProperties.addRoute(route);
252        } else {
253            mLinkProperties.removeLinkAddress(linkAddr);
254            mLinkProperties.removeRoute(route);
255        }
256        return true;
257    }
259    private String getRandomWifiIPv4Address() {
260        try {
261            byte[] bytes = NetworkUtils.numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
262            bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
263            return InetAddress.getByAddress(bytes).getHostAddress();
264        } catch (Exception e) {
265            return WIFI_HOST_IFACE_ADDR;
266        }
267    }

我们看到IP地址被保存到NetworkManagementService中,那么我们是不是可以通过NetworkManagementService来获得IP呢,很遗憾NetworkManagementService是hide的,那么是否可以通过反射方式的,于是试试:

private String getIpAddress() {
        String address = null;
        try {
            //IBinder b = ServiceManager.getService(Context.NETWORK_STATS_SERVICE);
            Class<?> serviceManager = Class.forName("android.os.ServiceManager");
            Method method = serviceManager.getMethod("getService", String.class);
            IBinder b = (IBinder)method.invoke(serviceManager.newInstance(), "network_management");
            //INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
            Class<?> stub =  Class.forName("android.os.INetworkManagementService$Stub");
            Method asInterface = stub.getMethod("asInterface", IBinder.class);
            DebugLog.info("asInterface:" + asInterface);
            Object service = asInterface.invoke(null, b);
            DebugLog.info("service:" + service);
            //InterfaceConfiguration ifcg = service.getInterfaceConfig("wlan0");
            if(service != null) {
                Method config = service.getClass().getMethod("getInterfaceConfig", String.class);
                Object ifcfg = config.invoke(service, "wlan0");
                //LinkAddress linkAddr = ifcg.getLinkAddress();
                Method getLink = ifcfg.getClass().getMethod("getLinkAddress");
                LinkAddress linkAddr = (LinkAddress)getLink.invoke(ifcfg);
                DebugLog.info("linkAddr:" + linkAddr);
                if (linkAddr != null) {
                    InetAddress Inetaddr = linkAddr.getAddress();
                    DebugLog.info("Inetaddr:" + Inetaddr);
                    if (Inetaddr != null) {
                        address = Inetaddr.getHostAddress();
                        DebugLog.info("address:" + address);
            //InterfaceConfiguration ifcg = service.getInterfaceConfig("wlan0");
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
            DebugLog.error("ClassNotFoundException:", e);
        return address;

但是,我的天啦:

 Neither user 10003 nor current process has android.permission.CONNECTIVITY_INTERNAL. 

android.permission.CONNECTIVITY_INTERNAL只能给系统APP使用,即使我们添加了也没用。于是路堵上了。
我的神啦,咋办?只好去圣地StackOverFlow找找,果然还真找到了:

private String getLocalIpAddress() {
        String ipAddress = null;
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        ipAddress = inetAddress.getHostAddress();
                        if(ipAddress != null && ipAddress.startsWith(Constants.HOT_SERVER_IP_PREFIX)) {
                            return ipAddress;
        } catch (SocketException ex) {
            DebugLog.error(ex.toString());
        return ipAddress;

参考TetherInterfaceStateMachine.java中的代码IP固定以192.168.43开头的。
Stackoverflow问题地址:how-to-get-my-ip-address
这下好了,热点的名称、密码、IP地址、端口(整个可以随便设置)都有了,通过蓝牙或者生成二维码的形式发给客户机,客户机就可以建立连接通信了。

  1. Socket 服务端
    这个也简单,不再描叙,直接贴代码:
@Override
    public void run() {
        try{
            if(sServerSocket != null) {
                sServerSocket.close();
                sServerSocket = null;
            sServerSocket =  new ServerSocket(Constants.HOT_SERVER_PORT);
            Socket clientSocket = sServerSocket.accept();//wait here
            String remoteIP = clientSocket.getInetAddress().getHostAddress();
            int remotePort = clientSocket.getLocalPort();
            DebugLog.info("remoteIP:" + remoteIP + " remotePort" + remotePort);
            inputStream = clientSocket.getInputStream();
            byte[] buffer = new byte[1024];
            int bytes;
            while(isRunning) {
                bytes = inputStream.read(buffer);
                if(bytes != -1) {
                    final byte[] data = new byte[bytes];
                    System.arraycopy(buffer, 0, data, 0, bytes);
                    final String content = new String(data);
                    DebugLog.info("data:" + content );
                    //dosomething
                Thread.sleep(20);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally{
            close();
  1. 客服端
    网上也有很多Socket客户端的代码,但是如果你直接照搬,你是不是发现,别人的正常,你的就是连接不上,或者说时好时不好?这其实是细节问题,连接上热点,网络不会马上就通,还需要等一会儿。具体过程可以这样描叙,连接热点—连通—分配IP—分配完成—开启socket连接—连接成功—开始发送数据,这其中都有一点等待时间,我这是通过轮询来保证连接成功的。
override fun run() {
        //wifiManager.updateNetwork(createConfig(ssid,psw))
        if (!ssid.equals("test")) {
            wifiManager.addNetwork(createConfig(ssid, psw))
            while (true) {//检测是否连接上热点
                if (wifiManager.connectionInfo != null) {
                    var c_ssid = getSSID(wifiManager)
                    Log.i("wenpd", "connectionInfo:$c_ssid")
                    if (ssid.equals(c_ssid)) break;
                Thread.sleep(500)
        while (true) {//检测是否已获得IP地址
            var ip = manager.connectionInfo.ipAddress
            Log.d("wenpd", "ip is $ip")
            if (ip != 0) {
                var routeip = manager.dhcpInfo.gateway
                Log.d("wenpd", "routeip is $routeip")
                break
            Thread.sleep(400)
        val ipRoute = getWifiRouteIPAddress(wifiManager);
        Log.d("wenpd", "connect:$ipRoute")
        while (true){//检测网络是否已经通了
            try {
                socket = Socket(ipRoute, 50000)
                break
            } catch (se: SocketException) {
            Thread.sleep(500)
        var outputStream = socket.getOutputStream();
        var i = 0;
        val len = Constants.CONTENTS.size
        while(isRunning) {
            val content = Constants.CONTENTS.get(i);
            outputStream.write(content.toByteArray())
            Thread.sleep(40)
            if(++i == len) {
                i = 0;

这里需要注意的是,如果客服端有保存其他wifi热点wifiManager.addNetwork不一定连上我们需要的那个,所以需要手动把保存的热点都先取消。

前言关于开启热点,手机间通信,网上有很多相关的例子,但几乎所有的例子只在开启热点和进行socket连接部分有比较详细的描叙,实际上,这其中还有很多重要的细节,例如热点的IP如何获得, socket连接如何保证连接上等?正文两个Android手机要想相互间通信,可以通过蓝牙,也可以通过WIFI,相对而已WIFI的速度更快,但两个手机必须连接同一WIFI源才行。如果没有WIF源呢?Android... import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.So
1。这个都是自己慢慢摸索的。mono for andriod ,资料真的太少了。 2.正在做安卓手机的项目。选择这条路都不知道能不能坚持下去。 3.希望有同样迷惑的人,照亮一点前面的路,也希望能照亮自己。 4.下面的代码运行环境 VS2012 + mono for andriod 4.8 java -sdk 与 mono for andriod 4.8 见下面的网盘 希望到时,这个网盘没有被封。 http://pan.baidu.com/s/1sjqMsqT http://www.51mono.com/article/show/172 http://www.51mono.com/article/show/96 安卓模拟器,推荐使用 Genymotion。 具体的安装教程,就请网上找吧。我实在没有力气了。 不过在模拟时,还是有点不够爽。 由于是 mono for andriod 专业破解版,所以可以直接使用 真机运行的。 我就是用 小米3 来运行。问题比较少。 另外要吐槽一下的是,开发这个手机APP,真的很耗内存。为此我不得不把我的老爷机换了4G的内存条。 由于是老爷机,内存条好贵的呀。 5,windows 开发环境 是 win8.1. 32位。 6.希望我啰嗦的这么多信息对你有用。 7.下面是正题了。 这个压缩包中,包含 C#写的服务端。 和 mono for andriod ---c# 写的手机客户端。 实现简单的 Socket --TCP/IP 通信。 其实,使用的类 和语法代码与 普通PC机的一样。但 手机客户端,还是有点点区别。 在pc客户端中,使用线程收取 信息,然后打印到UI控件上,是使用委托进行。 在手机APP中,有一个专门的方法来把这个信息寄送到 UI线程,然后在 UI控件上进行显示。 在例子中,有体现。 8.好了,就这么多了
最近把测试丢过来的种种BUG解决后,终于有时间去研究研究Socket通信,再加上以前做的WiFi连接和热点开启,于是有了现在的这篇博文:创建热点发送文件,让另一台手机连接热点接收文件。 两台设备是如何传输文件的: 发送端->创建WiFi热点 接收端->连接热点 发送端->发送文件列表 接收端->收到后展示文件列表,选择要接收的文件发送给发送端 发送端->发送所选文件 接收端->开始接收…
App端 打开wifi操作 private WifiManager mWifiManager; mWifiManager = (WifiManager) mContext.getSystemService(WifiManager.class);\\获取WIFI的服务 mWifiManager.setWifiEnabled(true) 让我们看一下WifiManager设置的时候是如何操作 * 创建一个新的 WifiManager 实例。 * 应用程序几乎
andriod-studio-2021.1.1.21-windows.exe是一款Android开发集成环境软件,这个版本是2021年1月1日发布的Windows版本。它主要用于开发Android应用程序,包括Android手机应用、平板应用、智能手表应用等。这个软件具备一系列强大的开发工具,如代码编辑器、编译器、调试器等,在开发过程中能够提高开发效率和开发质量。此外,andriod-studio-2021.1.1.21-windows.exe还提供了丰富的模板和插件,方便开发者在开发过程中更快速地完成任务。同时,该软件也是谷歌公司发布的官方工具,因此能够更好地支持Android操作系统和相关技术。如果您是一位Android应用程序开发者,那么andriod-studio-2021.1.1.21-windows.exe将是您值得选择的一个优秀工具。
解决了我的大问题。我的9.0手机上代码稍微改一下 if (!inetAddress.isLoopbackAddress() && (inetAddress.getAddress().length == 4)) { Log.e("5555", inetAddress.getHostAddress()); return inetAddress.getHostAddress(); [kubernetes]Calico运行异常:dial tcp 10.96.0.1:443: connect: connection refused vv0422: kube-proxy都正常,就很离谱~~~ 讯飞语音10407问题 weixin_42625420: 您好 您这 问题解决了吗