- C
- nat 적용
- openwrt nat table
- 제주도
- openwrt
- hostapd
- 나트랑 일정
- 네트워크
- menuconfig
- 자이언트얀
- openwrt nat 테이블 확인하기
- 리눅스
- Linux
- programmers level2 C
- C언어
- openwrt netfilter
- Wireless
- 취미생활
- 나트랑 여행 경비
- nat table 확인하기
- WiFi
- 동대문부자재시장
- ubuntu
- nf_conntrack
- WiFi6
- 비즈팔찌
- 프로그래머스 c언어
- openwrt nf_conntrack
- nat 정의
- openwrt nat
- Today
- Total
Mandoo’s WLAN story
[C언어] 서버-클라이언트 통신: 패킷 손실 문제의 원인 및 해결방법 본문
서버와 클라이언트가 소켓으로 패킷을 전달하는 과정에서 패킷이 손실되는 문제가 발생했고,
해결방법을 정리해보았다.
해결하는 과정을 세세하게 적어두었으니 부분부분 넘어가도 좋다.
▶ 통신 환경
통신 : 소켓통신
서버 : 웹 (웹 개발자가 따로 있어서 사용언어는 정확하지 않지만, JAVA로 추측됨)
클라이언트 : C언어로 개발된 리눅스용 프로그램
패킷 : 헥사값으로 이루어진 5000byte 이상의 패킷
▶ 문제점
클라이언트가 패킷 전체를 전달받지 못하는 문제가 간헐적으로 발생
▶ 원인 파악 과정
1. 네트워크 상태 체크 : 클라이언트 - 서버 ping체크
2. 클라이언트 C 소스코드에서 request 패킷 전송 전, 후 / response 패킷 수신 전, 후 마다 연결 상태 및 에러를 체크했다.
3. 서버에서 패킷을 전송하는 시점부터 클라이언트가 수신하는 과정을 wireshark로 확인해보았다.
▶ 원인 파악
wireshirk로 확인 시 패킷을 여러번 쪼개서 보냄을 확인했다.
이 때 패킷을 1464byte로 잘라서 총 4번을 보내고 있다는 것을 확인했고, 부가적인 설명은 아래와 같다.
- 클라이언트에서 패킷을 받을 때에는 Info 필드의 Len에서 확인되는 1398byte씩 총 4번씩 패킷을 수신하게 된다.
- 아래 사진 info 필드에 Sequence 넘버와 Len을 표시해두었다.
- 패킷이 전송될 때 붙었던 헤더들을 제외하고 실제로 서버에서 보낸 패킷만 전달받는다.
- wireshark 우측 하단에 보면 헥사값을 십진수로 나타낸 패킷 내용을 보면 패킷이 잘렸음을 확실하게 확인 할 수 있다.
- 해당 문제는 서버담당자에게 확인해보니, 서버의 MTU의 크기가 1500으로 설정되어있다고 한다.
- 사내 서버망 문제로 MTU 설정이 불가능하다고 해서 현재 상태로 유지하기로 했다.
- MTU는 네트워크의 최대 전송 단위이고, 따로 설정하지 않으면 기본값은 1460이며,
1300 ~ 8896 사이의 모든 값 으로 설정할 수 있다.
(MTU 설정하는 방법은 구글에 검색하면 쉽게 찾아볼 수 있다.)
아래 사진을 보면 서버에서 패킷이 전송 될 때 네트워크 상에서 4개의 패킷으로 쪼개서 전송됐지만,
클라이언트가 보낸 ACK 패킷은 2개만 존재하고, ACK 넘버가 1부터 시작을 하지 않은 상태임을 확인 할 수 있다.
정상적인 경우에는 아래 캡쳐와 같이 클라이언트에서 ACK 패킷을 총 4번 보내주어야 한다.
▶ 클라이언트 해결방법
근본적인 원인을 알았으니 이제 클라이언트에서 처리를 해주어야한다.
처음에 작성했던 패킷을 수신받는 부분 코드는 3번째줄이다.
read를 한번만 실행해주도록 했다.
write(sockfd, write_buff, TX_BUFF_SIZE);
int read_len = 0;
rx_len = read(sockfd, read_buff, RX_BUFF_SIZE);
if(rx_len<=0)
{
close(sockfd);
printf("\n read packet error!!\n");
}
수정한 코드는 아래 코드와 같다
#define REAL_LEN 6310
unsigned char read_buff[5000]={0x00,}; //서버로부터 전달받은 패킷을 저장할 변수
int read_len = 0; //read 함수의 반환값을 저장 (반환값 : 패킷의 길이)
read_len = read(sockfd, read_buff, RX_BUFF_SIZE); //서버에서 보낸 패킷을 수신
if(read_len != REAL_LEN) //실제로 read에서 받아야 하는 패킷 사이즈가 아닐 시 아래 코드 수행
{
unsigned char tmp_buff[1398]={0x00,}; //남은 패킷을 반복해서 저장할 변수
int read_len1 = 0; //다시 read할 때의 반환값을 저장 할 변수
unsigned char tmp_read_buff1[5000]={0x00,}; //다시 read한 패킷을 한 공간에 이어붙힐 때 사용 할 변수
int tmp_read_buff_len = 0; //남은 패킷들의 사이즈를 합산
int div_read_len = read_len/1398; //다시 read 할 횟수 정의.
int tmp_len = REAL_LEN - read_len;
if((tmp_len % 1398) != 0) //1398로 나눈 상태에서 나머지가 있을 시 +1
div_read_len++;
for(int i = 0; i < div_read_len; i++) //div_read_len 만큼 read를 반복
{
read_len1 = read(sockfd, tmp_buff, sizeof(tmp_buff));
if(read_len1 > 0) //read 로 패킷을 받았을때만 read_buff에 이어붙힌다.
{
memcpy(read_buff+read_len, tmp_buff, sizeof(tmp_buff));
read_len += read_len1;
}
}
}
1. 처음에 read 할 때 1398byte로 쪼개진 4개의 패킷을 다 받을수도 있고, 몇개만 받고 끝날수도 있음
2. 실제로 서버에서 전송한 패킷의 사이즈와 현재 read로 받은 패킷의 사이즈를 비교
3. 처음에 수신한 read_len이 실제 패킷의 길이와 다르다면 못받은 패킷의 개수만큼 read를 다시 해준다.
* read를 다시 해줄 횟수를 구하는 조건은 아래와 같다
- 원래 받아야 하는 패킷 사이즈 : REAL_LEN
- 처음 read 시 실제로 받은 패킷 사이즈 : read_len
- 몇 byte 기준으로 쪼개지고 있는지 : REAL_LEN
- 받아오지 못한 패킷 사이즈 : tmp_len = REAL_LEN - read_len
- 반복해야하는 횟수 : div_read_len = tmp_len / REAL_LEN
- 나머지가 있는 경우 : div_read_len++
-> 마지막에 남은 패킷의 사이즈가 1398보다 작은 값이 될 수 있기 때문에 +1이 되어야 함.
- div_read_len 만큼 read를 반복해서 수행
4. read를 반복 할 때 마다 처음에 read한 패킷이 저장되어있는 read_buff에 값을 쌓아준다.
* read_len에도 다시 받은 패킷의 길이를 누적한다.
패킷을 전달받는 부분만 따로 올리려고 임의로 코드를 작성했기 때문에, 문제가 있을 수 있으니 흐름만 보고 복붙해서 쓰는건 추천하지 않는다.
▶ 요약해보면
나는 위의 해결방법과 같이 실제로 받아야하는 패킷의 사이즈를 알고있는 상태에서 하드코딩해서 패킷 전체를 수신하였다.
위 원인파악 부분에서 설명했듯이 패킷이 쪼개지는 기준은 1398byte씩 동일하게 쪼개졌고, 이는 서버의 MTU로 인해 발생한 문제이다.
서버 세팅을 바꿀 수 없으니, 코드 안에서 패킷을 반복하여 받도록했다.
지금까지 문제없이 사용됐는데 개발환경이 바뀌면서 갑자기 발생한 이슈에 급하게 해결하느라 위와 같이 복잡한 계산을 통해 해결 할 수 밖에 없었다.
p.s 혹시라도 글에 문제점 or 궁금한 점 or 더 좋은 방법 이 있다면 댓글로 공유 부탁드립니다!
이만 뿅!
'Mandoo's IT Story > Dev' 카테고리의 다른 글
[C] openwrt 컴파일 에러 "No gnu/libc-version.h found package/devel/perf" (0) | 2024.03.20 |
---|---|
[C++] Visual Studio에서 C++ 시작하기! (0) | 2023.02.16 |
[Linux] shell script로 디스크 사용량 확인하기 (0) | 2022.09.20 |
[C] OpenWRT에서 Gateway 정보 가져오기 (0) | 2022.05.13 |
[Linux/C] 시스템 가동 시간 확인하기 (0) | 2021.12.22 |