> For the complete documentation index, see [llms.txt](https://plainchant.gitbook.io/plainchant/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://plainchant.gitbook.io/plainchant/tao-jie-zi-bian-cheng/untitled.md).

# 局域网发现协议

﻿局域网发现协议

网关和手机之间的发现协议应该用一个可以自动匹配IP的方式。

比如说，网关刚开始是处于路由模式的，那么它的IP地址可能是192.168.1.1，当用户将它切换成STA模式或者说桥接模式时，它的IP地址一定会变化，那么这个时候手机就无法获取设备的IP了。

网上经常说用组播去做发现协议，但是我经过测试后发现，组播只适用于一般的场景，比如手机只会连接网关，或者说只会去连接它的上级路由。因为组播协议在路由端会遇到路由的不同处理，有的可能无法出局域网导致无法发现。

组播想发送到自己的子网需要添加下面一条路由 协议：

```
route add -net 224.0.0.0 netmask 224.0.0.0 dev br-lan
```

此路由协议后面的接口改一下的话同样适用于上级路由。

组播的代码如下：

初始化

```
if(-1 == (sIotcMulticast.iSocketFd = socket(AF_INET, SOCK_DGRAM, 0)))
    {
        ERR_vPrintf(T_TRUE, "socket create error %s\n", strerror(errno));
        return E_MULTI_ERROR_CREATESOCK;
    }
    
    int on = 1;  /*SO_REUSEADDR port can used twice by program */
    if((setsockopt(sIotcMulticast.iSocketFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))<0) 
    {  
        ERR_vPrintf(T_TRUE,"setsockopt failed, %s\n", strerror(errno));  
        close(sIotcMulticast.iSocketFd);
        return E_MULTI_ERROR_SETSOCK;
    }  


    sIotcMulticast.server_addr.sin_family = AF_INET;  
    sIotcMulticast.server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    sIotcMulticast.server_addr.sin_port = htons(iPort);
    if(-1 == bind(sIotcMulticast.iSocketFd, 
                    (struct sockaddr*)&sIotcMulticast.server_addr, sizeof(sIotcMulticast.server_addr)))
    {
        ERR_vPrintf(T_TRUE,"bind socket failed, %s\n", strerror(errno));  
        close(sIotcMulticast.iSocketFd);
        return E_MULTI_ERROR_BIND;
    }


    sIotcMulticast.multi_addr.imr_multiaddr.s_addr = inet_addr(paMulAddress);
    sIotcMulticast.multi_addr.imr_interface.s_addr = htonl(INADDR_ANY);
    if(setsockopt(sIotcMulticast.iSocketFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
            (char *)&sIotcMulticast.multi_addr, sizeof(sIotcMulticast.multi_addr)) < 0)
    {
        ERR_vPrintf(T_TRUE,"setsockopt failed, %s\n", strerror(errno));  
        close(sIotcMulticast.iSocketFd);
        return E_MULTI_ERROR_SETSOCK;
    }
```

然后起一个线程作为服务器，等待手机发送请求，收到请求后随便回复一个什么值，手机会根据这个包去解析出IP，无需显式的发送：

```
while(sIotcMulticast.eThreadState)
    {
        sched_yield();
        if((iRecvLen = recvfrom(sIotcMulticast.iSocketFd, paRecvBuffer, MDBF, 0, 
                    (struct sockaddr*)&sIotcMulticast.server_addr,(socklen_t*)&iAddrLen)) < 0)
        {
            ERR_vPrintf(T_TRUE, "Recvfrom Data Error!\n");
        }
        BLUE_vPrintf(DBG_MUL, "Recv Data: %s\n", paRecvBuffer);
        const char *paResponse = "This is Server";
        if(sendto(sIotcMulticast.iSocketFd, paResponse, strlen(paResponse), 0, 
                    (struct sockaddr*)&sIotcMulticast.server_addr, sizeof(sIotcMulticast.server_addr)) < 0)
        {
            ERR_vPrintf(T_TRUE, "Send Data Error!\n");
        }
        
        sleep(0);
    }
```

手机端代码：

```
class SocketSearchThread extends Thread{
        public String stringSearch;


        public SocketSearchThread(String str){
            stringSearch = str;
        }


        @Override
        public void run() {
            Message msgSocket = new Message();
            utils.DBG_vPrintf("Create Mul Socket");
            try {
                MulticastSocket mSocket = new MulticastSocket(6789);
                InetAddress groupAddress = InetAddress.getByName(Utils.stringMulAddress);
                mSocket.joinGroup(groupAddress);
                mSocket.setTimeToLive(4);


                utils.DBG_vPrintf("Send Data to Server");
                byte[] buffSearch = new byte[255];
                buffSearch = stringSearch.getBytes("utf-8");
                DatagramPacket udpPacket = new DatagramPacket(buffSearch, buffSearch.length, groupAddress, Utils.iMulPort);
                mSocket.send(udpPacket);


                byte[] byteRev = new byte[512];
                udpPacket = new DatagramPacket(byteRev,byteRev.length);
                utils.DBG_vPrintf("Recv Data From Server");
                mSocket.setSoTimeout(2000);//Set Recv Timeout
                mSocket.receive(udpPacket);


                String stringIotcAddress;
                stringIotcAddress = new String(udpPacket.getData()).trim();
                utils.DBG_vPrintf("Recv Data From Server Success:" + stringIotcAddress);


                Message msgIotcAddress = new Message();
                msgIotcAddress.what = Utils.iFindServer;
                msgIotcAddress.obj = udpPacket.getAddress().getHostAddress();
                utils.DBG_vPrintf("The Server Ip is " + msgIotcAddress.obj.toString());


                handlerSocketRev.sendMessage(msgIotcAddress);


                mSocket.leaveGroup(groupAddress);
                mSocket.disconnect();
                mSocket.close();
            } catch (IOException e) {
                utils.ERR_vPrintf("Can't Search Iotc," + e.toString());
                e.printStackTrace();
                handlerSocketRev.sendEmptyMessage(Utils.iTimeOut);
            }


        }
    }
```

\================================================================================================

上面的组播经测试发现并不好用，网段一变就无法连接，下面介绍广播的方式。

广播的客户端代码：

```
static teBroadStatus IotcBroadcastSocketInit(int iPort, char *paNetAddress)
{
    DBG_vPrintf(DBG_BROAD, "IotcBroadcastSocketInit\n");
    if(NULL == paNetAddress)
    {
        ERR_vPrintf(T_TRUE, "The Param is Error\n");
        return E_BROAD_ERROR_PARAM;
    }
    
    if(-1 == (sIotcBroadcast.iSocketFd = socket(AF_INET, SOCK_DGRAM, 0)))
    {
        ERR_vPrintf(T_TRUE, "socket create error %s\n", strerror(errno));
        return E_BROAD_ERROR_CREATESOCK;
    }
    
    int on = 1;  /*SO_REUSEADDR port can used twice by program */
    if((setsockopt(sIotcBroadcast.iSocketFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))<0) 
    {  
        ERR_vPrintf(T_TRUE,"setsockopt failed, %s\n", strerror(errno));  
        close(sIotcBroadcast.iSocketFd);
        return E_BROAD_ERROR_SETSOCK;
    }  


    bzero(&sIotcBroadcast.server_addr,sizeof(sIotcBroadcast.server_addr));  
    sIotcBroadcast.server_addr.sin_family=AF_INET;  
    sIotcBroadcast.server_addr.sin_addr.s_addr=htonl(INADDR_ANY);  
    sIotcBroadcast.server_addr.sin_port=htons(iPort);  
    
    if(-1 == bind(sIotcBroadcast.iSocketFd, 
                    (struct sockaddr*)&sIotcBroadcast.server_addr, sizeof(sIotcBroadcast.server_addr)))
    {
        ERR_vPrintf(T_TRUE,"bind socket failed, %s\n", strerror(errno));  
        close(sIotcBroadcast.iSocketFd);
        return E_BROAD_ERROR_BIND;
    }


    BLUE_vPrintf(DBG_BROAD, "Create Socket Fd %d\n", sIotcBroadcast.iSocketFd);
    return E_BROAD_OK;
}
```

线程体：

```
while(sIotcBroadcast.eThreadState)
    {
        sched_yield();
        if((iRecvLen = recvfrom(sIotcBroadcast.iSocketFd, paRecvBuffer, MDBF, 0, 
                    (struct sockaddr*)&sIotcBroadcast.server_addr,(socklen_t*)&iAddrLen)) < 0)
        {
            ERR_vPrintf(T_TRUE, "Recvfrom Data Error!%s\n", strerror(errno));
            usleep(5);
            continue;
        }
        struct sockaddr_in *p = (struct sockaddr_in*)&sIotcBroadcast.server_addr;
        BLUE_vPrintf(DBG_BROAD, "Recv Data[%d]: %s, from %s\n", iRecvLen, paRecvBuffer, inet_ntoa(p->sin_addr));
        const char *paResponse = "This is Server";
        if(sendto(sIotcBroadcast.iSocketFd, paResponse, strlen(paResponse), 0, 
                    (struct sockaddr*)&sIotcBroadcast.server_addr, sizeof(sIotcBroadcast.server_addr)) < 0)
        {
            ERR_vPrintf(T_TRUE, "Send Data Error!\n");
        } else {
            BLUE_vPrintf(DBG_BROAD, "Send Data: %s\n", paResponse);
        }
        
        sleep(0);
    }
```

手机端代码，因为广播的数据会马上被路由转发，所以手机自己会收到自己的数据，需要重复收听直到收到服务端响应：

```
public void run() {
            Message msgSocket = new Message();
            utils.DBG_vPrintf("Create Broadcast Socket");
            try {
                DatagramSocket uSocket = new DatagramSocket(null);
                uSocket.setReuseAddress(true);
                uSocket.bind(new InetSocketAddress(6789));


                byte[] buffer = new byte[40];
                DatagramPacket dataPacket = new DatagramPacket(buffer, buffer.length);
                byte[] data = stringSearch.getBytes();
                dataPacket.setData(data);
                dataPacket.setLength(data.length);
                dataPacket.setPort(6789);


                InetAddress broadcastAddr = InetAddress.getByName("255.255.255.255");
                dataPacket.setAddress(broadcastAddr);


                uSocket.send(dataPacket);
                uSocket.setSoTimeout(2000);//Set Recv Timeout


                byte[] byteRev = new byte[512];
                dataPacket = new DatagramPacket(byteRev,byteRev.length);
                utils.DBG_vPrintf("Recv Data From Server");
                uSocket.setSoTimeout(2000);


                while(true){
                    uSocket.receive(dataPacket);
                    String stringIotcResponse;
                    stringIotcResponse = new String(dataPacket.getData()).trim();
                    utils.DBG_vPrintf("Recv Data From Server Success:" + stringIotcResponse);
                    if (stringIotcResponse.equals("This is Server")){
                        break;
                    }
                }


                Message msgIotcAddress = new Message();
                msgIotcAddress.what = Utils.iFindServer;
                msgIotcAddress.obj = dataPacket.getAddress().getHostAddress();
                utils.DBG_vPrintf("The Server Ip is " + msgIotcAddress.obj.toString());


                handlerSocketRev.sendMessage(msgIotcAddress);


                uSocket.disconnect();
                uSocket.close();
            } catch (IOException e) {
                utils.ERR_vPrintf("Can't Search Iotc," + e.toString());
                e.printStackTrace();
                handlerSocketRev.sendEmptyMessage(Utils.iTimeOut);
            }


        }
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://plainchant.gitbook.io/plainchant/tao-jie-zi-bian-cheng/untitled.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
