Socket.io 통신에 관련해 질문드립니다

안녕하세요 socket.io 통신에 관련해 궁금한 점이 생겨 질문 드립니다.
저희는 현재 github에 올려주신 텔레메트리 서비스를 저희 사정에 맞게 변경해보고자 서버와 코드를 자체 제작 중입니다.
이외 코드는 모두 완성했는데 서버와 통신 자체가 되지 않아 이에 관해 조언을 얻고자 질문드립니다.

서버는 node.js express 를 통하여 서버를 구축하였습니다. 서버는 로컬에서 포트포워딩을 이용하여 가동하였다가 현재는 AWS EC2 인스턴스에 가동 중입니다.

ESP32에는 서버의 public ip와 port를 입력하였고, url 부분에는 /socket.io/, /socket.io/?EIO=4&, /socket.io/?EIO=4&transport=websocket 등 여러 url을 시도해보았으나 실패하였습니다. esp32와 서버가 같은 네트워크에 연결되어 있을 때는 서버의 local ip를 입력하였습니다.

그러나 모든 경우에도 socket.io 연결이 되지 않습니다.

<웹사이트 - 서버 사진>

리액트 클라이언트와 서버 간 연결은 정상적으로 작동합니다. 서버에서 클라이언트와 연결되었을 때, 예시 데이터와 현재 시간을 보내고, 서버에 사용자 연결됨 : 소켓 id 가 입력되도록 하였습니다.

서버 코드와 esp32 코드 관련해서는 문제가 없다고 판단됩니다.

<wireshark 사진>

공유기의 54321 포트를 포트포워딩을 통해 열어 노트북으로 서버를 구동하였을 때 wireshark로 통신 패킷을 관찰하였습니다. 이때 esp32 클라이언트가 서버로 통신 실패가 계속 뜨는 것을 확인하였습니다. 이 문제를 해결하기 위해 컴퓨터의 모든 방화벽을 열고 해당 포트를 열었으나 문제는 해결되지 않았습니다.

Socket.io 통신 연결부만을 남긴 서버 코드입니다.

// 시작
require('dotenv').config();

const express = require('express');
const { DynamoDBClient, ScanCommand, PutItemCommand } = require('@aws-sdk/client-dynamodb');
const cors = require('cors');
const http = require('http');
const { Server } = require('socket.io');
const exceljs = require('exceljs');

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  pingTimeout: 60000,  // ping 타임아웃 20초로 늘림
  pingInterval: 25000,  // ping 간격 25초로 유지
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
  transports: ['websocket'],
  allowEIO3: true
});

const port = 2004;

app.use(cors());
app.use(express.json());

// 중략 ... 


// Socket.IO 연결 이벤트
io.on('connection', (socket) => {
  console.log('사용자 연결됨:', socket.id);

// 중략 ..

  socket.on('disconnect', (reason) => {
    console.log('사용자 연결 해제됨:', reason);
  });
});

// 서버 시작
server.listen(port, () => {
  console.log(`${port} 포트에서 서버가 시작되었습니다.`);
  console.log(getKoreaTime());
});
// 끝

esp32측은 통신에 사용하셨던 코드를 사용했습니다. 코드는 다음과 같습니다.

#include <WiFi.h>
#include <SocketIOclient.h>

// SON_ID
#define SON_ID "nitepp04"
#define SON_PWD "00000000"

// Hotspot AP configurations
const char ssid[] = SON_ID;
const char password[] = "SON_PWD";

// 서버 정보
const char server[] = "15.164.141.67";  // 소켓IO 서버 IP
const char url[] = "/socket.io/";       // 소켓IO 서버 URL
const int port = 2004;                  // 소켓IO 서버 포트

// Socket.IO 클라이언트
SocketIOclient socketIO;

// Socket.IO 이벤트 처리 함수
void socketIOEvent(socketIOmessageType_t type, uint8_t *payload, size_t length) {
  switch (type) {
    case sIOtype_CONNECT:
      Serial.println("Socket.IO 연결 성공");
      break;

    case sIOtype_DISCONNECT:
      Serial.println("Socket.IO 연결 해제");
      break;

    case sIOtype_EVENT:
      Serial.print("이벤트 수신: ");
      Serial.println((char *)payload);
      break;

    case sIOtype_ERROR:
      Serial.println("Socket.IO 오류 발생");
      break;

    default:
      break;
  }
}

void setup() {
  Serial.begin(115200);

  // Wi-Fi 연결
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // Wi-Fi 연결 대기
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWi-Fi 연결 완료");

  // Socket.IO 서버 연결
  socketIO.begin(server, port, url);
  socketIO.onEvent(socketIOEvent);
  Serial.println("Socket.IO 연결 시도 중...");
}

void loop() {
  // Wi-Fi 연결 확인
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Wi-Fi 연결 끊김, 재연결 시도 중...");
    WiFi.reconnect();
    delay(500);
    return;
  }

  // Socket.IO 루프 유지
  socketIO.loop();

  // 데이터를 서버로 전송 (테스트용 메시지)
  socketIO.sendEVENT("ESP32에서 서버로 보내는 메시지");
  delay(1000);  // 1초마다 메시지 전송
}

하지만 통신이 되지 않고 시리얼프린트에

Wi-Fi 연결 완료
Socket.IO 연결 시도 중…
Socket.IO 연결 해제
Socket.IO 연결 해제
Socket.IO 연결 해제
(이후 “Socket.IO 연결 해제” 반복됨)

다음과 같이 출력되어 통신이 연결되지 않음을 확인했습니다.

긴글 읽어주셔서 감사합니다.
올려주신 글들 모두 항상 많은 도움 되고있습니다 감사합니다.

안녕하세요. 올려주신 코드 읽어봤는데 이해가 잘 안 되는 부분이 있어서요.

서버는 포트 2004번에서 실행하고 esp32도 2004번에 연결하려는 것으로 보이는데요, 공유기의 54321번 포트는 무슨 말씀인지 잘 모르겠네요.

ESP32 2004 <—> 공유기 외부 2004 to 내부 2004 포워딩 rule <—> 서버 포트 2004

이런 식으로 설정이 되어야 서로 통신할 수 있을 텐데요, 글 내용은

ESP32 2004 <—> 공유기 외부 54321 to 내부 2004(?) <—> 서버 포트 2004

이런 상황인 건지 어떤 상황인지 잘 모르겠어요. 이런 거라면 ESP32에서는 54321번 포트로 연결을 시도해야 합니다.

집에서 공유기 포트포워딩으로 제 퍼블릭 ip의 54321 포트를 로컬 ip의 1101 포트로 열었습니다. 54321 포트로 열어서 테스트해봤을 때는 1101 포트로 변경 후 코드 실행하였습니다. 나머지 경우에는 2004로 실행하였습니다. 현재 계속 시도 중이며, 현재는 aws ec2로 2004번 포트에서 실행 중입니다.

이렇게 설정하셨다면 밖에서 바라볼 때는 (ESP32에서 연결할 때는) 1101/2004번이 아니라 54321/55555번 포트에 접속하셔야 해요.

그것 또한 실행해 보았는데 계속 실패하였습니다…

현재는 aws ec2에 서버를 가동 중입니다. ec2의 보안그룹에서 2004번으로 포트를 열어 외부에서 포트포워딩 작업 필요 없이 aws ec2의 퍼블릭 ip의 2004번 포트에 접근 가능하도록 설정해두었습니다.

현재는 이전과 동일한 esp32측 코드와

const ip = require("ip");
var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = 2004; // || 연산자로 기본 포트 설정

app.get('/', function (req, res) {
    // check sender ip
    var senderip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; // >    res.send('<h1>Hello world</h1>');
});

io.on('connection', function (socket) {
    console.log('Client connected');
    socket.on('message', function (data) {
        console.log('Message received: ', data);
    });
    socket.on('disconnect', function () {
        console.log('Client disconnected');
    });
});

server.listen(port, () => {
    console.log('Server is running on port', port);
});

해당 서버코드를 실행시켰더니

esp32측 시리얼 프린트에는

Socket.IO 연결 성공
Socket.IO 연결 해제
Socket.IO 연결 성공
Socket.IO 연결 해제

구문이 반복되어 출력되지만 서버측 콘솔창에는 아무것도 출력되지 않습니다. esp32측 시리얼 프린트로 판단해 보았을 때는 연결이 되기는 한 것 같은데 왜 서버측 콘솔창에 표시가 안되는지 모르겠습니다.

추가로 서버측 코드에 기존 사용했던

var io = require('socket.io')(server, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
  transports: ['polling', 'websocket'], // websocket과 polling 모두 허용
});

구문도 추가해 실행시켜 보았지만 동일합니다.

원인이 잘 짐작이 가질 않네요. 웹 클라이언트에서는 정상적으로 접속된다고 하셨으니 서버 방화벽 문제는 아닐테고 네트워크 자체는 잘 설정된 것 같은데요. ESP32에서 Connected 메시지가 뜨는걸 보면 뭔가 받긴 받았을텐데 서버 콘솔에 로그가 안 뜨는게 이상하네요.

혹시 웹 클라이언트에서 서버에 접속했을 때는 정상적으로 서버 콘솔에 로그가 찍히나요? 지금 보니 제가 작성했던 코드는 io.sockets.on('connection', socket => { }); 인데 사용하고 계신 코드는 io.on('connection', socket => { }); 로 약간 다르네요. 좀 오래되어서 정확히 기억나지는 않는데, socket.io 문법이 저런거 한 끗 차이로 뭔가 됐다 안 됐다 하던 기억이 있어서요.


추가로 올려주신 패킷 캡쳐를 보면 ESP32가 HTTP 요청을 보내는데 서버가 400 Bad Request 로 응답하는게 보이는데요, 저게 80번 포트로 날아가서 HTTP로 찍히는 건지 패킷 헤더가 HTTP라서 그렇게 찍히는 건지는 모르겠지만 응답이 400이면 무슨 다른 웹 서버가 대신 응답하고 있는게 아닌가 하는 의심이 드네요. 저 요청을 node.js 프로그램이 받았다면 핸들링하는 코드가 없기 때문에 timeout이 나지 자기가 알아서 400으로 응답하지는 않거든요.