wifi_scanner.py

Download
python 513 lines 15.8 KB
  1#!/usr/bin/env python3
  2"""
  3WiFi ๋„คํŠธ์›Œํฌ ์Šค์บ๋„ˆ ๋ฐ ์†Œ์ผ“ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์˜ˆ์ œ
  4
  5์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹ค์Œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:
  61. WiFi ๋„คํŠธ์›Œํฌ ์Šค์บ” (์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ชจ๋“œ ์ง€์›)
  72. TCP ํด๋ผ์ด์–ธํŠธ/์„œ๋ฒ„ ์˜ˆ์ œ
  83. HTTP ํด๋ผ์ด์–ธํŠธ๋กœ ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ „์†ก
  94. ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง
 105. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฐ ์žฌ์‹œ๋„ ๋กœ์ง
 11
 12์ฐธ๊ณ : content/ko/IoT_Embedded/04_WiFi_Networking.md
 13"""
 14
 15import subprocess
 16import re
 17import socket
 18import json
 19import time
 20import random
 21import sys
 22from typing import Optional, List, Dict
 23from datetime import datetime
 24
 25
 26# ============================================================================
 27# WiFi ๋„คํŠธ์›Œํฌ ์Šค์บ”
 28# ============================================================================
 29
 30def is_raspberry_pi() -> bool:
 31    """๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ํ™˜๊ฒฝ์ธ์ง€ ํ™•์ธ"""
 32    try:
 33        with open('/proc/cpuinfo', 'r') as f:
 34            cpuinfo = f.read()
 35            return 'BCM' in cpuinfo or 'Raspberry' in cpuinfo
 36    except FileNotFoundError:
 37        return False
 38
 39
 40def get_wifi_info() -> dict:
 41    """
 42    ํ˜„์žฌ WiFi ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ
 43
 44    Returns:
 45        dict: WiFi ์ •๋ณด (ssid, ip_addresses, signal_dbm, mac_address)
 46    """
 47    info = {}
 48
 49    if not is_raspberry_pi():
 50        # ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ชจ๋“œ
 51        print("[์‹œ๋ฎฌ๋ ˆ์ด์…˜] ์‹ค์ œ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ")
 52        info = {
 53            'ssid': 'SimulatedWiFi',
 54            'ip_addresses': ['192.168.1.100'],
 55            'signal_dbm': -45,
 56            'mac_address': 'AA:BB:CC:DD:EE:FF'
 57        }
 58        return info
 59
 60    try:
 61        # SSID ์กฐํšŒ
 62        result = subprocess.run(
 63            ['iwgetid', '-r'],
 64            capture_output=True,
 65            text=True,
 66            timeout=5
 67        )
 68        info['ssid'] = result.stdout.strip()
 69
 70        # IP ์ฃผ์†Œ ์กฐํšŒ
 71        result = subprocess.run(
 72            ['hostname', '-I'],
 73            capture_output=True,
 74            text=True,
 75            timeout=5
 76        )
 77        ips = result.stdout.strip().split()
 78        info['ip_addresses'] = ips
 79
 80        # ์‹ ํ˜ธ ๊ฐ•๋„ ์กฐํšŒ
 81        result = subprocess.run(
 82            ['iwconfig', 'wlan0'],
 83            capture_output=True,
 84            text=True,
 85            timeout=5
 86        )
 87        match = re.search(r'Signal level=(-?\d+)', result.stdout)
 88        if match:
 89            info['signal_dbm'] = int(match.group(1))
 90
 91        # MAC ์ฃผ์†Œ
 92        result = subprocess.run(
 93            ['cat', '/sys/class/net/wlan0/address'],
 94            capture_output=True,
 95            text=True,
 96            timeout=5
 97        )
 98        info['mac_address'] = result.stdout.strip()
 99
100    except Exception as e:
101        info['error'] = str(e)
102
103    return info
104
105
106def get_wifi_networks() -> List[dict]:
107    """
108    ์ฃผ๋ณ€ WiFi ๋„คํŠธ์›Œํฌ ์Šค์บ”
109
110    Returns:
111        list: ๋„คํŠธ์›Œํฌ ์ •๋ณด ๋ฆฌ์ŠคํŠธ (ssid, quality, signal_dbm)
112    """
113    networks = []
114
115    if not is_raspberry_pi():
116        # ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ชจ๋“œ
117        print("[์‹œ๋ฎฌ๋ ˆ์ด์…˜] WiFi ์Šค์บ” ๊ฒฐ๊ณผ ์ƒ์„ฑ ์ค‘...")
118        networks = [
119            {'ssid': 'HomeNetwork', 'quality': '70/70', 'signal_dbm': -35},
120            {'ssid': 'OfficeWiFi', 'quality': '65/70', 'signal_dbm': -42},
121            {'ssid': 'GuestNetwork', 'quality': '50/70', 'signal_dbm': -58},
122            {'ssid': 'Neighbor_2.4G', 'quality': '40/70', 'signal_dbm': -65},
123            {'ssid': 'CafeWiFi', 'quality': '30/70', 'signal_dbm': -75},
124        ]
125        return networks
126
127    try:
128        result = subprocess.run(
129            ['sudo', 'iwlist', 'wlan0', 'scan'],
130            capture_output=True,
131            text=True,
132            timeout=10
133        )
134
135        current_network = {}
136        for line in result.stdout.split('\n'):
137            if 'ESSID:' in line:
138                ssid = re.search(r'ESSID:"(.+)"', line)
139                if ssid and current_network:
140                    networks.append(current_network)
141                current_network = {'ssid': ssid.group(1) if ssid else ''}
142
143            elif 'Quality=' in line:
144                quality = re.search(r'Quality=(\d+)/(\d+)', line)
145                if quality:
146                    current_network['quality'] = f"{quality.group(1)}/{quality.group(2)}"
147
148                signal = re.search(r'Signal level=(-?\d+)', line)
149                if signal:
150                    current_network['signal_dbm'] = int(signal.group(1))
151
152        if current_network:
153            networks.append(current_network)
154
155    except Exception as e:
156        print(f"์Šค์บ” ์‹คํŒจ: {e}")
157        print("์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค...")
158        return get_wifi_networks()  # ์žฌ๊ท€ ํ˜ธ์ถœ๋กœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ชจ๋“œ ์‚ฌ์šฉ
159
160    return networks
161
162
163def monitor_wifi_signal(threshold_dbm: int = -70, interval: int = 5, duration: int = 30):
164    """
165    WiFi ์‹ ํ˜ธ ๊ฐ•๋„ ๋ชจ๋‹ˆํ„ฐ๋ง
166
167    Args:
168        threshold_dbm: ๊ฒฝ๊ณ  ์ž„๊ณ„๊ฐ’ (dBm)
169        interval: ํ™•์ธ ๊ฐ„๊ฒฉ (์ดˆ)
170        duration: ๋ชจ๋‹ˆํ„ฐ๋ง ๊ธฐ๊ฐ„ (์ดˆ)
171    """
172    print(f"=== WiFi ์‹ ํ˜ธ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์ž‘ ===")
173    print(f"๊ฒฝ๊ณ  ์ž„๊ณ„๊ฐ’: {threshold_dbm} dBm")
174    print(f"ํ™•์ธ ๊ฐ„๊ฒฉ: {interval}์ดˆ, ์ด {duration}์ดˆ")
175    print()
176
177    start_time = time.time()
178
179    while time.time() - start_time < duration:
180        info = get_wifi_info()
181
182        if 'error' in info:
183            print(f"[{datetime.now().strftime('%H:%M:%S')}] ์˜ค๋ฅ˜: {info['error']}")
184        else:
185            signal = info.get('signal_dbm', 0)
186            ssid = info.get('ssid', 'Unknown')
187
188            status = "์–‘ํ˜ธ" if signal > threshold_dbm else "โš ๏ธ  ๊ฒฝ๊ณ "
189            print(f"[{datetime.now().strftime('%H:%M:%S')}] {ssid}: {signal} dBm ({status})")
190
191            if signal <= threshold_dbm:
192                print(f"  โš ๏ธ  ์‹ ํ˜ธ๊ฐ€ ์•ฝํ•ฉ๋‹ˆ๋‹ค! ({signal} dBm <= {threshold_dbm} dBm)")
193
194        time.sleep(interval)
195
196    print("\n๋ชจ๋‹ˆํ„ฐ๋ง ์ข…๋ฃŒ")
197
198
199# ============================================================================
200# TCP ์†Œ์ผ“ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
201# ============================================================================
202
203def tcp_server(host: str = '0.0.0.0', port: int = 9999):
204    """
205    TCP ์„œ๋ฒ„ - ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 
206
207    Args:
208        host: ๋ฐ”์ธ๋“œํ•  IP ์ฃผ์†Œ
209        port: ํฌํŠธ ๋ฒˆํ˜ธ
210    """
211    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
212        # ์ฃผ์†Œ ์žฌ์‚ฌ์šฉ ํ—ˆ์šฉ
213        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
214
215        server.bind((host, port))
216        server.listen(5)
217
218        print(f"TCP ์„œ๋ฒ„ ์‹œ์ž‘: {host}:{port}")
219        print("ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ๋Œ€๊ธฐ ์ค‘... (Ctrl+C๋กœ ์ข…๋ฃŒ)")
220
221        try:
222            while True:
223                client, address = server.accept()
224                print(f"\nํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ: {address}")
225
226                with client:
227                    while True:
228                        data = client.recv(1024)
229                        if not data:
230                            break
231
232                        try:
233                            # JSON ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ
234                            message = json.loads(data.decode('utf-8'))
235                            timestamp = datetime.now().strftime("%H:%M:%S")
236                            print(f"[{timestamp}] ์ˆ˜์‹ : {message}")
237
238                            # ์‘๋‹ต ์ „์†ก
239                            response = {
240                                "status": "ok",
241                                "received": message.get("sensor_id"),
242                                "timestamp": time.time()
243                            }
244                            client.sendall(json.dumps(response).encode('utf-8'))
245
246                        except json.JSONDecodeError:
247                            print(f"์ž˜๋ชป๋œ JSON: {data}")
248
249                print(f"ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์ข…๋ฃŒ: {address}")
250
251        except KeyboardInterrupt:
252            print("\n์„œ๋ฒ„ ์ข…๋ฃŒ")
253
254
255def tcp_client(server_host: str, server_port: int = 9999, count: int = 10):
256    """
257    TCP ํด๋ผ์ด์–ธํŠธ - ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ „์†ก
258
259    Args:
260        server_host: ์„œ๋ฒ„ IP ์ฃผ์†Œ
261        server_port: ์„œ๋ฒ„ ํฌํŠธ
262        count: ์ „์†ก ํšŸ์ˆ˜
263    """
264    try:
265        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
266            print(f"์„œ๋ฒ„ ์—ฐ๊ฒฐ ์ค‘: {server_host}:{server_port}")
267            client.connect((server_host, server_port))
268            print(f"์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต!")
269
270            sensor_id = "temp_sensor_01"
271
272            for i in range(count):
273                # ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ (์‹œ๋ฎฌ๋ ˆ์ด์…˜)
274                data = {
275                    "sensor_id": sensor_id,
276                    "temperature": round(random.uniform(20, 30), 1),
277                    "humidity": round(random.uniform(40, 70), 1),
278                    "timestamp": time.time(),
279                    "sequence": i + 1
280                }
281
282                # ์ „์†ก
283                message = json.dumps(data).encode('utf-8')
284                client.sendall(message)
285                print(f"[{i+1}/{count}] ์ „์†ก: ์˜จ๋„={data['temperature']}ยฐC, ์Šต๋„={data['humidity']}%")
286
287                # ์‘๋‹ต ์ˆ˜์‹ 
288                try:
289                    client.settimeout(5.0)
290                    response = client.recv(1024)
291                    if response:
292                        resp_data = json.loads(response.decode('utf-8'))
293                        print(f"         ์‘๋‹ต: {resp_data['status']}")
294                except socket.timeout:
295                    print(f"         ์‘๋‹ต ํƒ€์ž„์•„์›ƒ")
296
297                if i < count - 1:
298                    time.sleep(2)
299
300            print("\n์ „์†ก ์™„๋ฃŒ")
301
302    except ConnectionRefusedError:
303        print(f"์˜ค๋ฅ˜: ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค ({server_host}:{server_port})")
304        print("์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ์ค‘์ธ์ง€ ํ™•์ธํ•˜์„ธ์š”.")
305    except Exception as e:
306        print(f"์˜ค๋ฅ˜: {e}")
307
308
309# ============================================================================
310# HTTP ํด๋ผ์ด์–ธํŠธ
311# ============================================================================
312
313def send_sensor_data_http(api_base: str, sensor_id: str, data: dict) -> bool:
314    """
315    HTTP POST๋กœ ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ „์†ก
316
317    Args:
318        api_base: API ๋ฒ ์ด์Šค URL
319        sensor_id: ์„ผ์„œ ID
320        data: ์ „์†กํ•  ๋ฐ์ดํ„ฐ
321
322    Returns:
323        bool: ์„ฑ๊ณต ์—ฌ๋ถ€
324    """
325    try:
326        import requests
327    except ImportError:
328        print("requests ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค: pip install requests")
329        return False
330
331    url = f"{api_base}/sensors/{sensor_id}/data"
332
333    try:
334        response = requests.post(
335            url,
336            json=data,
337            headers={"Content-Type": "application/json"},
338            timeout=5
339        )
340
341        if response.status_code in (200, 201):
342            print(f"๋ฐ์ดํ„ฐ ์ „์†ก ์„ฑ๊ณต: {data}")
343            return True
344        else:
345            print(f"์ „์†ก ์‹คํŒจ: {response.status_code} - {response.text}")
346            return False
347
348    except requests.exceptions.RequestException as e:
349        print(f"๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜: {e}")
350        return False
351
352
353def http_client_with_retry(api_base: str, sensor_id: str, retries: int = 3):
354    """
355    ์žฌ์‹œ๋„ ๋กœ์ง์ด ์žˆ๋Š” HTTP ํด๋ผ์ด์–ธํŠธ
356
357    Args:
358        api_base: API ๋ฒ ์ด์Šค URL
359        sensor_id: ์„ผ์„œ ID
360        retries: ์žฌ์‹œ๋„ ํšŸ์ˆ˜
361    """
362    print(f"=== HTTP ํด๋ผ์ด์–ธํŠธ (์žฌ์‹œ๋„: {retries}ํšŒ) ===")
363    print(f"API: {api_base}")
364    print(f"์„ผ์„œ ID: {sensor_id}\n")
365
366    for i in range(10):
367        data = {
368            "temperature": round(random.uniform(20, 30), 1),
369            "humidity": round(random.uniform(40, 70), 1),
370            "timestamp": int(time.time())
371        }
372
373        success = False
374        for attempt in range(retries):
375            if send_sensor_data_http(api_base, sensor_id, data):
376                success = True
377                break
378            else:
379                if attempt < retries - 1:
380                    wait_time = 2 ** attempt  # Exponential backoff
381                    print(f"  ์žฌ์‹œ๋„ ๋Œ€๊ธฐ: {wait_time}์ดˆ...")
382                    time.sleep(wait_time)
383
384        if not success:
385            print(f"  โŒ {retries}๋ฒˆ ์žฌ์‹œ๋„ ํ›„ ์‹คํŒจ")
386
387        time.sleep(5)
388
389
390# ============================================================================
391# ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ
392# ============================================================================
393
394def check_internet_connection(host: str = "8.8.8.8", port: int = 53, timeout: int = 3) -> bool:
395    """
396    ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ ํ™•์ธ
397
398    Args:
399        host: ํ…Œ์ŠคํŠธํ•  ํ˜ธ์ŠคํŠธ (๊ธฐ๋ณธ: Google DNS)
400        port: ํฌํŠธ
401        timeout: ํƒ€์ž„์•„์›ƒ (์ดˆ)
402
403    Returns:
404        bool: ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ ์—ฌ๋ถ€
405    """
406    try:
407        socket.setdefaulttimeout(timeout)
408        socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
409        return True
410    except socket.error:
411        return False
412
413
414def get_local_ip() -> str:
415    """
416    ๋กœ์ปฌ IP ์ฃผ์†Œ ์กฐํšŒ
417
418    Returns:
419        str: IP ์ฃผ์†Œ
420    """
421    try:
422        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
423        s.connect(('8.8.8.8', 80))
424        ip = s.getsockname()[0]
425        s.close()
426        return ip
427    except Exception:
428        return "127.0.0.1"
429
430
431def network_status():
432    """๋„คํŠธ์›Œํฌ ์ƒํƒœ ํ™•์ธ"""
433    print("=== ๋„คํŠธ์›Œํฌ ์ƒํƒœ ===\n")
434
435    # ๋กœ์ปฌ IP
436    local_ip = get_local_ip()
437    print(f"๋กœ์ปฌ IP: {local_ip}")
438
439    # ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ
440    internet = check_internet_connection()
441    print(f"์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ: {'โœ“ ์—ฐ๊ฒฐ๋จ' if internet else 'โœ— ์—ฐ๊ฒฐ ์•ˆ๋จ'}")
442
443    # WiFi ์ •๋ณด
444    wifi_info = get_wifi_info()
445    if 'error' not in wifi_info:
446        print(f"\nWiFi SSID: {wifi_info.get('ssid', 'N/A')}")
447        print(f"์‹ ํ˜ธ ๊ฐ•๋„: {wifi_info.get('signal_dbm', 'N/A')} dBm")
448        print(f"MAC ์ฃผ์†Œ: {wifi_info.get('mac_address', 'N/A')}")
449
450
451# ============================================================================
452# ๋ฉ”์ธ ํ•จ์ˆ˜
453# ============================================================================
454
455def main():
456    """๋ฉ”์ธ ํ•จ์ˆ˜"""
457    if len(sys.argv) < 2:
458        print("WiFi ๋„คํŠธ์›Œํฌ ์Šค์บ๋„ˆ ๋ฐ ์†Œ์ผ“ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์˜ˆ์ œ")
459        print("\n์‚ฌ์šฉ๋ฒ•:")
460        print("  python wifi_scanner.py info         - WiFi ์ •๋ณด ์กฐํšŒ")
461        print("  python wifi_scanner.py scan         - WiFi ๋„คํŠธ์›Œํฌ ์Šค์บ”")
462        print("  python wifi_scanner.py monitor      - WiFi ์‹ ํ˜ธ ๋ชจ๋‹ˆํ„ฐ๋ง")
463        print("  python wifi_scanner.py status       - ๋„คํŠธ์›Œํฌ ์ƒํƒœ ํ™•์ธ")
464        print("  python wifi_scanner.py server       - TCP ์„œ๋ฒ„ ์‹œ์ž‘")
465        print("  python wifi_scanner.py client <IP>  - TCP ํด๋ผ์ด์–ธํŠธ ์‹œ์ž‘")
466        print("\n์˜ˆ์ œ:")
467        print("  python wifi_scanner.py info")
468        print("  python wifi_scanner.py client 192.168.1.100")
469        return
470
471    command = sys.argv[1].lower()
472
473    if command == 'info':
474        print("=== WiFi ์—ฐ๊ฒฐ ์ •๋ณด ===")
475        info = get_wifi_info()
476        for key, value in info.items():
477            print(f"  {key}: {value}")
478
479    elif command == 'scan':
480        print("=== ์ฃผ๋ณ€ WiFi ๋„คํŠธ์›Œํฌ ===")
481        networks = get_wifi_networks()
482        print(f"๋ฐœ๊ฒฌ๋œ ๋„คํŠธ์›Œํฌ: {len(networks)}๊ฐœ\n")
483        for net in networks:
484            ssid = net.get('ssid', 'Unknown')
485            signal = net.get('signal_dbm', 'N/A')
486            quality = net.get('quality', 'N/A')
487            print(f"  {ssid:20} - ์‹ ํ˜ธ: {signal:4} dBm, ํ’ˆ์งˆ: {quality}")
488
489    elif command == 'monitor':
490        monitor_wifi_signal(threshold_dbm=-70, interval=5, duration=30)
491
492    elif command == 'status':
493        network_status()
494
495    elif command == 'server':
496        tcp_server()
497
498    elif command == 'client':
499        if len(sys.argv) < 3:
500            print("์˜ค๋ฅ˜: ์„œ๋ฒ„ IP ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”")
501            print("์˜ˆ์ œ: python wifi_scanner.py client 192.168.1.100")
502            return
503        server_ip = sys.argv[2]
504        tcp_client(server_ip, count=10)
505
506    else:
507        print(f"์•Œ ์ˆ˜ ์—†๋Š” ๋ช…๋ น: {command}")
508        print("'python wifi_scanner.py'๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋„์›€๋ง์„ ํ™•์ธํ•˜์„ธ์š”")
509
510
511if __name__ == "__main__":
512    main()