實驗: 取得 RSSI 並回傳至伺服器
利用 Socket 的架構,即時的取得 RSSI 資訊,並回傳至後端伺服器
整體 RSSI 蒐集架構
本實驗中的程式以及說明由蔡東倫先生所準備,作者只做些微修改與補充。
本實驗的目標為取得監聽的 RSSI 資訊並回傳至後端的伺服器。一開始我們有一個 WiFi 裝置透過OpenWRT WiFi AP 進行資料傳輸,此 OpenWRT WiFi AP 以建立兩張網卡,分別處於 master 模式以及 monitor 模式,因此可以一邊幫該使用者轉傳封包,一邊記錄下 RSSI 資訊,並送到後端的伺服器。
伺服器和 WiFi AP 的資料傳輸使用 socket 模式,換言之,伺服器為 socket server,而 WiFi AP 為 socket client,兩者透過 polling 的方式將資料回傳。
WiFi AP 端程式
在之前的實驗中,我們在 OpenWRT 上建立虛擬無線網卡,模式分別為 AP 模式與 Monitor 模式。接著用 tcpdump 監聽 Monitor 模式的無線網卡,並用 grep 指令保留同時包含 RSSI 值與source MAC 的訊息。上述的動作皆可以由 C 語言建立 pipe 時觸發,接著在 C 語言實作 parser將所要資訊擷取出來。
以下附的是參考程式碼,由於使用了 thread 因此用 gcc 編譯時需加上 -lpthread,才能編譯成功。在此程式中使用了兩個 thread:
sensing_func(): 負責讀取 pipe、取出對應的 RSSI、計算 RSSI 平均值。
report_func(): 當 socket server polling 時回報所得到的 RSSI 資訊。
為了避免兩者同時存取共用記憶體,在 critical section 使用了 mutex ,避免對 RSSI 數值同時存取,造成錯誤。以下是所撰寫的程式 (RSSI_client.c):
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "netdb.h"
#include "signal.h"
#define MAX_USER_NUM 200
#define ReconnectionDelayTime 2
#define WLAN "wlan0"
char* server_ip;
int server_port;
void *sensing_func(void *parm);
void *report_func(void *parm);
char* get_interface_MAC_string();
char* search_pattern_following(char* str1, char* str2);
static pthread_t sensing_thread;
static pthread_t report_thread;
static pthread_mutex_t sensing_report_mutex;
static int sensing_socket;
static int construct_link(char ip[], int port);
void disconnection(int sig);
struct sensing_result
{
char src_mac[100];
float rssi_mean;
float rssi_variance;
int sample_num;
};
static struct sensing_result sensing_result[100];
static int sensing_result_num=0;
int main(int argc, char *argv[])
{
if(argc<3)
{
printf("argument: [Server IP] [Server Port]\n");
return;
}
else
{
server_ip = argv[1];
server_port = atoi(argv[2]);
}
(void)signal(SIGINT, disconnection);
pthread_mutex_init(&sensing_report_mutex, NULL);
pthread_mutex_lock(&sensing_report_mutex);
pthread_create(&sensing_thread, NULL, sensing_func, NULL);
pthread_create(&report_thread, NULL, report_func, NULL);
pthread_mutex_unlock(&sensing_report_mutex);
while(1);
return 0;
}
#define TCPDUMP_CMD "tcpdump -ne -y ieee802_11_radio -s 65535 -i wlan1-2 |grep \"signal\" | grep \"SA\\|TA\" |grep \"dBm\""
void *sensing_func(void *parm)
{
FILE *fp;
char sensing_dump[1000];
char src_mac[100];
float rssi;
float old_rssi_mean;
float new_rssi_mean;
float old_rssi_variance;
float new_rssi_variance;
int i;
if( (fp = popen(TCPDUMP_CMD, "r"))== NULL)
{
printf("popen() error!\n");
exit(1);
}
while(fgets(sensing_dump, sizeof(sensing_dump), fp))
{
sscanf(strstr(sensing_dump, "dBm ")-4, "%fdB", &rssi);
memset(src_mac, 0, sizeof(src_mac));
if(strstr(sensing_dump, "SA:")!=NULL)
{
sscanf(strstr(sensing_dump, "SA:")+3, "%s", src_mac);
}
else if(strstr(sensing_dump, "TA:")!=NULL)
{
sscanf(strstr(sensing_dump, "TA:")+3, "%s", src_mac);
}
else
{
printf("ERROR: SA and TA are not found\n");
}
for(i=0;i<strlen(src_mac);i++) if(islower(src_mac[i]))
{
src_mac[i]=toupper(src_mac[i]);
}
pthread_mutex_lock(&sensing_report_mutex);
//search old item
for(i=0;i<sensing_result_num;i++)
{
if(strcmp(sensing_result[i].src_mac, src_mac)==0)
{
//policy 1: keep the latest RSSI sample for all devices
new_rssi_mean = rssi;
new_rssi_variance = 0;
sensing_result[i].rssi_mean=new_rssi_mean;
sensing_result[i].rssi_variance=new_rssi_variance;
sensing_result[i].sample_num=1;
break;
}
}
//create new item if necessary
if(i==sensing_result_num)
{
memcpy(sensing_result[i].src_mac, src_mac, strlen(src_mac));
sensing_result[i].rssi_mean=rssi;
sensing_result[i].rssi_variance=0.0;
sensing_result[i].sample_num=1;
sensing_result_num++;
}
pthread_mutex_unlock(&sensing_report_mutex);
}
pclose(fp);
return;
}
void *report_func(void *parm)
{
int i;
char sensing_report[100];
char rcv_buf[100];
start:
sensing_socket = construct_link(server_ip, server_port);
while(1)
{
//format: [round] [observer MAC] [target MAC] [RSSI]
memset(rcv_buf, 0, sizeof(rcv_buf));
while(read(sensing_socket, rcv_buf, sizeof(rcv_buf))==0); //wait for polling
printf("%s is received\n", rcv_buf);
pthread_mutex_lock(&sensing_report_mutex);
for(i=0;i<sensing_result_num;i++)
{
printf("sensing_result[%d].src_mac=%s\n", i, sensing_result[i].src_mac);
if(1)
{
memset(sensing_report, 0, sizeof(sensing_report));
sprintf(sensing_report, "%s %s %s %f\n",
rcv_buf,
get_interface_MAC_string(),
sensing_result[i].src_mac,
sensing_result[i].rssi_mean);
printf("%s is sent\n", sensing_report);
if(write(sensing_socket, sensing_report, strlen(sensing_report))<=0)
{
printf("%s:socket write error\n", __FUNCTION__);
goto start;
}
}
}
memset(sensing_result, 0, sizeof(sensing_result));
sensing_result_num=0;
pthread_mutex_unlock(&sensing_report_mutex);
printf("\n");
}
}
void disconnection(int sig)
{
close(sensing_socket);
(void)signal(SIGINT, SIG_DFL);
}
static int construct_link(char ip[], int port)
{
int client_fd = -1;
struct hostent *host;
struct sockaddr_in client_sin;
while(client_fd <= 0)
{
if((host = gethostbyname(ip)) == NULL)
{
fprintf(stderr, "Can't get ip !!!\n");
return -1;
}
client_fd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&client_sin, sizeof(client_sin));
client_sin.sin_family = AF_INET;
client_sin.sin_addr = *((struct in_addr *)host->h_addr);
client_sin.sin_port = htons(port);
if(connect(client_fd, (struct sockaddr *)&client_sin, sizeof(client_sin)) == -1)
{
fprintf(stderr, "Can't connect to server \n");
fprintf(stdout, "Reconstruct link after 2 seconds \n");
client_fd = -1;
sleep(ReconnectionDelayTime);
}
}
return client_fd;
}
char* get_interface_MAC_string()
{
FILE *fp;
char cmd[100];
static char ret[50];
char line_buf[100];
char *cursor;
int mac[6];
memset(ret, 0, sizeof(ret));
sprintf(ret, "-1");
memset(cmd, 0, sizeof(cmd));
sprintf(cmd, "ifconfig %s", WLAN);
if((fp=popen(cmd, "r"))!=NULL)
{
while(!feof(fp))
{
memset(line_buf, 0, sizeof(line_buf));
fgets(line_buf, sizeof(line_buf), fp);
if( (cursor=search_pattern_following(line_buf, "HWaddr ")) != NULL )
{
sscanf(cursor, "%02X:%02X:%02X:%02X:%02X:%02X",
&mac[0], &mac[1], &mac[2],
&mac[3], &mac[4], &mac[5] );
memset(ret, 0, sizeof(ret));
sprintf(ret, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2],
mac[3], mac[4], mac[5] );
break;
}
}
pclose(fp);
}
else
{
printf("%s:Fail to open the file\n", __FUNCTION__);
}
return ret;
}
char* search_pattern_following(char* str1, char* str2)
{
char* ret;
if( (ret=strstr(str1, str2)) != NULL ) ret+=strlen(str2);
return ret;
}
編譯時指令為:
mips-openwrt-linux-gcc RSSI_client.c --static -lpthread -o RSSI_client
會出現許多警告,如下所示,不過不影響使用:
ofwrt@ofwrt-18:~/ofwrt_code$ mips-openwrt-linux-gcc RSSI_client.c --static -lpthread -o RSSI_client
RSSI_client_smartAnt.c: In function 'main':
RSSI_client_smartAnt.c:48:3: warning: 'return' with no value, in function returning non-void
return;
^~~~~~
RSSI_client_smartAnt.c:43:5: note: declared here
int main(int argc, char *argv[])
^~~~
RSSI_client_smartAnt.c:57:2: warning: implicit declaration of function 'pthread_mutex_init'; did you mean 'pthread_kill'? [-Wimplicit-function-declaration]
pthread_mutex_init(&sensing_report_mutex, NULL);
^~~~~~~~~~~~~~~~~~
pthread_kill
RSSI_client_smartAnt.c:58:2: warning: implicit declaration of function 'pthread_mutex_lock'; did you mean 'pthread_kill'? [-Wimplicit-function-declaration]
pthread_mutex_lock(&sensing_report_mutex);
^~~~~~~~~~~~~~~~~~
pthread_kill
RSSI_client_smartAnt.c:60:2: warning: implicit declaration of function 'pthread_create'; did you mean 'pthread_kill'? [-Wimplicit-function-declaration]
pthread_create(&sensing_thread, NULL, sensing_func, NULL);
^~~~~~~~~~~~~~
pthread_kill
RSSI_client_smartAnt.c:63:2: warning: implicit declaration of function 'pthread_mutex_unlock' [-Wimplicit-function-declaration]
pthread_mutex_unlock(&sensing_report_mutex);
^~~~~~~~~~~~~~~~~~~~
RSSI_client_smartAnt.c: In function 'sensing_func':
RSSI_client_smartAnt.c:108:37: warning: implicit declaration of function 'islower' [-Wimplicit-function-declaration]
for(i=0;i<strlen(src_mac);i++) if(islower(src_mac[i]))
^~~~~~~
RSSI_client_smartAnt.c:110:15: warning: implicit declaration of function 'toupper' [-Wimplicit-function-declaration]
src_mac[i]=toupper(src_mac[i]);
^~~~~~~
RSSI_client_smartAnt.c:167:2: warning: 'return' with no value, in function returning non-void
return;
^~~~~~
RSSI_client_smartAnt.c:72:7: note: declared here
void *sensing_func(void *parm)
^~~~~~~~~~~~
伺服器端程式
client 程式為執行在OpenWRT上的 C 程式,執行時需附上 server IP 與 port,即 ./{client程式名} {server IP} {server port}
。另外,在使用 CRTL+C 中止程式時,請用指令 ps 確認是否所有執行緒皆已中止,並用指令 kill 中止殘留多餘執行緒。若遇到無法使用CRTL+C 中止程式的情形時,重新建立一個新的 SSH 連線並用指令 kill 中止程式。
在 server 與 client 之間,我們依不同方向制定了二種溝通格式,參數之間以空白隔開、訊息之間以換行隔開。server 觸發 client 回報最新的 RSSI 時,會附上該次觸發的序號 (round)。為了讓client 能支援回報多個傳送端的封包 RSSI、並讓 server 能區分回報結果屬於哪回合,client 回報時會附上 server 傳來的序號 (round)、傳送端 (target MAC) 與RSSI (單位為dBm)。
方向
格式
server → client
[round]
client → server
[round] [observer MAC] [target MAC] [RSSI]
透過此格式設定,server 會定期要求 WiFi AP 回傳 RSSI 訊號,並顯示於螢幕上。
import java.io.*;
import java.net.*;
import java.util.*;
public class RSSI_Server
{
static public void main(String[] args) throws Exception
{
Server Server = new Server();
Server.start();
IdleHandler IdleHandler=new IdleHandler(Server);
IdleHandler.start();
//guard interval for access
Thread.sleep(1000);
int interval=0;
int round=0;
String FileName;
Scanner input=new Scanner(System.in);
while(true)
{
System.out.println("Enter parameters for sensing");
System.out.println("Enter sensing interval(msec):");
try
{
interval = Integer.valueOf(input.nextLine());
}
catch(NumberFormatException e)
{
System.out.println("Format ERROR!!");
continue;
}
System.out.println("Enter total sensing round:");
try
{
round = Integer.valueOf(input.nextLine());
}
catch(NumberFormatException e)
{
System.out.println("Format ERROR!!");
continue;
}
System.out.println("Enter FileName to save result:");
if( (FileName = input.nextLine())==null)
{
continue;
}
for(int i=0;i<round;i++)
{
Server.sendSensingCommandToAll(String.valueOf(i));
Thread.sleep(interval*3/4);
Server.getSensingResultFromAll(FileName);
Thread.sleep(interval/4);
System.out.println(interval+"msec passed");
}
IdleHandler.clearWatchDogCount();
}
}
}
class Server extends Thread
{
List<Client> users;
public void run()
{
users = new ArrayList<Client>();
System.out.println("server start.");
try
{
ServerSocket ss = new ServerSocket(2223);
while(true)
{
Socket s = ss.accept(); //listen 2223
Client u = new Client(s, users);
users.add(u);
u.start();
System.out.println(users.size() +" clients are connected");
}
}
catch (IOException e)
{
e.printStackTrace();
System.out.println("server exception while listening.");
}
}
public void sendSensingCommandToAll(String cmd)
{
for(Client u:users)
{
u.sendSensingCommand(cmd);
}
}
public void getSensingResultFromAll(String FileName)
{
try
{
BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter(FileName, true));
for(Client u:users)
{
String result = u.getSensingResult();
if(result!=null)
{
bufferedWriter.write(result+'\n');
}
}
bufferedWriter.write('\n');
bufferedWriter.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class Client extends Thread
{
Socket UserSocket;
List<Client> users;
String message;
Client(Socket s, List<Client> list)
{
UserSocket=s;
users=list;
}
public void run()
{
//server-to-client format: [round]
//client-to-server format: [round] [observer MAC] [target MAC] [RSSI]
try
{
BufferedReader in = new BufferedReader(new InputStreamReader(UserSocket.getInputStream()));
while( (message=in.readLine())!=null)
{
System.out.println(message);
}
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("an user is leaving");
return;
}
}
public void sendSensingCommand(String cmd)
{
try
{
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(UserSocket.getOutputStream()));
out.write(cmd);
out.flush();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public String getSensingResult()
{
String ret=null;
if(message!=null)
{
ret = message;
message = null;
}
return ret;
}
}
class IdleHandler extends Thread
{
int WatchDogCount=0;
Server Server;
IdleHandler(Server s)
{
Server = s;
}
public void run()
{
while(true)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
WatchDogCount++;
if(WatchDogCount>=60)
{
Server.sendSensingCommandToAll(String.valueOf(-1));
WatchDogCount=0;
}
}
}
void clearWatchDogCount()
{
WatchDogCount=0;
}
}
Last updated