Skip to content
오늘을살자
Go back

lwIP RAW TCP가 '가끔 멈춘다': tcp_recved() 안 치면 윈도우가 안 열린다

Edit page

lwIP로 TCP bring-up 하면 “가끔 멈춘다”는 말이 꼭 나온다.

이 상황에서 제일 먼저 의심하는 포인트가 있다.

RAW API에서 tcp_recved()를 치고 있나?

tcp_recved() 한 줄 정리

tcp_recved()

“내가 이만큼 데이터를 소비했으니, 그만큼 receive window를 다시 열어도 된다”

를 lwIP TCP 스택에 알려주는 호출이다.

이걸 안 치면 lwIP는 이렇게 행동한다.

즉 네트워크가 죽은 게 아니라, 흐름제어(flow control)가 막힌 것이다.

어디서 호출해야 하나: tcp_recv 콜백 안에서

RAW API를 쓰면 보통 구조가 이렇다.

tcp_recv(pcb, recv_cb)
  recv_cb(arg, pcb, pbuf *p, err)

콜백으로 pbuf가 들어오면,

  1. pbuf를 처리(복사 or 스트림 파서에 투입)
  2. 처리한 만큼 tcp_recved() 호출
  3. pbuf 해제(pbuf_free())

이 3개를 “빠짐없이” 해야 한다.

가장 흔한 안전 템플릿(개념용)

프로젝트마다 버퍼/스레딩 구조가 다르니 그대로 복붙용은 아니고,
흐름만 보자.

static err_t recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
  if (p == NULL) {
    // 상대가 FIN 보냄(연결 종료)
    return ERR_OK;
  }

  if (err != ERR_OK) {
    pbuf_free(p);
    return err;
  }

  // (1) 데이터 처리(예: 복사)
  app_consume_bytes(p);

  // (2) "내가 소비한 바이트 수"를 스택에 알려줌
  tcp_recved(pcb, (u16_t)p->tot_len);

  // (3) pbuf 반환
  pbuf_free(p);
  return ERR_OK;
}

포인트는 p->len이 아니라 p->tot_len이다.

RAW TCP에서 pbuf가 체인으로 들어오는 케이스가 많아서,
p->len만큼만 tcp_recved()를 치면 윈도우가 천천히 새서 결국 막힌다.

“바로 처리 못하는 구조”면 언제 tcp_recved()를 치나

recv 콜백에서 바로 소비를 못 하고,
다른 태스크로 넘겨서 나중에 처리하는 구조도 많다.

이때 선택지는 둘 중 하나다.

1) 콜백에서 복사하고 바로 tcp_recved()

가장 안전하고 디버깅이 쉬운 선택이다.

2) pbuf를 들고 가고, “진짜로 소비한 시점”에 tcp_recved()

zero-copy에 가까운 구조를 원하면 이 방향인데,
대신 규칙이 생긴다.

실무에서는 1)로 먼저 안정화하고, 2)로 천천히 옮기는 편이 덜 아프다.

이 증상이 “가끔”으로 보이는 이유

tcp_recved 누락은 사실 재현이 꽤 잘 되는 버그인데,
현장에서는 “가끔”으로 표현되는 경우가 많다.

이유는 보통 이렇다.

그래서 팀 내에서는 “랜덤”처럼 느껴지지만,
Wireshark로 보면 대개 흐름이 똑같다.

Wireshark에서 보이는 힌트(빠르게)

아래 중 하나가 보이면 의심할 가치가 충분하다.

즉 “링크/라우팅 문제가 아니라, 수신 윈도우가 막혔다”는 시그널이다.

빠른 디버깅 체크리스트

  1. tcp_recv() 콜백에서 tcp_recved() 호출이 실제로 있는지 grep
  2. p->tot_len을 쓰는지 확인(체인 대비)
  3. 에러/드롭/early return 경로에서도 pbuf_free()가 빠지지 않았는지 확인
  4. “다른 태스크로 넘기는 구조”면 tcp_recved() 호출 시점을 표로 정리
  5. 필요하면 recv 콜백에서 p->tot_len을 로그로 찍고, 누적 소비량이 증가하는지 확인

한 줄 요약

lwIP RAW TCP에서 전송이 멈추고 재전송만 쌓이면, 먼저 tcp_recved() 누락(또는 p->tot_len 처리 실수)으로 TCP receive window가 안 열리는지부터 의심하는 게 제일 싸고 빠르다.

추천 키워드

lwIP, RAW TCP, tcp_recved, TCP window, pbuf, 네트워크 디버깅

참고 자료


DevBJ | 오늘을살자, Log Today


Edit page
Share this post on:

Previous Post
tcpdump 입문: 초보자를 위한 패킷 덤프 읽는 법
Next Post
DoIP는 TCP stream이다: recv()만 믿고 파싱하면 깨지는 이유