tcp 패킷 받아서 사용하기

3권에서 새로 도입된 네트웍 및 멀티플레이어 프로그로그래밍 섹션을 위한 게시판입니다.

Moderator: 류광

Locked
비회원

tcp 패킷 받아서 사용하기

Post by 비회원 »

안녕하세요

tcp 패킷의 경우 크기가 제멋대로 들어오잖아요

그걸 프로그램에서 처리할때 큐같은데 차례대로 집어넣어서 순차적으로 파싱해야할가요 ?

아니면 큐같은거 없이 받는 즉시 파싱 남는것 다음에 파싱 이런식으로 해야할까요 ?

보통 tcp 패킷 처리할때 어떡게 하나요 ?

책보니 정해진 에코 메시지만 다루고 있어서

직접 패킷 구조를 만들어서 사용할때 어떡게 다루어야 효율적일지 모르겠네요

가령

struct 구조
{
uint32_t size;
uint32_t type;
uint8_t *ptr;
};

이런식일때 패킷에서 4 바이트크기의 size 변수를 파싱할때 네트워 tcp 상에 3 바이트가 왔다면 (가령)

다음번 100 바이트... 이런식이면 3 + 1 바이트를 size 파싱, type 에 해당하는 그다음 데이터 4 바이트 파싱

나머지 데이터 파싱... 뭔가 간단한건데 어렵게 하는거 같아서 질문올립니다.

그럼 좋은 답변 부탁드리며... 좋은 하루 되세요
xster
Posts: 214
Joined: 2006-10-30 10:56

Post by xster »

패킷이라는 것을 하나의 단위로 생각해야 한다면 해당 단위가 다 올 때까지 받은 내용을 유지하고 있다가 처리하는 것이 간단할 것 같습니다.

하나의 단위가 아니라면 하나의 패킷이 아니라고 판단할 수 있을 것 같은데요.

앞에서의 예에서 보면 사이즈와 타입과 실제 데이터를 나눠서 파싱하는 것이
큰 의미가 없을 것 같습니다.
어차피 실제 데이터 영역까지 다 받기 전의 패킷 구조체는 큰 의미가 없는 것 같군요.
속도 최적화나 공간 최적화의 이슈라면 모르겠지만 일반적으로 봤을 때 각 단계로 나눴을 때의 이득은 잘 모르겠네요.
softpark
Posts: 4
Joined: 2009-11-20 12:20

Post by softpark »

소스 참고 하시기 바랍니다. ACE 가 중간중간에 들어가있어서... 도움이 될지는... OTL

int
ZZL_Simple_Handler::recv (ACE_Message_Block *&mb)
{
if (this->msg_frag_ == 0)// No existing fragment...
ACE_NEW_RETURN (this->msg_frag_, ACE_Message_Block (ACE_DEFAULT_CDR_BUFSIZE), -1);

ssize_t header_received = 0;
ssize_t header_bytes_left_to_read = ZZL_HEADER_SIZE - this->msg_frag_->length ();

//해더를 받을 차례이거나, 헤더를 받다 말았다.
if (header_bytes_left_to_read > 0 )
{
header_received = this->peer().recv(this->msg_frag_->wr_ptr(), header_bytes_left_to_read);

if (header_received == -1 /* error */|| header_received == 0 /* EOF */)
{
ZZL_ERROR ("Recv error during header read err_code[%d]\n", ARG1(::WSAGetLastError()));
ZZL_DEBUG ("attempted to read %d bytes\n", ARG1(header_bytes_left_to_read));

this->msg_frag_ = this->msg_frag_->release ();
return header_received;
}

this->msg_frag_->wr_ptr (header_received);

if (this->msg_frag_->length () < ZZL_HEADER_SIZE)
{
ZZL_DEBUG ("Partial header received: only %d bytes\n", ARG1(this->msg_frag_->length ()));
errno = EWOULDBLOCK;
return -1;
}

ZZL_Packet_Parser parser(*msg_frag_);
size_t body_size = parser.written_body_size ();
size_t msg_size = parser.packet_size ();

if (body_size > 4096) //ServiceBase::getMaxPacketBodySize()
{
errno = EINVAL;
ZZL_DEBUG ("Data payload(body size) is too big (%d bytes)\n", ARG1(body_size));
mb->release ();
return 0;
}

this->msg_frag_->size(msg_size);

}

// 자 헤더를 받았으니. Body를 받자, 처음일수도 있고, 바디가 너무 커서, 2번째 이상일수도 있다.(EWOULDBLOCK)
// class Message 내부에 header 필드 자체가 없음으로, 아쉽지만, message_type, body size -> hard coding

u_short body_size = *((u_short *) (this->msg_frag_->rd_ptr () + 2)); // first 2byte: msg_type, next 2byte: bodysize

ssize_t data_bytes_left_to_read = body_size - (this->msg_frag_->length() - ZZL_HEADER_SIZE);

ssize_t body_received = !data_bytes_left_to_read ? 0 :
this->peer ().recv (this->msg_frag_->wr_ptr (), data_bytes_left_to_read);

switch (body_received)
{
case -1:
if (errno == EWOULDBLOCK)
return -1;
else
/* FALLTHROUGH */;

case 0: // Premature EOF.
if (data_bytes_left_to_read)
{
this->msg_frag_ = this->msg_frag_->release ();
return 0;
}
/* FALLTHROUGH */;

default:
this->msg_frag_->wr_ptr (body_received);

if (body_received != data_bytes_left_to_read)
{
errno = EWOULDBLOCK;
return -1;
}
else
{
this->msg_frag_->rd_ptr (this->msg_frag_->base ());
mb = this->msg_frag_;

this->msg_frag_ = 0;
}

return body_received + header_received;
}
}
Locked