局域网发现协议

局域网发现协议

网关和手机之间的发现协议应该用一个可以自动匹配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);
            }


        }

最后更新于