Skip to content

About

rpi4 - TCP를 이용한 원격 장치 제어 프로그램

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TCP 기반 원격 장치 제어 시스템

1. 프로젝트 개요

1.1 프로젝트 소개

본 프로젝트는 TCP/IP 소켓 통신을 기반으로 원격지에 있는 임베디드 장치(라즈베리파이 4)에 연결된 다양한 하드웨어 모듈을 제어하고 모니터링하는 시스템입니다.

다중 클라이언트 접속을 효율적으로 처리하기 위해 멀티스레드 기반으로 설계되었으며, 하드웨어 제어 로직을 독립적인 동적 라이브러리(.so) 형태로 구성하여 유지보수성과 확장성을 극대화하였습니다. 또한 텍스트 기반 리눅스 클라이언트뿐만 아니라, 사용자 편의성을 고려한 웹 클라이언트 인터페이스를 추가로 제공합니다.

1.2 시스템 구성 및 개발 환경

  • 서버
    • 하드웨어: Raspberry Pi 4
    • 운영체제: Linux (Kernel: 6.18.29+rpt-rpi-v8)
    • 실행 형태: 시스템 백그라운드에서 상시 동작하는 데몬 프로세스
  • 리눅스 클라이언트
    • 운영체제: Ubuntu 26.04 (Kernel: 7.0.0-15-generic)
    • 특징: 프로그램 실행 중 예기치 않은 종료를 방지하기 위해 SIGINT를 제외한 모든 시그널을 블로킹 처리
  • 웹 클라이언트
    • 특징: 사용자가 웹 브라우저를 통해 편리하게 장치를 제어할 수 있도록 추가 구현된 인터페이스
  • 제어 대상 장치 (Target Devices)
    • LED: 원격 On/Off 제어 및 3단계 밝기 조절(최대, 중간, 최저)
    • 부저: 경고음 및 음악 소리 On/Off 제어
    • 조도 센서: 주변 밝기 실시간 값 확인 및 조도에 따른 LED 자동 제어 연동
    • 7 세그먼트: 타이머 숫자(0~9) 표시 및 카운트다운 기능, 0 도달 시 부저 연동
  • 빌드 시스템: Makefile을 통한 빌드 자동화 구성

2. 개발 일정

  • 개발 기간: 2026-06-02 ~ 2026-06-05 (총 4일)

프로젝트는 위 기간 동안 요구사항 정의부터 통합 테스트 및 배포까지 단계별로 수행됩니다.

일차 단계 세부 개발 활동 내용
1일차 요구사항 정의 & 환경 구축 - 라즈베리파이4 및 Ubuntu 환경 구축
- TCP/IP 소켓 통신 프로토콜 정의
- 개발 환경에 맞는 Makefile 뼈대 구성 및 빌드 자동화 준비
2일차 서버 데몬 & 동적 라이브러리 개발 - 서버의 데몬 프로세스(Daemon) 구현
- 하드웨어 제어 모듈(LED, 부저, 조도센서, 7세그먼트)의 동적 라이브러리(.so) 설계 및 구현
- 멀티스레드 기반의 장치 제어 흐름 설계
3일차 클라이언트 구현 & 시그널 처리 - 리눅스(Ubuntu) 환경의 TCP 클라이언트 기능 구현
- 클라이언트 실행 중 SIGINT 외 타 시그널 블록킹 구조 설계
- 타이머 연동(7세그먼트 카운트다운 및 부저 트리거) 로직 검증
- 웹 클라이언트 추가 인터페이스 구현 및 서버 연동
4일차 통합 테스트 및 디버깅 - 서버-클라이언트-하드웨어 간 전체 연동 테스트
- 예외 상황 처리(소켓 연결 끊김, 센서 오작동 등) 및 안정성 검증
- 최종 산출물 정리 및 문서 작성 완료

3. 실행 방법

프로젝트 최상위 디렉토리에서 스크립트를 통해 빌드 및 배포를 진행한 후, 클라이언트 프로그램을 실행하여 서버에 접속합니다.

⚠️ 주의 사항: scp.sh 스크립트를 실행하기 전에 파일 내부의 서버 접속 정보를 본인의 라즈베리파이 환경에 맞게 반드시 수정해야 합니다.

  • scp.sh 수정 예시:
#!/bin/bash

make
# 아래의 사용자명(mangjun), IP(172.20.33.117), 홈 디렉터리 경로를 본인 환경에 맞춰 변경하세요.
scp ./bin/librpi.so mangjun@172.20.33.117:/home/mangjun/
scp ./bin/index.html mangjun@172.20.33.117:/home/mangjun/
scp ./bin/server mangjun@172.20.33.117:/home/mangjun/

환경에 맞게 스크립트를 수정했다면, 아래 명령어를 순서대로 실행합니다.

# 1. 자동 빌드 및 서버 배포 스크립트 실행 (프로젝트 최상위 폴더)
./scp.sh

# 2. 홈 디렉터리에서 서버 실행 (rpi4) 
./server

# 3. 프로젝트 루트 디렉터리에서 클라이언트 실행 (Linux)
./bin/client <서버_IP> <서버_PORT>

4. 세부 구현 내용

4.1 폴더 구조

.
├── Makefile             # 전체 프로젝트(client, device, server) 통합 빌드 자동화 스크립트
├── README.md            # 프로젝트 개요, 개발 일정, 트러블슈팅 등 통합 문서
├── bin                  # 빌드된 최종 실행 파일(client, server)과 라이브러리(librpi.so)가 모이는 폴더
│   └── index.html       # 웹 페이지
├── client               # 텍스트 기반 리눅스 TCP 클라이언트 프로그램
│   ├── Makefile         # 클라이언트 전용 빌드 스크립트
│   ├── include
│   │   ├── menu.h       # 사용자 인터페이스(CLI 메뉴) 관련 헤더
│   │   └── setup.h      # 클라이언트 설정 관련 헤더
│   └── src
│       ├── main.c       # 클라이언트 프로그램 진입점 (main 함수)
│       ├── menu.c       # 콘솔 UI 출력 및 사용자 입력(target/action) 파싱 로직
│       └── setup.c      # 서버 연결 및 시그널 설정
├── device               # 하드웨어 제어 라이브러리 (서버와 독립적으로 분리된 모듈)
│   ├── Makefile         # librpi.so 동적 공유 라이브러리 빌드 스크립트
│   ├── buzzer.c         # 부저 제어 및 상태 갱신 로직
│   ├── cds.c            # 조도 센서(I2C) 제어
│   ├── led.c            # LED 제어 (소프트웨어 PWM 활용)
│   └── segment.c        # 7-세그먼트 타이머 출력 및 0 도달 시 부저 연동 로직
├── scp.sh               # 빌드 완료 후 라즈베리파이 원격 서버로 파일 자동 배포 스크립트
└── server               # epoll 기반 고성능 비동기 TCP/HTTP 제어 서버
    ├── Makefile         # 서버 전용 빌드 스크립트
    ├── include
    │   ├── core.h       # 서버 네트워크 뼈대(소켓, epoll 이벤트 루프, 데몬화) 헤더
    │   ├── handler.h    # 클라이언트 명령 파싱 및 하드웨어 디스패처 헤더
    │   ├── list.h       # 브로드캐스트용 클라이언트 세션 관리를 위한 이중 연결 리스트 헤더
    │   ├── state.h      # 하드웨어 통합 상태 구조체 설계도 및 extern 선언부
    │   └── thread.h     # 동적 라이브러리 로드 및 워커 스레드 관리 헤더
    └── src
        ├── core.c       # 백그라운드 데몬 전환, epoll 비동기 이벤트 루프 및 TCP/HTTP 세션 통합 처리부
        ├── handler.c    # 수신 문자열 분기 처리 및 하드웨어 워커 스레드 생성(상태 선반영) 로직
        ├── list.c       # 세션(Session) 삽입/삭제를 O(1)로 처리하기 위한 이중 연결 리스트 구현부
        ├── server.c     # 서버 프로그램 진입점 (main 함수, 소켓 초기화 및 루프 실행)
        ├── state.c      # 전역 상태(g_state) 메모리 실제 할당 및 초기값 세팅
        └── thread.c     # dlopen을 이용한 librpi.so 동적 로드 및 하드웨어 제어 함수 비동기 실행 로직

4.2 시스템 아키텍처 및 통신 흐름

  • 비동기 이벤트 루프(core.c) 초기 프로토타입에서는 클라이언트가 접속할 때마다 새로운 스레드를 생성하는 멀티 스레드 방식을 사용했습니다. 하지만 제어 명령을 내리는 TCP 클라이언트와 상태를 모니터링하는 HTTP 웹 클라이언트가 동시에 다수 접속할 경우, 잦은 Context Switching으로 인해 라즈베리파이 환경에서 시스템 오버헤드가 급증하는 문제가 있었습니다. 이를 해결하기 위해 멀티 스레드 네트워크 수신 구조를 과감히 제거하고, 리눅스의 고성능 I/O 다중화 기법인 epoll을 도입하여 싱글 스레드 비동기 이벤트 루프로 서버 코어를 전면 리팩터링했습니다.
while (1) {
    if ((event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1)) == -1) {
        continue;
    }

    for (i = 0; i < event_count; i++) {
        if (events[i].data.fd == tcp_sockfd) // TCP 연결 요청 처리
        else if (events[i].data.fd == http_sockfd) // HTTP 연결 요청 처리
        else // TCP / HTTP 수신 처리
    }
}
  • SSE 아키텍처 SSE는 서버에서 클라이언트로의 단방향 통신에 특화된 브라우저 네이티브 기술입니다. Content-Type: text/event-stream 응답 헤더만으로 기존 HTTP 인프라 위에서 손쉽게 지속 연결을 유지할 수 있어, 현재의 시스템 구조에 훨씬 가볍고 적합한 아키텍처라고 판단했습니다.

4.3 전역 상태 관리 및 멀티스레드 동기화

  • 상태 중앙 집중화

    • 설계 배경 및 문제 인식 다양한 하드웨어(LED, 부저, 세그먼트 등)를 제어하는 동적 라이브러리(.so)를 비동기 스레드로 실행할 때, 초기에는 각 모듈마다 전달해야 하는 매개변수의 형태가 달라 스레드 생성 코드의 파편화가 발생했습니다. 또한, 웹 클라이언트 환경이 추가되면서 전체 장치의 현재 상태를 한 번에 JSON 형태로 브로드캐스트해야 했는데, 개별 스레드나 모듈에서 상태를 각자 관리할 경우 데이터를 취합하는 과정에서 병목과 동기화 문제가 발생할 것이 자명했습니다.
    • 해결 방안 이를 해결하기 위해 시스템의 모든 하드웨어 상태와 제어 플래그를 캡슐화한 전역 구조체 device_state를 설계하여, 서버 내의 단일 진실 공급원으로 삼았습니다.
      • 인터페이스 통일 및 모듈화: 모든 하드웨어 제어 워커 스레드가 device_state 포인터 하나만을 인자로 받도록 시그니처를 통일했습니다. 덕분에 dlopen으로 동적 라이브러리를 로드하고 스레드를 생성하는 중복 코드를 제거하고, 하나의 깔끔한 범용 헬퍼 함수로 추상화하여 코드의 가독성과 확장성을 극대화했습니다. 새로운 하드웨어 모듈이 추가되더라도 메인 서버 로직의 수정 없이 라이브러리만 추가하면 되는 구조를 완성했습니다.
      • 데이터 파이프라인 최적화: 웹 서버 로직이 클라이언트들에게 상태를 전송할 때, 여러 스레드를 순회하며 데이터를 수집할 필요가 없어졌습니다. 오직 device_state 구조체 하나만 읽어 JSON으로 직렬화한 뒤 전송하면 되므로, 실시간 브로드캐스트 아키텍처가 극도로 단순해지고 응답 속도가 향상되었습니다.
  • 멀티스레드 동기화 및 안정성 확보

    • 경쟁 상태 방지 비동기 네트워크 이벤트 루프와 여러 하드웨어 제어 워커 스레드가 전역 구조체인 g_state에 동시에 접근하여 값을 읽고 쓰는 환경에서는 데이터 오염이 발생할 위험이 높습니다. 이를 차단하기 위해 상태 변경 시 pthread_mutex를 도입하여 모든 데이터 갱신 로직을 스레드 안전하게 보호했습니다.
    • 임계 구역 최소화 및 I/O 블로킹 방어 로직 단순히 락을 거는 것에 그치지 않고, 시스템 전체의 퍼포먼스 저하와 잠재적인 교착 상태를 예방하기 위해 상태 갱신과 네트워크 전송 시점을 완벽히 분리하는 플래그 패턴을 설계했습니다.
// state.c: 부저 상태 업데이트 및 브로드캐스트 트리거 로직
void update_buzzer_state(const int new_state)
{
    int changed = 0;

    // 1. 임계 구역(Critical Section): 순수 메모리 갱신만 수행하여 락 점유 시간 최소화
    pthread_mutex_lock(&mutex);
    if (g_state.buzzer_on != new_state) {
        g_state.buzzer_on = new_state;
        changed = 1;    // 상태 변경 플래그 활성화
    }
    pthread_mutex_unlock(&mutex);
    // 임계 구역 종료

    // 2. 락 해제 후 안전한 상태에서 네트워크 I/O(브로드캐스트) 수행
    if (changed) {
        broadcast_sse();
    }
}

4.4 네트워크 예외 처리 및 방어적 프로그래밍

  • 이중 연결 리스트를 활용한 O(1) 세션 관리 epoll 기반의 싱글 스레드 서버에서는 특정 클라이언트의 연결이 종료되었을 때, 세션 리스트에서 해당 클라이언트를 빠르게 제거하고 자원을 반환해야 합니다.
    • 문제점: 배열이나 단일 연결 리스트를 사용할 경우, 삭제할 세션을 찾기 위해 전체 리스트를 순회해야 하므로 $O(N)$의 시간 복잡도가 발생하여 서버의 이벤트 루프에 병목을 유발할 수 있습니다.
    • 해결 방안: 클라이언트 세션 관리에 이중 연결 리스트 자료구조를 도입했습니다. 종료 요청이 감지되면, 해당 노드의 이전/다음 포인터만 재연결하여 $O(1)$의 시간 복잡도로 즉각적인 노드 삭제 및 메모리 반환(free)이 이루어지도록 최적화하여 메모리 누수와 좀비 소켓을 원천 차단했습니다.

5. 문제점 및 보완 사항

5.1 하드웨어 이슈: 7세그먼트 모듈 핀 매핑 오류

  • 문제 현상: 1일차 단위 테스트 도중 7세그먼트 디스플레이가 정상적으로 동작하지 않고 아무런 출력이 켜지지 않는 현상 발생.

  • 원인 분석: 초기 회로 구성 시 해당 모듈을 공통 음극 방식으로 판단하여 공통 핀을 GND에 연결했으나, 실제 사용된 소자는 공통 양극 방식인 것으로 확인됨.

  • 해결 방법: 공통 핀의 연결을 GND에서 5V(VCC) 전원으로 재배치한 후, 소스 코드의 제어 신호(High/Low)를 모듈 특성에 맞게 수정하여 정상 동작을 확인함.

5.2 통신 이슈: 웹 클라이언트 실시간 상태 업데이트 불가

  • 문제 현상: 웹 서버 연동 후, 서버 측에서 장치 상태 데이터를 전송하기만 해서는 웹 클라이언트 화면이 즉각적으로 갱신되지 않는 현상 발생. 사용자가 수동으로 새로고침을 해야만 최신 상태가 반영됨.

  • 원인 분석: 일반적인 HTTP 프로토콜은 클라이언트의 요청이 있어야만 서버가 응답하는 단방향 통신 구조임. 따라서 다른 클라이언트나 하드웨어 연동에 의해 장치 상태가 변경되었을 때, 서버가 주도적으로 웹 브라우저에 변경된 데이터를 밀어 넣어줄 수 없었음.

  • 해결 방법: 서버와 웹 클라이언트 간에 단방향 지속 연결을 유지할 수 있는 SSE 기술을 도입함. 이를 통해 장치 상태에 변화가 생길 때마다 서버가 연결된 모든 웹 브라우저에 JSON 형태로 이벤트를 즉시 브로드캐스트하도록 통신 구조를 개편하여, 실시간으로 UI가 동기화되도록 보완함.


6. 개발 진행 리뷰

[1일차 리뷰] 2026-06-02

요약: 초기 계획을 크게 앞당겨 1~3일차 분량의 핵심 로직(빌드 자동화, 하드웨어 제어 라이브러리, TCP 멀티스레드 통신 등)을 대부분 1일차에 구현 완료함.

✅ 완료된 작업
  1. 프로젝트 환경 및 빌드 시스템 구축
    • Makefile 작성 완료 및 전체 프로젝트 폴더 구조 확립
    • 쉘 스크립트를 활용하여 빌드부터 서버 배포까지의 과정 자동화 완료
  2. 하드웨어 제어 모듈 및 동적 라이브러리 구현
    • LED, 부저, 조도 센서, 7세그먼트 등 모든 하드웨어 모듈에 대한 단위(모듈) 테스트 진행 및 검증 완료
    • 단위 테스트가 완료된 소스 코드들을 독립적인 동적 라이브러리(.so)로 추출 완료
  3. 통신 및 이벤트 처리 로직 구현
    • 이벤트 처리용 CLI 작성 및 테스트 완료
    • main.c를 분리하여 멀티스레드 기반의 TCP 서버와 클라이언트 구조 구축 및 통신 테스트 완료
🚀 향후 계획 및 보완 사항
  • 서버 데몬화: 현재 서버가 포그라운드에서 동작 중이므로, 이를 백그라운드에서 상시 동작하는 데몬 프로세스로 전환할 예정
  • 웹 서버 확장: 기본 요구사항을 조기 달성함에 따라, 추가 기능으로 사용자 편의성을 높이기 위한 웹 서버를 별도로 구축하고 기존 제어 시스템과 연동할 계획

[2일차 리뷰] 2026-06-03

요약: 클라이언트마다 스레드를 생성하던 기존 방식을 탈피하여 epoll 기반의 고성능 비동기 이벤트 루프 아키텍처로 서버를 전면 리팩터링하였습니다. 클라이언트 역시 select를 활용한 비동기 수신 구조를 갖추었으며, 하드웨어 상태를 통합 관리하는 구조체를 도입해 향후 웹 서버 확장을 위한 튼튼한 기반을 마련했습니다.

✅ 완료된 작업
  1. 서버 데몬화 및 전면 리팩터링
    • 서버가 시스템 백그라운드에서 상시 동작하도록 데몬 프로세스 전환 로직 구현
    • 네트워크 코어(core.c)와 상태 관리(state.c) 등 역할에 따라 모듈을 완벽히 분리하여 구조적 안정성 확보
  2. epoll 기반 서버 비동기 이벤트 루프 구축
    • 스레드 생성 오버헤드를 없애고 epoll을 도입하여 TCP 및 향후 사용될 HTTP 소켓의 이벤트를 싱글 스레드로 고속 처리하는 구조 완성
    • TCP/HTTP 세션을 통합 관리하는 태깅 및 상호 참조 패턴을 적용하여, 클라이언트 연결 종료 시 O(1) 속도로 리스트에서 삭제되도록 최적화
  3. 클라이언트 select 기반 비동기 I/O 구현
    • 클라이언트 측에서 사용자의 키보드 입력(stdin)과 서버 응답 수신(sockfd)을 select로 멀티플렉싱하여, 서버의 상태 변화를 화면에 실시간으로 출력하도록 개선
  4. 전역 상태 제어 및 동기화 이슈 해결
    • 하드웨어 전체 상태를 중앙에서 관리하는 device_state 구조체 도입
    • 워커 스레드와 메인 스레드 간의 속도 차이로 인한 응답 지연을 막기 위해 상태 선반영 기법 적용
🚀 향후 계획 및 보완 사항
  • 웹 서버 로직 구현 및 웹 클라이언트 확장: 열어둔 HTTP 서버 소켓을 기반으로 웹 소켓 또는 HTTP 파싱 로직을 작성하여, 웹 브라우저 환경에서도 하드웨어 상태를 실시간으로 모니터링하고 제어할 수 있는 GUI 인터페이스 연동

[3일차 리뷰] 2026-06-04

요약: 프로젝트의 최종 목표이자 확장 기능이었던 '실시간 웹 모니터링 및 제어 시스템'을 미들웨어 없이 순수 C 언어만으로 완벽하게 구현하며 모든 개발을 조기 완료하였습니다. HTTP 파싱과 SSE를 활용한 단방향 브로드캐스트를 구축하였고, 멀티스레드 환경에서의 Race Condition 및 데드락을 방지하기 위해 Mutex 동기화 패턴을 정밀하게 설계했습니다.

✅ 완료된 작업
  1. 순수 C 기반 HTTP 파싱 및 웹 서버 연동
    • epoll 이벤트 루프 내부에 HTTP GET/POST 요청을 라우팅하는 로직을 직접 구현
    • 웹소켓 대신 브라우저 네이티브 기술인 SSE를 채택하여, 연결을 끊지 않고 하드웨어 상태 변화를 다수의 웹 클라이언트에게 실시간 JSON 형태로 브로드캐스트하는 구조 완성
  2. Mutex를 활용한 완벽한 스레드 동기화
    • state.c로 전역 상태 접근을 캡슐화하고, pthread_mutex를 적용하여 여러 워커 스레드와 네트워크 루프가 동시에 상태에 접근할 때 발생하는 Race Condition 원천 차단
    • 락 안에서는 값만 갱신하고 플래그를 반환한 뒤, 락을 해제한 상태에서 브로드캐스트를 쏘는 플래그 패턴을 도입하여 데드락 위험 완벽 해소
  3. 반응형 웹 대시보드 UI 구축
    • 다크 모드 및 네온 테마를 적용한 세련된 IoT 관제 대시보드(index.html) 구현
    • 클라이언트 명령은 Fetch API(POST) 로 전송하고, 상태 업데이트는 EventSource(SSE) 로 수신하는 비동기 분리형 프론트엔드 통신 아키텍처 적용
🚀 향후 계획 및 보완 사항
  • 통합 안정성 테스트 및 예외 처리: 모든 기능 구현이 완료됨에 따라, 네트워크 단절, 잘못된 HTTP 패킷 수신 등 극한의 엣지 케이스 상황에서 서버가 죽지 않고 버티는지 최종 스트레스 테스트 진행 예정
  • 최종 문서화: 프로젝트 마무리를 위한 코드 주석 정리 및 README 문서 최종화

About

rpi4 - TCP를 이용한 원격 장치 제어 프로그램

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages