[µKod] Sposób na znajdywanie własnych urządzeń w sieci LAN
: sobota 10 lut 2018, 21:50
Często podłączając urządzenie do sieci LAN, co chwila trzeba sprawdzać jaki ma adres IP. Czy to poprzez informacje via UART, wyświetlacz, lub też listę dostępnych urządzeń po zalogowaniu się do kontrolera sieci. Zaczęło mnie to męczyć, więc wymyśliłem uniwersalny pomysł. Zaimplementowałem go na ESP, orange PI czy Raspberry Pi. Postanowiłem się nim podzielić, sam zamysł jest banalnie prosty. Postawiony serwer UDP, czeka na konkretny ciąg znaków, na konkretnym porcie. W momencie jak go otrzyma, po prostu wysyła informację zwrotną z nazwą "projektu", albo urządzenia wraz z własnym adresem IP. W tym wypadku prezentowany kod jest skrobnięty na malinkę. Korzysta ona z linuxowych soketów. W projekcie z ESP12/32 wykorzystuje SDK RTOS od producenta i tam korzystam z funkcjonalności LWiP.
Przechodząc do rzeczy. Kod prezentuje się następująco:
Uruchomienie jest banalnie proste. Po kompilacji należy uruchomić serwer, należy podać parametr, który określa nazwę urządzenia.
Efekt:
Przy braku argumentów startowych efekt będzie taki:
Te 3 definicje, określają ciąg znaków w formie stringu, który będzie szukany w pakietach, domyślna nazwa urządzenia, oraz format odpowiedzi.
No i to by było na tyle
Zaznaczam że kod nie jest idealny, jest generalnie odpowiednio spreparowaną sklejką kodów znalezionych na stackoverflow
Na razie działa, nie wymaga poprawek, a wiadomo, lepsze jest wrogiem dobrego.
Pozdrawiam
Przechodząc do rzeczy. Kod prezentuje się następująco:
Kod: Zaznacz cały
#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netdb.h>
#include<ifaddrs.h>
#include<unistd.h>
#define BUFLEN 512 //Max length of buffer
#define PORT 1994 //The port on which to listen for incoming data
#define DEVICE_NAME "Some device"
#define SEARCH_STRING "find devices"
#define RESPONSE_STRING "%s there, from: %s!"
void die(char *s)
{
perror(s);
exit(1);
}
void getIp( unsigned char *_ip)
{
FILE *f;
char line[100] , *p , *c;
f = fopen("/proc/net/route" , "r");
while(fgets(line , 100 , f))
{
p = strtok(line , " \t");
c = strtok(NULL , " \t");
if(p!=NULL && c!=NULL)
{
if(strcmp(c , "00000000") == 0)
{
printf("Default interface is : %s \n" , p);
break;
}
}
}
//which family do we require , AF_INET or AF_INET6
int fm = AF_INET;
struct ifaddrs *ifaddr, *ifa;
int family , s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
exit(EXIT_FAILURE);
}
//Walk through linked list, maintaining head pointer so we can free list later
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
{
continue;
}
family = ifa->ifa_addr->sa_family;
if(strcmp( ifa->ifa_name , p) == 0)
{
if (family == fm)
{
s = getnameinfo( ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6) , host , NI_MAXHOST , NULL , 0 , NI_NUMERICHOST);
if (s != 0)
{
printf("getnameinfo() failed: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
printf("address: %s\n", host);
strcpy(_ip, host);
}
printf("\n");
}
}
freeifaddrs(ifaddr);
}
int main(int argc , char *argv[])
{
struct sockaddr_in si_me, si_other;
int s, i, slen = sizeof(si_other) , recv_len;
char buf[BUFLEN];
char host[NI_MAXHOST];
char response[BUFLEN];
char deviceName[BUFLEN];
if( argc < 2 )
{
sprintf(deviceName, DEVICE_NAME);
}
else if (argc == 2)
{
sprintf(deviceName, argv[1]);
}
//create a UDP socket
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
die("socket");
}
// zero out the structure
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
//bind socket to port
if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
{
die("bind");
}
//get IP using on local machine
getIp(host);
while(1)
{
//try to receive some data, this is a blocking call
if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1)
{
die("recvfrom()");
}
if(strcmp(buf, SEARCH_STRING) == 0)
{
//now reply the client with the same data
sprintf(response, RESPONSE_STRING, deviceName, host);
if (sendto(s, response, strlen(response), 0, (struct sockaddr*) &si_other, slen) == -1)
{
die("sendto()");
}
}
}
close(s);
return 0;
}
Uruchomienie jest banalnie proste. Po kompilacji należy uruchomić serwer, należy podać parametr, który określa nazwę urządzenia.
Kod: Zaznacz cały
gcc udp_search.c -o search_devices && ./search_devices testEfekt:
Przy braku argumentów startowych efekt będzie taki:
Te 3 definicje, określają ciąg znaków w formie stringu, który będzie szukany w pakietach, domyślna nazwa urządzenia, oraz format odpowiedzi.
Kod: Zaznacz cały
#define DEVICE_NAME "Some device"
#define SEARCH_STRING "find devices"
#define RESPONSE_STRING "%s there, from: %s!"No i to by było na tyle
Pozdrawiam