[소켓] 수신할때 두번나눠서 Recv()합니다.

dummy999의 이미지

버퍼사이즈가 8192 입니다.

송신에서 8000정도를 수신쪽에 쏴주니까.
처음할때는 8000정도를 그냥 읽더니
두번째는 이걸 두번에 끊어서 recv()를 합니다.
한번에 읽을수있는방법이 없을까요?

또 OS에서 지원해주는 소켓 사이즈를 코딩으로 볼수없을까요?

소켓번호=[4547748]
Recv Size =[8028]
메시지 : A수신


소켓번호=[4547748]
Recv Size =[12]
메시지 : B수신


소켓번호=[4547748]
Recv Size =[4380]
메시지 : A수신

소켓번호=[4547748]
Recv Size =[3648]
메시지 : 알수없는 메시지

위와같이 나옵니다. 물론 약간 다르지만..
보면 하단에 두개의 메시지크기가 위에 하나일때의 메시지크기와 동일합니다.
다시말해 나눠져서 수신하고있다는 뜻입니다.
이것을 해결해줄수있는방법좀 알려주세요

---------추가-------------------------------------
그런데 이패턴이 일정하지않습니다.
어떨땐 제대로 가고 어떨땐 위와같이 가고합니다.

---------또추가-------------------------------------
안정적인 TCP 사이즈는 얼마나되나요?

cjh의 이미지

송신쪽과 수신쪽의 연결 환경을 보장할 수 없으므로. 송신측에서 보낸 크기대로 받는다는 보장은 전혀 없습니다. 길이가 중요하다면 프로토콜을 변경해서 사전에 보내는 길이를 보내야 하고, 받는 쪽에서는 원하는 양만큼 받을 때 까지 recv()를 수행해야 합니다.

OS의 현재 할당된 소켓 버퍼 크기는 setsockopt() 콜에서 옵션을 SO_SNDBUF, SO_RCVBUF 로 얻을 수 있을 겁니다.

--
익스펙토 페트로눔

익명 사용자의 이미지

* 참고하세요
http://bbs.kldp.org/viewtopic.php?t=20791

Quote:
...소켓 버퍼 크기는 setsockopt() ...

* getsockopt()/setsockopt()를 보세요.
simpid의 이미지

원래 그렇습니다.

Crossover 케이블로 1:1로 연결헀다면...
한번에 다 가져올 가능성이 높고...

아주 먼~~ 경우 (서버가 우간다에 있고... 클라이언트가 한국에...)
한번에 다 가져올 가능성이 낮습니다.
어쩌면 100개쯤의 조각이 날 수 도 있습니다.

결론적으로 프로그램을 나눠서 받아온다는 가정으로 개발하셔야 합니다.

추가)
헤더에 데이터 크기 부분이 있을때 크기에 해당하는 데이터가 1byte라면 모를까.. 2bytes나 4bytes의 경우 이것도 조각날 우려가 있어서 받는데 골치 아프더군요.

참고로 Windows의 경우 IOCP란 녀석이 있는데 이것은 요청한 전송이 완료됐을때 통지를 받으므로 좀 편하긴 합니다.

rasungboy의 이미지

111 을 보냈다고 111 오는게 아니라

11 오고 1 이 올수 있는거죠.

그래서 프로토콜을 정해서 파싱합니다.

즉 하나의 패킷은

헤더 + 패킷바디 로 구성되며

헤더의 구성은 보통

패킷타입
패킷사이즈

로 구성됩니다.

하나의 패킷을 처리할때는 헤더를 읽고 현재 recv 한 바이트가

패킷사이즈만큼 안된다면 패킷을 처리하지 않고 읽은

데이타를 저장하고

recv한 바이트 패킷사이즈 만큼 왔다면 그때 패킷을 처리

하는 구조로 프로그래밍 합니다.

여기서 유의할점은 헤더도 잘려서 올수 있기때문에

헤더읽을때도 현재 헤더크기만큼 데이타가 왔나 검사해서

적잘한 처리를 해주어야 합니다.

이렇게 짤리지 않고 데이타를 보내고 싶으시면 udp 를 쓰지면

되지만 udp 경우 데이타가 손실될수 있으며 순서가 보장되지

않습니다.

dummy999의 이미지

같은 기종간 통신으로 했는데도 분할전송이되었습니다.
결론은 얼마가되었던지간에 완전하게 수신해야하며 그렇게하기위해선 분할전송을 해야할것같습니다.

저는 다음과같은 상황에서 이런 문제를 해결하고싶습니다.

#define SPLIT 1000
buf[10000];
int size1=0, size2=0, idx=0;

size1= recv(소켓,buf,sizeof(buf),MSG_PEEK); //옵션을 이용해서 수신버퍼의 사이즈를 알아내고 
size2= size1;
do
{
    size2= size1/SPLIT;
    recv(소켓,buf[idx], SPLIT,size);
    idx=idx+SPLIT;    
}while(size2/SPLIT != 0);

그냥 알고리즘이 이럽니다.
적용하기전에 다른일을 하고있어서 이것이 잘되는지는 모르겠습니다. 이렇게해도될까요?
검증하고 적용하려합니다.

제가 한가지 깨달은게있습니다.
테스트한곳은 네트워크는 로컬이고, OS는 WINDOWS이며, 인터페이스는 TCP인데도 왜 그것이 신뢰적이지못할까생각해봤습니다.
그런데 보니까. recv()의 개인적인 처리능력이 다를뿐이지 TCP가 UDP처럼 손실 전송을 하지는 않는것같습니다.
위에 그림을 봐도 메시지A의 합을 계산해보면 제일처음꺼 A와 똑같습니다.
다시말해 TCP는 recv단계에서 실수가 나와도 전송은 신뢰적인것같습니다.

------------------------------------
F/OSS bless you... ^^*

simpid의 이미지

dummy999 wrote:
같은 기종간 통신으로 했는데도 분할전송이되었습니다.
결론은 얼마가되었던지간에 완전하게 수신해야하며 그렇게하기위해선 분할전송을 해야할것같습니다.

저는 다음과같은 상황에서 이런 문제를 해결하고싶습니다.

#define SPLIT 1000
buf[10000];
int size1=0, size2=0, idx=0;

size1= recv(소켓,buf,sizeof(buf),MSG_PEEK); //옵션을 이용해서 수신버퍼의 사이즈를 알아내고 
size2= size1;
do
{
    size2= size1/SPLIT;
    recv(소켓,buf[idx], SPLIT,size);
    idx=idx+SPLIT;    
}while(size2/SPLIT != 0);

그냥 알고리즘이 이럽니다.
적용하기전에 다른일을 하고있어서 이것이 잘되는지는 모르겠습니다. 이렇게해도될까요?
검증하고 적용하려합니다.

제가 한가지 깨달은게있습니다.
테스트한곳은 네트워크는 로컬이고, OS는 WINDOWS이며, 인터페이스는 TCP인데도 왜 그것이 신뢰적이지못할까생각해봤습니다.
그런데 보니까. recv()의 개인적인 처리능력이 다를뿐이지 TCP가 UDP처럼 손실 전송을 하지는 않는것같습니다.
위에 그림을 봐도 메시지A의 합을 계산해보면 제일처음꺼 A와 똑같습니다.
다시말해 TCP는 recv단계에서 실수가 나와도 전송은 신뢰적인것같습니다.

아직 요점을 잘못 잡고 계신것 같습니다.

버퍼의 크기가 얼마냐가 중요한게 아닙니다.
recv 함수가 요청한만큼 한번에 처리하지는 않는다는게 요점입니다.

그러므로 위의 코드는 정상 동작할 수 없습니다.

제 생각으로는 아래의 코드를 참조하시는게 좋을것 같습니다.

아래 코드는 recv()와 같은 동작과 같은 리턴값을 갖지만
모든 전송 요청이 완료됐을때 반환됩니다. (물론 블록킹 소켓에서만 사용 가능합니다.)

int socket_recv(int socket, char* buffer, int size)
{
	int total_received;
	int received;

	assert(buffer);
	assert(size > 0);

	total_received = 0;

	while(size)
	{
		received = recv(socket, buffer, size, 0);

		// 리턴값이 = 0 이면 close되었음을 의미
		// 리턴값이 < 0 이면 오류 발생을 의미
		if(received <= 0) break;

		total_received += received;
		size -= received;

		buffer += received;
	}

	return received > 0 ? total_received : received;
}
익명 사용자의 이미지

MSG_WAITALL 옵션을 사용해서 버퍼 사이즈만큼 다 읽어오면 되지 않을까요?

송지석의 이미지

struct timeval timeout; //setting timeout to socket
timeout.tv_sec=MY_TIMEOUT_VAL;
timeout.tv_usec=0;

result= setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); //timeout 지정

rcv_len= recv(*sock, recv_buf, MY_RECV_LEN, MSG_WAITALL);

이런 식으로 쓰면 될 듯.

IsExist의 이미지

os 레벨에서의 버퍼 사이즈랑 관계 없이 하드웨어 mtu 사이즈 이상은 한 패킷으로 날아가지 않죠.

$ ifconfig -a

소프트웨어적인 방법은 다른분들이 설명하셨네요.

send 호출시 1바이트를 여러번 호출하더라도 한번에 날아갈 경우가 있고 1바이트씩 날아가는 경우가 있습니다. 받는 쪽에서 항상 버퍼 사이즈만큼 기대하고 받는건 좋은 방법이 아닌거 같습니다.

---------
간디가 말한 우리를 파괴시키는 7가지 요소

첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스

이익추구를 위해서라면..

다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치

dummy999의 이미지

송지석님 답변 감사합니다. 그런데 Winsock에는 MSG_WAITALL 이런 플래그가 없습니다. ㅠ.ㅠ

#define SPLIT 1000
buf[10000];
int size1=0, size2=0, idx=0;

size1= recv(소켓,buf,sizeof(buf),MSG_PEEK); //옵션을 이용해서 수신버퍼의 사이즈를 알아내고 
size2= size1;
do
{
    size2= size1/SPLIT;
    recv(소켓,buf[idx], SPLIT,size);
    idx=idx+SPLIT;    
}while(size2/SPLIT != 0);

int socket_recv(int socket, char* buffer, int size)
{
	int total_received;
	int received;

	assert(buffer);
	assert(size > 0);

	total_received = 0;

	while(size)
	{
		received = recv(socket, buffer, size, 0);

		// 리턴값이 = 0 이면 close되었음을 의미
		// 리턴값이 < 0 이면 오류 발생을 의미
		if(received <= 0) break;

		total_received += received;
		size -= received;

		buffer += received;
	}

	return received > 0 ? total_received : received;
}

제가보기엔 제가 한거랑 그다지 차이가 없어보입니다.
무슨차이가있을까요?

물론저의경우에도 함수로서 정의해도 됩니다.
다만 바로 짠거라 이게 맞는지모르지만..

함수만들고 안만들고 차이말고 또있습니까?

리턴값 사용부분도 다르긴하지만.. 그게 크게다른점일까요?
소스가 작으니 큰비중을 가진게 안보임 @_@

------------------------------------
F/OSS bless you... ^^*

simpid의 이미지

dummy999 wrote:
송지석님 답변 감사합니다. 그런데 Winsock에는 MSG_WAITALL 이런 플래그가 없습니다. ㅠ.ㅠ

#define SPLIT 1000
buf[10000];
int size1=0, size2=0, idx=0;

size1= recv(소켓,buf,sizeof(buf),MSG_PEEK); //옵션을 이용해서 수신버퍼의 사이즈를 알아내고 
size2= size1;
do
{
    size2= size1/SPLIT;
    recv(소켓,buf[idx], SPLIT,size);
    idx=idx+SPLIT;    
}while(size2/SPLIT != 0);

int socket_recv(int socket, char* buffer, int size)
{
	int total_received;
	int received;

	assert(buffer);
	assert(size > 0);

	total_received = 0;

	while(size)
	{
		received = recv(socket, buffer, size, 0);

		// 리턴값이 = 0 이면 close되었음을 의미
		// 리턴값이 < 0 이면 오류 발생을 의미
		if(received <= 0) break;

		total_received += received;
		size -= received;

		buffer += received;
	}

	return received > 0 ? total_received : received;
}

제가보기엔 제가 한거랑 그다지 차이가 없어보입니다.
무슨차이가있을까요?

물론저의경우에도 함수로서 정의해도 됩니다.
다만 바로 짠거라 이게 맞는지모르지만..

함수만들고 안만들고 차이말고 또있습니까?

리턴값 사용부분도 다르긴하지만.. 그게 크게다른점일까요?
소스가 작으니 큰비중을 가진게 안보임 @_@

recv(소켓,buf,sizeof(buf),MSG_PEEK) 는 현재 버퍼에 들어와있는 실 데이터 크기만 알아내고 실제 사용자의 버퍼로 복사해 오진 않습니다.

상대가 1000바이트를 보냈으면 내 컴퓨터에 1000바이트가 모두 받아지고 그걸 recv했는데.. 2,3개 조각으로 받아지는건 아닙니다.

상대가 1000바이트를 보냈으면 내 컴퓨터에 200바이트만 받아졌기에 recv 를 하면 200바이트만 받아지고... 잠시 시간이 지난후 recv를 했을때 나머지 800바이트가 여러번에 걸쳐 받아지는 겁니다.

dummy999님의 코드는 루프에 집입하기 전에 버퍼에 받아져 있는 크기를 얻어옵니다.(recv(소켓,buf,sizeof(buf),MSG_PEEK))
그리로 그걸 바탕으로 루프내에서 무언가를 하고 있습니다.
바로 그것이 잘못된 겁니다.

그리고 의도와는 다르게 코드상에 버그도 있습니다.
recv(소켓,buf[idx], SPLIT,size);
마지막 인자는 flag로 size를 넣어선 안되면.
SPLIT를 size 부분에 넣었기에 실제 시도하는 전송 크기는 1000
이며 ... 루프 끝내는 조건도 뭔가 이상해 보입니다.

simpid의 이미지

송지석 wrote:
struct timeval timeout; //setting timeout to socket
timeout.tv_sec=MY_TIMEOUT_VAL;
timeout.tv_usec=0;

result= setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); //timeout 지정

rcv_len= recv(*sock, recv_buf, MY_RECV_LEN, MSG_WAITALL);

이런 식으로 쓰면 될 듯.

이런 좋은게 있는지는 몰랐군요.

저의 경우 필요할때만 아니면 보통 Windows플렛폼에서 개발하기에 이런건 처음봤습니다.

물론 Windows에서는 SO_RCVTIMEO와 MSG_WAITALL이 지원이 안되서 사용할 순 없겠군요.

Linux용으로 개발해 놓은 데몬에 나중에 적용해 봐야 겠습니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.