tcp에서 메시지 송수신 시 발생하는 문제 관련하여 질문드립니다.

balgarac1의 이미지

아래와 같은 메세지 타입이 있습니다.
이 메세지를 서버와 클라이언트가 주고 받습니다.
메세지를 수신 할 때 어떤 메세지가 올 지 예측할 수 없으므로
recv 사용시 void * 형 버퍼를 사용했습니다.

수신을 다 받으면 메세지 타입에 맞도록 형변환 시켜서 대입했습니다.

문제는 수신 받을 때 메세지 타입을 알아야 하므로
2바이트를 먼저 읽는 코드를 짜면
2바이트 수신 후에는 메세지 타입을 알 수 있습니다.

메세지 타입 확인 후에는 읽어들일 메세지 크기를 알 수 있으므로
이것도 while 루프를 돌면서 저장 받을 수 있습니다.

이런 로직의 코드를 작성했는데 문제가 있는지 송수신이 안되고 있습니다.
다시 봐도 로직에 문제가 잘 깨달아지지 않아서

이곳에 질문 드립니다.. 가르침 주시면 감사하겠습니다...

#define UB2 unsigned short	  
#define UB4 unsigned int	  
#define SCHAR signed char		 
 
#define AUTH_REQ_MSG 1
#define AUTH_RES_MSG 2
#define SQL_REQ_MSG 3
#define SQL_RES_MSG 4
#define CLOSE_REQ_MSG 5
#define CLOSE_RES_MSG 6
#define BROKEN_PACKET 7
#define PROGRAM_EXIT 8
#define SUCCESS 9
#define QUERY_EXIT 10
 
#define SELECT 1
#define UPDATE 2
#define DELETE 3
 
// msg type number 1
typedef struct _dgt_auth_req_msg
{
	UB2 msg_type;
	SCHAR db_user[33];
	SCHAR password[33];
	SCHAR program_name[33];
	SCHAR db_name[33];
}dgt_auth_req_msg;
 
// msg type number 2
typedef struct _dgt_auth_res_msg
{
	UB2 msg_type;
	UB2 rtn_len;
	SCHAR rtn_msg[257];
}dgt_auth_res_msg;
 
// msg type number 3
typedef struct _dgt_sql_req_msg
{
	UB2 msg_type;
	UB4 sql_len;
	SCHAR sql_text[1025];
	UB2 sql_type;
}dgt_sql_req_msg;
 
// msg type number 4
typedef struct _dgt_sql_res_msg
{
	UB2 msg_type;
	UB4 rtn_len;
	SCHAR rtn_data[1025];
	UB2 sql_type;
}dgt_sql_res_msg;
 
// msg type number 5
typedef struct _dgt_close_req_msg
{
	UB2 msg_type;
}dgt_close_req_msg;
 
// msg type number 6
typedef struct _dgt_close_res_msg
{
	UB2 msg_type;
	UB2 rtn_len;
	SCHAR rtn_msg[257];
}dgt_close_res_msg;

int CNetwork::CNetworkRecvPacket(void * m_pBuf, UB2 * msgType)
{
 
	ssize_t recvCnt = 0;
	int msgBytePos = 0;
	int recvPacketCnt = 0;
 
	// m_pBuf 메모리 공간 0으로 초기화
	bzero(m_pBuf, 0);
 
	while(true)
	{
		// 2바이트만 받는다
		recvCnt = recv(m_nSock, m_pBuf + msgBytePos, BUFSIZ, 0);
		// 2바이트 이상 받으면 루프 탈출
		if(recvCnt >= 2)
			break;
 
		msgBytePos += recvCnt;
 
		// EOF 프로그램 종료
		if(recvCnt == 0)
			return 0;
 
		// 실패 시 다시 수신한다
		if(recvCnt == -1)
		{
			cout <<"CNetworkRecvPacket Recv Error" << endl;
			msgBytePos = recvCnt = 0;
			continue;
		}
	}
 
	// 메세지 타입 식별 위해 2바이트 복사
	memcpy(msgType, m_pBuf, sizeof(UB2));
 
	if(*msgType == AUTH_RES_MSG)
		recvPacketCnt = sizeof(dgt_auth_res_msg) - msgBytePos;
	else if(*msgType == SQL_RES_MSG)
		recvPacketCnt = sizeof(dgt_sql_res_msg) - msgBytePos;
	else if(*msgType == CLOSE_RES_MSG)
		recvPacketCnt = sizeof(dgt_close_res_msg) - msgBytePos;
	else
		return BROKEN_PACKET;
	int CNetwork::CNetworkRecvPacket(void * m_pBuf, UB2 * msgType)
{
 
	ssize_t recvCnt = 0;
	int msgBytePos = 0;
	int recvPacketCnt = 0;
 
	// m_pBuf 메모리 공간 0으로 초기화
	bzero(m_pBuf, 0);
 
	while(true)
	{
		// 2바이트만 받는다
		recvCnt = recv(m_nSock, m_pBuf + msgBytePos, BUFSIZ, 0);
		// 2바이트 이상 받으면 루프 탈출
		if(recvCnt >= 2)
			break;
 
		msgBytePos += recvCnt;
 
		// EOF 프로그램 종료
		if(recvCnt == 0)
			return 0;
 
		// 실패 시 다시 수신한다
		if(recvCnt == -1)
		{
			cout <<"CNetworkRecvPacket Recv Error" << endl;
			msgBytePos = recvCnt = 0;
			continue;
		}
	}
 
	// 메세지 타입 식별 위해 2바이트 복사
	memcpy(msgType, m_pBuf, sizeof(UB2));
 
	if(*msgType == AUTH_RES_MSG)
		recvPacketCnt = sizeof(dgt_auth_res_msg) - msgBytePos;
	else if(*msgType == SQL_RES_MSG)
		recvPacketCnt = sizeof(dgt_sql_res_msg) - msgBytePos;
	else if(*msgType == CLOSE_RES_MSG)
		recvPacketCnt = sizeof(dgt_close_res_msg) - msgBytePos;
	else
		return BROKEN_PACKET;
 
	// 남은 구조체 길이 만큼 수신한다
	while(msgBytePos < recvPacketCnt)
	{
		recvCnt = recv(m_nSock, m_pBuf + msgBytePos, BUFSIZ, 0);
		if(recvCnt == -1)
			continue;
		if(recvCnt == 0)
			return 0;
 
		msgBytePos += recvCnt;
	}
 
	// 타입에 맞는 구조체로 변환한다
	if(*msgType == AUTH_RES_MSG)
		m_LoginResMsg = *(dgt_auth_res_msg*)m_pBuf;
	else if(*msgType == SQL_RES_MSG)
		m_SqlResMsg = *(dgt_sql_res_msg *)m_pBuf;
	else if(*msgType == CLOSE_RES_MSG)
		m_CloseResMsg = *(dgt_close_res_msg *)m_pBuf;
 
	return *msgType;
}

shint의 이미지

저도 그런 문제가 있었습니다.

- m_pbuf 에 크기를 확인
- recv 의 BUFSIZ 크기를 확인
- 각 함수마다 인자값. 오류값. 리턴값을 확인한후. printf() 문으로 확인

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

익명 사용자의 이미지

답변 감사드립니다.
근데 버퍼값을 왜 확인하나요?

m_pbuf 버퍼 사이즈가 BUFSIZ보다 작아야 하는 건가요?

shint의 이미지

확인해봐야 알거 같습니다.
저도 거기서 멈췄던거 같습니다.

일단. 다 지우고. recv가 되는 상황만 만들어보시는것이 답입니다.
잘 되는 예제소스 따라해보시는것이 좋습니다.

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

익명 사용자의 이미지

컴파일 되는 코드가 아니니 눈으로 쫓을 수 밖에 없는데요.
msgBytePos가 이상한듯 싶습니다.

첫 while에서 타입처리를 2바이트만 받는다고 했는데요.
받은 크기를 msgBytePos에 반영해주지를 않습니다.

그러니 m_pBuf + msgBytePos(0인상태)를 만나게 되니, 첫 while에서 받은 데이터는 덮어 씌워지게 되겠지요?
결국 남은 구조체 길이만큼 수신한다 했는데, 서버는 다 보냈는데, 클라이언트는 데이터를 더 내놓으라 기다리겠네요..;

twinwings의 이미지

코드는 자세히 안봤습니다. 그런데 htons or ntohs 와 같은 함수가 안보이는데

혹시 Byte Order 고려안하신건 아닌가요?

그리고 두번째, 고정길이의 헤더만큼 받고 나머지 받는 코드로 작성하는게 좋지 싶네요.

struct a_type_packet;
struct fixed_len_packet_hdr* hdr = &a_type_packet;
size_t hdr_sz;
size_t dat_sz;
int    recvbyte;
 
recvbyte = recv(fd, hdr, hdr_sz, MSG_WAITALL);
if(recvbyte != hdr_sz) {
  // TCP  연결 끊김, 예외처리
}
 
recvbyte = recv(fd, hdr->data, dat_sz, MSG_WAITALL);
if(recvbye != dat_sz) {
  // TCP  연결 끊김, 예외처리
}

이 코드는 바이트오더가 고려되지 않았습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.