소켓프로그래밍할때 read()질문입니다.

ch0nn0m의 이미지

서버에서 메시지를 write()함수로 전송을 하고...
클라이언트에서 수신하는건데...
거기서...

str_len=read(sock,message,sizeof(message)-1);
이부분에서 마지막부분에 왜-1을 하는지??
뒷부분 message[str_len]=0;
여기서 문자열 마지막에 0을 넣어서 문자열의 끝임을 지정해주는데...
왜 수신받을때 마지막부분을 왜 안받는건지??
이렇게되면 마지막 전송내용이 0이 되는게 아닌건지??

rgbi3307의 이미지

read()의 리턴값을 아래와 같이 정리할 수 있습니다.

(1) 양수 (읽은 크기)
(2) 0 (End-Of-File 인 경우이거나(문자열끝), 더이상 읽을 데이터가 없음)
(3) -1 (오류발생)

(2)번인 경우에 주의를 해야 하는데,
fd(sock)를 블락킹 모드로 오픈했을때, 0값이 리턴되면,
읽을 데이터가 없는줄 알고 read가 블락될 수 있습니다.
즉, read는 EOF와 0를 구분 하지 못합니다.
따라서, 아래와 같이 코드를 테스트 해보세요~

ssize_t ret;
int len = sizeof(message);
char buf[len];

while (len != 0 && (ret = read (fd, buf, len)) != 0) {
if (ret == -1) {
if (errno == EINTR)
continue;
perror ("read");
break;
}
len -= ret;
buf += ret;
}

위의 코드가 복잡다면, fd(sock)를 넌블락킹모드(O_NONBLOCK)로 오픈해서 read 해 보시길..

저는 read를 소켓 프로그래밍에서 실습한 경험이 없어서,
직접 테스트 해보시고,
결과를 공유해 주시면 더욱 좋겠습니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

eungkyu의 이미지

위 쓰레드는 너무 배가 산으로 가는 경향이 있네요
하나만 여쭤보겠습니다.

Quote:
(2) 0 (End-Of-File 인 경우이거나(문자열끝)], 더이상 읽을 데이터가 없음)

이 표현이 나와있는 reference좀 알려주시기 바랍니다. 잘 이해가 안가서.

rgbi3307의 이미지

Linux System Programming
by Robert Love 책의 30페이지입니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

dorado2의 이미지

http://books.google.co.kr/books?id=k_ocKY0iegsC&pg=PA29&lpg=PA29&dq="Reading+via+read"&source=bl&ots=VfFOJ9oFJx&sig=zslysMFBVgyMQTTtD-_iQRjC1Js&hl=ko&ei=hOZdSsPBLaXk6gPS48z_DQ&sa=X&oi=book_result&ct=result&resnum=1

링크가 길긴 한데, 위 링크에 해당 책의 내용이 나오네요.

말씀하신 예제 코드가 실려 있습니다만, 32페이지 정도까지 읽어도
"(2) 0 (End-Of-File 인 경우이거나(문자열끝)], 더이상 읽을 데이터가 없음) " 의 근거가 되는 내용은 없네요.

'문자열끝' 이 부분을 제외하면 맞는 이야기입니다.

rgbi3307의 이미지

책내용을 충분히 확인하시고 답변하신듯하여 감사합니다.
그리고 바르게 지적해 주셨습니다.

(EOF == 문자열끝) 이라고 책에서 언급하진 않았습니다.

제가 "End-Of-File 인 경우이거나(문자열끝)" 이라고 표현한 부분은 사과드립니다.

다만, EOF == 문자열끝 == NUL 로 주관적으로 판단해 봤고,
이런 의문점을 가지고,
최초질문자에게 이것을 객관적으로 실습해보시고, 그내용을 공유해 달라고 했습니다.
(소켓프로그래밍을 하고 계시니 실습환경이 있고, 궁금해하신 분이 바로 확인할 것같아)

그런데, 객관적 실습을 통한 답변은 없는듯하군요.
더구나 최초 질문자는 너무 조용하시고,
다른분들과 입씨름하는 결과만 되었군요.

단지, 제가 최초 답변한 의도는 이해해 주시기 바랍니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

eungkyu의 이미지

전 EOF == 문자열끝 == NUL 이라는 판단이 잘못되었다는 것을 얘기하고자 함입니다.

EOF와 문자열 끝은 전혀 다른 것이고 read와 문자열은 전혀 관계없는데
연관지어서 얘기하고 있으셔서요

eungkyu의 이미지

왜 여기엔 답을 안해주시나요 ㅠㅠ

eungkyu의 이미지

저도 같은 부분을 읽었는데 간략히 정리하면 다음과 같습니다.

- 파일의 끝을 탐지했을 때 0리턴 (end-of-file) 이 경우 아무것도 읽지 않음
- (blocking call일 경우) 읽을 데이터가 없으면 데이터가 available할 때까지 block됨 (no data available)
- "end of data" != "no data available"
- 문자열 끝이라는 말은 나오지 않음

Quote:
(2) 0 (End-Of-File 인 경우이거나(문자열끝)], 더이상 읽을 데이터가 없음)

이것과는 다르네요.

select99의 이미지

일단 -1 한건 구현하기 나름입니다. 안해도 됩니다. 그리고 리턴과는 무관합니다.

아마도 예상컨데. 통신하는 데이터들은 스트링데이터들 일것으로 보입니다.

따라서 구현한 당사자는 buf 를 정하고 그에 받아들여지는 데이터는 스트링으로 가정하여 특정크기만큼만 읽고

널터미널을 붙이기위해 비워둔것으로 보입니다.

즉, 받은후 즉시(?) 스트링화 하기위해 초기화후데이터를 받거나 받은후 마지막에 널터미널을 붙일수있을겁니다.

rgbi3307의 이미지

-1을 안해도 상관없을 까요?
그런데, 최초의 질문은 왜? -1을 하는가입니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

select99의 이미지

인자로 들어가는값에 -1 한것인데 구현의도는 리턴과 관련없다고 보시면됩니다..
왜 -1했는가는 예상컨데.. 위에적은데로 입니다.

rgbi3307의 이미지

리턴과 관련이 되기 때문입니다.

문자열의 끝은 널(0)이고, 이걸 -1하여 짤라내고, 수신한후,
다시 널(0)을 붙이면, 아무런 문제없이 실행되었기 때문에(실습해 봤기 때문에)
그냥 그렇다라고 하시는것 같은데...

최초 질문은 왜? -1을 하는지였고,
그렇게 하는 이유는 read의 리턴값이 중요하기 때문입니다.

Linux System Programming
by Robert Love 책의 30페이지를 참고해 보시면,
read()의 특성에 대해서 잘 설명하고 있습니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

ymir의 이미지

원래의 질문글을 봤을 때, 수신 buffer 크기에서 마지막 1 byte 를 비워두는 건..
해당 buffer 는 문자열로 사용하고 있기 때문에..
마지막을 반드시 null terminate 하기 위함이라고 짐작할 수 있습니다.

즉, string 데이터가 얼마나 넘어올지는 모르겠지만, 만약 수신 버퍼보다 큰 경우에도..
최대한 null terminate 할 수 있는 마지막 한 byte 는 data 로 채우지 않겠다는 뜻입니다.
read() 의 return 값과는 무관하게, 문자열로 사용하는 수신 버퍼를 보호하기 위한 장치일 뿐입니다.
수신 buffer 보다 큰 경우라면, read 를 여러번 호출해서 적당히 이어붙여서 쓸것 같네요.

단순 binary 데이터라면, buffer size 를 그대로 넘겨줘도 어차피 잘라서 쓰면 되니깐 상관없지만..
문자열 데이터라서 마지막 바이트까지 데이터로 채운다면, 문자열 관련 함수를 이용하여 조작하기 어렵고..
반드시 길이 정보를 같이 넘겨줘야 하니까요.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

rgbi3307의 이미지


최번째, 저의 답글에서 아래의 코드는 해석해 보셨는지요?
read()를 왜 아래와 같이 돌리고 있는지, 답글 한번 달아 주십시요.

while (len != 0 && (ret = read (fd, buf, len)) != 0) {
if (ret == -1) {
if (errno == EINTR)
continue;
perror ("read");
break;
}
len -= ret;
buf += ret;
}

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

jick의 이미지

전형적인 소켓 통신 코드로군요.

...그거랑 원래 코드에서 sizeof(message)에다가 -1 하는 거랑 도대체 무슨 연관이 있으시다는 건지?

이거야 원 꿈보다 해몽이 더 어렵군요.

bushi의 이미지

그러게 말입니다. 계속 봐야 하는 사람도 무척 괴롭습니다.

OTL

rgbi3307의 이미지

아주많이,
"sizeof(message)에다가 -1 하는 거랑" 무척 상관이 있어서 코드를 올렸습니다.

어떤 꿈을 꾸셨고, 어떤 해몽을 하려 하는지요?

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

ymir의 이미지

데이터의 정확성이 중요한 프로토콜 설계에서는 반드시 원하는 데이터 크기만큼 와야 하기 때문에..
가급적 제시하신 코드와 같이 작성하는게 좋습니다.

다만, 본래의 질문으로 돌아가서 살펴 보면..
read 에서 sizeof(buffer)-1 만큼 읽어들이고..
읽어들인 문자열의 마지막을 null terminate 하는 걸로 봐서..
본래의 코드는 단순히 랜덤 길이의 문자열을 취급하는 서버/클라이언트 구조일 가능성이 높습니다.
(즉, 수신 데이터가 얼마나 올지 알 수 없는 케이스일거란 얘깁니다.)
서버는 임의 길이의 문자열을 생성해서 그 길이만큼 write 를 하는 경우겠죠.

길이 정보를 알 수 없는 상태에서 제시하신 코드를 수행하면..
buffer 가 가득 찰 때까지 무한 루프를 돌 수 밖에 없는 문제가 있습니다.

프로토콜이 상세하게 정의된 경우라면, 제시하신 방법대로 read() 를 wrapping 해서..
정확한 크기만큼 데이터가 도착했는지 확인한 후에, 에러 처리를 하면 좋겠지만..
그렇게까지 하지 않았다고 해서 문제될 것은 없겠죠.
데이터의 중요도에 따라 적당히 버려도 되는 경우라면..
이 또한, 이 프로그램의 프로토콜 설계 방식으로 봐도 무방하니깐요..

본래의 질문글에서, return 값이 중요한 경우라면...
str_len 이 >0 인지 확인해서, null terminate 하는지의 여부입니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

rgbi3307의 이미지

"본래의 코드는 단순히 랜덤 길이의 문자열을 취급하는 서버/클라이언트 구조일 가능성이 높습니다."

랜덤 길이의 문자열을 취급할수록 아래 코드와 같이 read()해야 합니다.

len == sizeof(buffer) == sizeof(message) == 문자열크기;

while (len != 0 && (ret = read (fd, buf, len)) != 0) { ... };

0 <= 수신되는 랜덤 문자열길이 <= len
이므로, len에 도달하기 전에 널(0)이 수신될 수 있으니까요.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

ymir의 이미지

서버에서 랜덤 길이의 문자열을 보내는데, 계속해서 길이 정보를 알고 있다고 가정하시는군요.
저 "문자열크기" 는 서로 미리 정의해 두거나..
상대방이 해당 정보를 보내주기 전에는, 얼마나 보낼 지 알 수 없습니다.
그래서 프로토콜 정의를 언급했던 것이구요.

어쨌거나 그 길이 정보를 socket 을 통해 받아오든, 서로 정의해 놓든 간에..
그걸 알고 있어야만 쓸 수 있다는 얘깁니다.

설령 길이 정보를 수신해서 가지고 있다고 한다면...
read() 에서 sizeof(message)-1 을 하는 일은 하면 안되겠죠..
길이 정보를 알고 있는데 왜, sizeof 를 사용하겠습니까..

본문의 코드는, 아래 두 줄 뿐입니다.

str_len=read(sock,message,sizeof(message)-1);
message[str_len]=0;

그리고 제 대답을 요약하자면, 데이터가 아무리 많아도 (수신버퍼-1) 만큼만 데이터를 읽어들이고..
데이터의 끝을 null-terminate 하기 위한 것이라는 겁니다.

위 코드에서는 데이터가 큰 경우에도, str_len == sizeof(message)-1 일꺼고..
이 경우에는 다음 데이터가 더 있는 것으로 간주하고, 다음 데이터를 더 읽거나 하는 등의 처리를 해도 되니까요.

단순히, 노래 제목을 보내면, 가사를 보내주는 서버가 있다고 가정했을 때..
이를 사용하는 client 를 어떻게 작성하면 좋을지 생각해 보시는 것도 좋을 것 같습니다.

서버에서는 write(fd, buf, sizeof(buf)) 로 전송하는게 아니라..
write(fd, buf, strlen(buf)) 로 전송하는 경우입니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

rgbi3307의 이미지

"계속해서 길이 정보를 알고 있다고 가정하시는군요." 로 언급하신것은
ymir님의 생각이지,
저는 그렇게 가정하지 않았습니다.

아래 코드 때문인듯 한데,

len == sizeof(buffer) == sizeof(message) == 문자열크기;

저는 read() 문법을 굳이 언급하지 않아도,
최대로 읽을 수 있는 길이로 당연히 이해하고 있을거라 생각했는데,
이걸 문제로 삼는군요. 본질적인 문제에서 벗어나 씁쓸합니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

ymir의 이미지

아래 댓글들을 종합해서 정리해 보건데..
아무래도 서버와 client 단에서 같은 크기의 buffer 를 사용하고..
양측 다 buffer 크기만큼 read/write 하고 있다고 가정하지 않는 한에는..
납득하기 어려운 말씀만 하고 계십니다.

게다가 read()에서 EOF 와 0x00 을 혼동하고 계신듯 합니다.
0x00 은 그냥 데이터일 뿐입니다.

char buf[10] = "hello\0\0\0\0\0";

위와 같은 데이터가 있을 때, write(fd, buf, sizeof(buf)) 라고 한 경우와..
write(fd, buf, strlen(buf)) 한 경우가 있다면..
client 에서 read() 했을 때의 리턴값은 서로 다릅니다.

첫번째의 경우에는 binary 데이터를 사용하는 케이스로..
같은 크기의 버퍼를 사용하고 있다면, 단순히 read(fd, buf, sizeof(buf)) 를 호출하거나..
제시하신 코드를 사용하면 되겠지만...

두번째의 경우에서라면, 제시하신 wrapping code 를 사용하면 반드시 문제가 생깁니다.
그래서 길이 정보를 사전에 알아야 한다고 말씀드리는 겁니다.

중요한것은 서버에서 과연 얼마만큼의 데이터를 전송했느냐이고..
이를 사전에 효율적으로 알리거나 약속하자는것이 프로토콜 설계의 중심입니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

rgbi3307의 이미지

다양하게 제시하는 것으로 봐서,
ymir 님은 그만큼 생각이 많으신것 같습니다.
덕분에 저는 머리가 아픕니다.
그런데,
ymir님이 4번째 줄에서 언급하신,
"...납득하기 어려운 말씀만 하고 계십니다."는 ,
제가 객관적으로 설명하고자 노력한 부분을 지나치게 왜곡하고 있군요.

천동설과 지동설을 객관적인 입장에서 얘기하고 있는 분위기에서,
자기동설('세상은 나를 중심으로 돌고 있다')을 주장하시는듯...

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

ymir의 이미지

그 노력을 왜곡하거나 폄하하자는게 아닙니다.
단지 그 말씀에는 몇 가지 제약조건이 있어야 성립될 것 같은 부분이 있는데..
그런 부분에 대해서는 별 얘기가 없으시니..
반대로 제가 가정을 제시하고, 그 가정 하에서라면 납득할 수 있다고 말씀드리는 겁니다.

이젠 자기동설 같은건 믿지도 않으니, 이쯤에서 물러서는게 좋을 것 같네요.
언제부턴가 세상이 고장이라도 났는지, 제 맘대로 안 움직인다는 걸 안 이후로는..
자기동설 같은 건 버렸습니다.. =)

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

dorado2의 이미지

전반적인 말씀에 대해서는 동감합니다.

다만 원하는 크기만큼의 데이터가 필요한 경우라도 위 코드처럼 체크하는 것은 다소 위험할 수 있겠네요.
길이 정보를 알고 있더라도 무한 루프에 빠질 위험이 여전히 있지 않을까요?

송신 측에서 잘못된 길이만큼 보낸다던가, 그 외 에러 상황이 발생해서 정해진 크기만큼
전송되지 않는 경우가 발생한다면 무한 루프에 빠지게 되니까요.

예외 상황에 대한 처리가 추가되어야 할 것 같네요.

ymir의 이미지

그래서 정확한 프로토콜 설계를 얘기한겁니다. (물론 구현은 제대로 했다고 가정해야겠죠.. ^^;;)
그리고, non-blocking 으로 처리한다거나..
적당히 timeout 처리를 한다거나..
하는 등의 상황에 맞는 대응방안도 필요하겠죠..

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

rgbi3307의 이미지


while (len != 0 && (ret = read (fd, buf, len)) != 0) {
if (ret == -1) { //오류발생(예외상황)
if (errno == EINTR)
continue; //원하는 길이(len)만큼 수신하지 못한 경우이면, read 계속.
perror ("read"); //그외의 경우에는 오류메세지 출력후 loop break(exit).
break;
}
len -= ret; //수신되는 길이 점검, 0에 도달시, 최대길이만큼 수신했으므로 while 조건 false(loop exit).
buf += ret; //버퍼 메모리 포이터는 수신되는 길이 만큼 증가.
}

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

dorado2의 이미지

read()가 -1을 return하는 경우만 error로 생각하시는 것 같은데, 그렇지 않은 경우도
생각하셔야 합니다.

수신 측에서는 len만큼 받고자 while loop를 도는데, 송신측이 len보다 작게 보냈다면 어떻게 하실 건가요?
무한 loop를 돌게 됩니다. 이러한 예외 상황에 대한 고려가 없네요.

Quote:

(2) 0 (End-Of-File 인 경우이거나(문자열끝), 더이상 읽을 데이터가 없음)

read()가 0을 return하는 경우도 착오가 좀 있으신 것 같네요.
stream 중간에 0x0인 데이터가 있다고 해서 read()가 0을 return하고 종료되지 않습니다.

man 2 read로 확인해보세요.

0를 return하는 경우는 end-of-file이라고 명시하고 있습니다만, 문자열에 대한 언급은 안 보이네요.
EOF와 문자열 끝(0x0)은 당연히 다른 것이구요.

rgbi3307의 이미지

언급하신,
(1)"송신측이 len보다 작게 보냈다면 어떻게 하실 건가요? 무한 loop를 돌게 됩니다."
(2)"read()가 0을 return하는 경우도 착오가 좀 있으신 것 같네요."
(3)"stream 중간에 0x0인 데이터가 있다고 해서 read()가 0을 return하고 종료되지 않습니다."

(1)의 문제는 (2)로 해결할 수 있고,
(2)의 문제를 (3)으로 부연 설명 하신것 같은데,
(3)의 문제는,
컴퓨터 문자표현 표준(아스키코드)을 보면,
0x0은 NULL이고,
read()에서 EOF를 0x0으로 취급하고 있습니다.
이것(0x0)을 수신했을때, read()는 0을 리턴하고 종료되지 않습니다.
정확히는 블락킹모드에서 블락(block, sleep)되는 거죠.
이것을 방지하고자,
while 루프에서
(ret = read (fd, buf, len)) != 0 을 점검하여 False일때 루프를 빠져나옵니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

cinsk의 이미지

ASCII 코드 0은 NULL이라 쓰지 않고 NUL이라 씁니다.

read(2)는 end-of-file (즉 EOF)를 만났을 경우에 0을 리턴합니다. 만약 읽은 내용이 한 바이트이고, 이것이 ASCII 0, NUL이라면 read(2)는 1을 리턴합니다. 맨 앞에서 read(2)는 0과 EOF를 구별하지 못한다고 하셨는데, 뭔가 애매하군요. 다시 한 번 말하지만, read(2)는 EOF일 경우에만 0을 리턴합니다. 읽은 데이터가 ASCII 0번, NUL인지 아닌지는 read(2)의 리턴값과 아무런 상관이 없습니다.

따라서 read(2)를 반복적으로 호출해서 파일 내용 끝까지 읽을려면 "read(...) != 0"보다, "read(...) >= 0"이 낫습니다. "read(...) != 0"을 조건으로 반복문을 돌리면, read()가 -1을 리턴할 때에도 반복을 멈추지 않기 때문입니다.

--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/

rgbi3307의 이미지

read()에서
EOF == 0 == NULL == NUL (아스키코드는 NUL로표기, 이걸 문제삼지는 않는것이 좋겠구요)
로 판단하고 있다고 이해하고 있습니다.
이런 이유로, read()가 EOF와 0을 같이 보기 때문에(EOF==0==NUL), 구별하지 못한다고 한것입니다.

read()가 이값(EOF==0==NUL)을 읽으면 0을 리턴한다고 얘기했구요.

"읽은 데이터가 ASCII 0번, NUL인지 아닌지는 read(2)의 리턴값과 아무런 상관이 없습니다." 라고 언급하신 부분은
납득이 되지 않습니다.
읽은 데이터가 (EOF==0==NUL)이면, read()는 0을 리턴한다고 주장했는데, 이것을 완전히 부정하는 말씀이군요.

그리고,
"read(...) != 0" 이조건을 사용하는 이유는, -1일때 오류처리를 해주기 위함입니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

grassman의 이미지

현재까지 제가 알기로는 EOF (파일의 끝을 나타내는 식별 기호) != 0 (ASCII CODE 0, NUL)로 알고 있습니다. 그리고 EOF는 읽은 데이터 버퍼상에는 저장되지 않고 return 값으로만 판단하는 것으로 알고 있습니다.

실제 EOF == 0으로 구현된 UNIX 계열 OS가 있다면 여러사람들이 알 수 있게 알려 주시면 고맙겠습니다.

tristam의 이미지

소켓통신 안해보신분께서 어찌 저리 당당하게 주장을 하실 수 있는지 궁금할 따름입니다.

다 아니라고 하는데 왜 혼자서만 계속 우기시는건지 .쩝..

0x00 으로 100바이트 만들어서 write, read 해보라고 하고 싶네요. read 리턴값으로 0이 오는지 100이 오는지..

yielding의 이미지

군더더기도 없고 딱 알맞게 설명하셨네요. 질문하신 분은 이 설명으로 쓰레드 닫아도 되겠습니다.
Life rushes on, we are distracted

Life rushes on, we are distracted

haze11의 이미지

추측해보면..
message 는 char[] 정도 될테고.. 전송되는 데이터는 ascii 겠죠?

sizeof(message)-1 는.. NULL-term. 문자열을 위해 마지막 하나는 비워두는게 아닌지요.

spbrain의 이미지

앞뒤를 다 짤라먹고 쓰셔서 추측하기가 뭐해서 구글에서 찾아봤습니다.
Hello 보내는 기초적인 socket 프로그램인것 같은데 맞는지 모르겠네요.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
 
void error_handling(char *message);
 
 
int main(int argc, char **argv)
{
     int sock;
     struct sockaddr_in serv_addr;
     char message[30];
     int str_len;
 
 
    if(argc!=3)
     {
          printf("Usage : %s <IP> <port> \n", argv[0]);
          exit(1);
     }
 
 
    sock=socket(PF_INET, SOCK_STREAM, 0);
    if(sock == -1)
        error_handling("socket() error");
 
 
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_addr.sin_port=htons(atoi(argv[2]));
 
 
    if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1) /* 서버로 연결 요청 */
        error_handling("connect() error");
 
 
    str_len=read(sock, message, sizeof(message)-1); /* 데이터 수신 */
    if(str_len==-1)
         error_handling("read() error");
    message[str_len]=0;
    ptrintf("Message from server : %s \n", message);
    close(sock); /* 연결 종료 */
    return 0;
}
 
void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

위의 코드가 맞는지 모르겠지만 위의 코드에서 볼때는
-1을 하는 이유는 버퍼의 크기가 자신이 잡은 크기의 최대 사이즈로 왔을때 출력시 NULL이 들어갈 자리가 없어서
print할때 문제가 있으므로 마지막에 NULL을 붙이기 위한 코드이라고 사료됩니다.

rgbi3307의 이미지

최초 질문에 대한 답변으로 동의할 수 없습니다.
더구나 아래와 같이 부연설명 했는데,
"-1을 하는 이유는 버퍼의 크기가
자신이 잡은 크기의 최대 사이즈로 왔을때 출력시 NULL이 들어갈 자리가 없어서"

반증하는 예로,
아래와 같이 했을때,

char buf[4] = { "abcd" };

문자열의 끝에 NULL이 들어갈 자리가 없나요?

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

jick의 이미지

* 제가 늙었나 봅니다. 요즘 개그는 너무 어려워서 이해가 안돼... 쿨럭...

* 그리고 저렇게 쓰시면 컴파일이 안될 텐데요...

rgbi3307의 이미지

어떤 개그를 했는지,
나도 웃을 수 있게, 개그 한마디 부탁합니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

jick의 이미지

그것도... 개그를 이해 못한 사람에게 해설을 하라고 하시면... -_-;;

#include <stdio.h>
 
int main(int argc, char **argv)
{
    char s[4] = "abcd";
    puts(s);
    return 0;
}
 
------
$ gcc x.c
$ ./a.out
abcdÿ
$

* 주: 위의 결과는 소비자의 이해를 돕기 위한 예제이며 실제 수행 결과는 시스템에 따라 달라질 수 있습니다.

rgbi3307의 이미지

#include

int main(int argc, char **argv)
{
char s[4] = "abcd";

s[4] = 0;
puts(s);
return 0;
}

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

select99의 이미지

님.. 많은 부분을 잘못알고 계시네요.

위에.. "10년동안 IT관련개발자로...." 오래 되셨는지는 몰라도 잘못알고 계신건 바로 알아야겠지요..

죄송한 말씀입니다만.. 기초적인걸 잘못알고 계십니다. s[4]는 선언메모리 영역을 벗어날 뿐만아니라.

위에 다른님들이 적었듯이 님이 많은부분을 잘못알고 계신겁니다..

예전에도 비슷한상황이 있었지요? 지금도 비슷하군요...

rgbi3307의 이미지

int main (void)
{
char s[4] = "abcd";

s[4] = 0;
puts(s);
return 0;
}

그리고,
"예전에도 비슷한상황이 있었지요? 지금도 비슷하군요..." 라고 언급하셨는데,
예전에 포인터에 관한 글로 비슷한 상황이 있었습니다.
그때, 여러가지 정황을 얘기하기 힘들어... 제가 사과하는 글로 마무리 된듯 합니다.
그런데, 이번에는 제가 사과를 받아야 겠습니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

grassman의 이미지

잘못된 부분에 대해 직설적으로 알려드리겠습니다.
이건 테스트 해보셨습니까?

int main(void)
{
        char flag = 1;
        char s[4] = "abcd";
 
        s[4] = 0;
 
        if (flag == 1)
                puts(s);
        else
                printf("OMG, where's my flag??\n");
 
        return 0;
}

rgbi3307의 이미지

테스트(실습)한 것을 그대로 붙입니다.

-rw-rw-r-- 1 jungjj jungjj 242 6월 11 15:21 p001.c
-rwxrwxr-x 1 jungjj jungjj 11663 7월 15 17:26 str
-rwxrwxr-x 1 jungjj jungjj 11834 7월 16 11:18 str2
-rw-rw-r-- 1 jungjj jungjj 270 7월 16 11:20 str2.c
-rw-rw-r-- 1 jungjj jungjj 151 7월 15 17:26 string.c
-rw-rw-r-- 1 jungjj jungjj 868 7월 15 17:25 string.o
-rw-rw-r-- 1 jungjj jungjj 261 7월 16 11:17 string2.c
-rw-rw-r-- 1 jungjj jungjj 1000 7월 16 11:18 string2.o
-rwxrwxr-x 1 jungjj jungjj 12147 6월 10 11:42 test
-rw-rw-r-- 1 jungjj jungjj 776 6월 10 11:37 test.c
-rwxrwxr-x 1 jungjj jungjj 11654 6월 18 15:55 test2
-rw-rw-r-- 1 jungjj jungjj 212 6월 18 15:55 test2.c
[jungjj@localhost test]$ gcc -o str2 str2.c
[jungjj@localhost test]$ ./str2
abcd
buf=abcd, size=4, len=4
[jungjj@localhost test]$ cat ./str2.c
#include

int main (void)
{
char flag = 1;
char s[4] = "abcd";

s[4] = 0;

if (flag == 1)
puts (s);
else
printf ("0MG, where's my flag??\n");

printf ("buf=%s, size=%d, len=%d\n", s, sizeof(s), strlen(s));

return 0;
}
[jungjj@localhost test]$

"재미있는 분이군요" 하셨는데,
내가 재미있게 한 부분을 지적해 주시기 바랍니다.

참고로, 전 과묵하고 고지식한 사람입니다.
유머감각이 부족하여 요즘 많이 노력중입니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

grassman의 이미지

주의사항으로 local variable 영역으로 stack을 사용하는 머신에서만 의도한 동작이 발생합니다. :)
설마 저 코드를 통과하는 환경이 있을줄은 몰랐군요.

int main(void)
{
        char flag = 1;
        char s[4] = "abcd";
        char flag2 = 1;
 
        s[4] = 0;
 
        if (flag == 1 && flag2 == 1)
                puts(s);
        else
                printf("OMG, where's my flag??\n");
 
        return 0;
}

P.S. 어느 부분이 잘못되었는지는 이미 알고 계신듯 한데요.
이제 적당한 선에서 멈춰 주시기 바랍니다. :)

jick의 이미지

사실 꽤 많은 환경에서 저런 코드가 무사(?)통과합니다. 특히 gcc는 종종 이유없이 로컬변수 사이에 어마어마한 padding을 집어넣는 짓을 잘 하더군요. 저런 코드를 쓰는 프로그래머를 배려해서는 아닌 것 같고, 이유는 모르겠습니다.

(뭐, 몇 년 전에 본 거기 때문에 요즘 gcc는 좀 똑똑해졌을지도 모르겠습니다.)

괜히 C가 더러운 게 아니죠.

rgbi3307의 이미지

답변 태도에 문제가 있어,
저의 실습 내용을 다시 그대로 붙입니다.

C에 대해서 애착을 가지고,
밤낮으로 C를 다루고 있는 사람들은 뭐가 됩니까? C가 더럽다니요?

[jungjj@localhost test]$ gcc -o str3 str3.c
[jungjj@localhost test]$ ./str3
abcd
s=abcd, S2=ABCD, size=4, len=4
[jungjj@localhost test]$ cat ./str3.c
#include

char S2[4] = "ABCD";

int main (void)
{
char flag = 1;
char s[4] = "abcd";
char flag2 = 1;

s[4] = 0;
S2[4] = 0;

if (flag == 1 && flag2 == 1)
puts (s);
else
printf ("0MG, where's my flag??\n");

printf ("s=%s, S2=%s, size=%d, len=%d\n", s, S2, sizeof(s), strlen(s));

return 0;
}
[jungjj@localhost test]$
[jungjj@localhost test]$ uname -a
Linux localhost.localdomain 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux
[jungjj@localhost test]$

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

tristam의 이미지

jick 님께서 말씀하셨던것처럼 위와 같은 코드는 상황에 따라서 오류가 발생할수도, 발생하지 않을수도 있습니다.

RGB님께서 작성하신 코드는 리눅스머신에 GCC로 컴파일한것이고, 제가 아래 샘플로 올린 코드는 HP 장비에서 c89로 컴파일한겁니다.

RGB님의 리눅스머신에서 결과에 이상이 없었다고 해서 제 실험 결과에서 나온 문제를 무시하고 넘어가도 된다는 말씀은 아니시겠죠?
NUL(0x00)이 어디 들어가건 잘 돌아가기만 하면 된다고 하시는건지...주장하시는 바를 잘 모르겠습니다.

작성하신 코드에서도 버퍼사이즈, 문자열길이 모두 4로 나와 있는데..그럼 도대체 ABCD(abcd) 말고 NUL(0x00)이 어디 들어갔다고 생각하시는건가요?
RGB님 실습 결과는 변수사이에 패딩된 공간에 NULL이 들어가서 당장 문제가 발생하지 않을지는 모르겠지만, 다른 환경으로 가면 반드시 문제가 발생하게 되어 있습니다.

코드와 결과만 보여주지 마시고, 정확히 주장하는 바를 말씀해주셨으면 하네요.

rgbi3307의 이미지

기대를 했는데, 아니군요.
C컴파일러 혹은 아키텍쳐에 따라 결과가 다르게 나올 수 있고,
그렇게 코딩하는 것은 위험하다는라는 것에 대해서는 인정합니다.

그러나, 왜 그렇게 나오는지는 이야기 되어야 합니다.
저는 이걸 말해 주기를 기다렸구요.
제가 시원 시원하게 답변 않한점은, 문제제기자의 답변태도에 있었습니다.

암튼, C코드를 gcc를 통하여 어셈블리 코드로 생성하면,

[jungjj@localhost test]$ cat ./str1.c
int main (void)
{
    char buf[4] = { "abcd" };
    buf[4] = 0;
    puts (buf);
    return 0;
}
[jungjj@localhost test]$ gcc -S ./str1.c
[jungjj@localhost test]$ cat ./str1.s
        .file   "str1.c"
        .section        .rodata
.LC0:
        .string "abcd"
        .text
.globl main
        .type   main,@function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        subl    %eax, %esp
        movl    .LC0, %eax      #1
        movl    %eax, -4(%ebp)  #2
        movb    $0, 0(%ebp)     #3
        subl    $12, %esp
        leal    -4(%ebp), %eax  #4
        pushl   %eax            #5
        call    puts            #6
        addl    $16, %esp
        movl    $0, %eax
        leave
        ret
.Lfe1:
        .size   main,.Lfe1-main
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
[jungjj@localhost test]$

위에서 #1에서 #6을 보시면,

#1 문자열("abcd")이 저장되어 있는 .LC0를 레지스터(%eax)에 Store
#2 레지스터(%eax)값을 베이스포인터(ebp) - 4 오프셋에 Store
#3 0(널)값을 베이스포인터(ebp) - 0 오프셋에 Store

위까지 진행하면, 베이스포인터 - 4 에서 -0 에 걸쳐서 문자열 "abcd\0"가 저장

#4 베이스포인터 - 4(최초 시작)를 레지스터(%eax)에 Store
#5 레지스터(%eax) 값을 스택에 Push
#6 puts 함수를 호출하여 문자열 출력

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

powersys의 이미지

"문자열 출력.."

그다음은? 무슨얘기를 하고 싶은건가요?

어셈코드는 뭐하러 붙이셨는지.. 무의미한내용을 붙여놓고.. 자신의 주장을 뒷받침할 내용은 온데간데 없군요..

tristam의 이미지

어셈코드 가져다 붙이셨는데..여전히 주장하는 바가 없으시네요.
왜그렇게 되는지 언급이 되어야 한다면서요. 왜 언급을 안하시는지?

지금 말씀하시는 뉘앙스는 어셈코드상 abcd 뒤에 0x00이 들어가니까 아무 문제 없다는건가요?

벽하고 얘기하고 있는것도 아니고...자기 머신에서 이상없이 나온 어셈코드를 가져다 놓고 설명하시면 다른사람들이
납득하리라고 생각하시는건가요?

제가 HP 머신에서 abcd 뒤에 ? 가 들어가는 어셈코드를 제시하기를 바라시는건가요?

도대체 말이 안통하시는 분 같아서 답답하네요.

이제와서 "난 다 알고 있었는데, 당신들이 어떻게 나오나 보려고 가만히 있었다.." 이건가요? 좀 악취미시네요. 정말 개그하시는건지..

jick의 이미지

1. C에서 잘못된 코드가 한번 잘 돌아가는 건 부지기수입니다. "내 컴퓨터에서 잘 돌아가!"는 전혀 그 코드가 문제없다는 증명이 되지 못합니다. (C가 괜히 더러운 언어가 아니라니깐요...)
1-1. C에서 잘못된 코드가 내 컴퓨터에서 항상 잘 돌아가는 사례 역시 부지기수입니다.

2. 당장 서점에 가서 가장 왕초보를 위한 C 입문서 괜찮은 걸로 하나 구한 다음 처음부터 끝까지 정독하세요.
2-1. Linux System Programming 같은 책은 일단 책장 깊숙히 쳐박아두세요. 지금 이런 책을 읽고 이해할 수 있는 레벨이 아닙니다. 구구단도 모르는 초등학생이 파인만의 물리학 강의 읽고 이해하겠다고 설레발치는 것과 다를 게 없습니다. 고급 레벨의 책은 "이미 당연히 다 아는 것"을 설명하지 않고 넘어가기 때문에, 기초가 안 잡힌 상태에서 읽으면 그 부분을 자신의 멋대로 상상으로 채워서 엉뚱하게 이해하게 됩니다.
2-2. 그래서 말인데, 책을 읽을 때에는 "책에 있는 내용"만 읽으세요. 머릿속으로 상상한 내용을 덧붙여서 나중에 책에 쓰여있다고 우기지 말고.

3. rgbi3307 님은 지금 누구를 가르쳐 줄 수 있는 레벨이 아닙니다. 배열 크기의 개념도 모르는 사람이 뭘 가르친다는 겁니까?

4. 그리고 이건 개인적인 부탁인데... 어디 가서 IT 경력 10년차라고 얘기하지 마세요. 같은 IT 종사자로서 부끄럽습니다.

rgbi3307의 이미지

IT경력 10년에 집착하는군요.
IT경력 10년 사실 부끄럽습니다.
그래서 앞으로 경력을 더 채워 볼려구요.

암튼, 제가 올린 내용에 대해서, 조목조목 순번을 달아서 심하게 충고해 주셔서 감사합니다.
단, 가르치려고는 하지 않았습니다.
온라인상에서는 서로 계급장 떼고 토론 하는거 아닌가요?
달게 받겠습니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

powersys의 이미지

님.. 왜.. 괜한C 를 탓하고 그러세요..

잘못코딩한게 정상으로 돌아간다해서 뭐가 잘못된거라구..ㅡㅡ;;

정상이면 정상으로 돕니다. 잘못코딩한건 잘못될수 있는겁니다.물론 정상으로될수도 있는거죠..

이게 왜.. 잘못된겁니까. 고장난자동차는 항상시동이 안걸려야되나요..ㅡㅡ;;

더구나 저는 의도적으로 저러한코딩을 할수도 있다고 생각합니다. 즉, 구현자체가 비할당영역을 가져와서 사용하는프로그램인데..

안되면 어쩌라는건가요..ㅡㅡ;; 죽건말건 그것자체도 동작의 시나리오중하나인 프로그램말이죠..

grassman의 이미지

역시 증명은 정공법으로 해야 하는데 말입니다. :)
안되는 것을 편법으로 증명하려다 제대로 당한 "모범 사례"입니다.

C언어 표준에서는 인접한 변수 선언이 반드시 연속적인 메모리 영역에 할당되는 것을 보장하지는 않기 때문에 부작용이 발생할 수 있었음에도 Visual C++와 일부 gcc만으로 테스트 해보고 넘어간 것이 위의 결과로 나타났습니다. :) 변수 사이에 padding이 되어 있는 것은 아마 BoF (Buffer Overflow) 공격에 대한 초기 대응책 중 하나였을거라고 짐작됩니다. (물론 저렇게 해도 일부 BoF 공격 외에는 효과는 없습니다만...)

tristam의 이미지

버퍼를 4바이트 잡아놓고, 5번째 바이트에 0x00을 넣으면 그 0x00이 정상적으로 유지가 될까요?
필시 다른 변수영역을 침범해서 버그가 발생할겁니다.
int main (void)
{
char s[4] = "abcd";
char b;

s[4] = 0;

b = '?';

puts(s);
return 0;
}

하면 어떻게 될까요?
컴파일해보지는 않았지만 "abcd?" 라고 찍힐것 같은데요.

컴파일 해봤습니다.

결과네요.

-rw-r--r-- 1 casdv dba 396 2008.08.28 test.c
-rwxr-xr-x 1 casdv dba 20480 2008.08.28 a.out*
-rw-r--r-- 1 casdv dba 89 7¿ù 16ÀÏ 11:43 str.c
-rwxr-xr-x 1 casdv dba 20480 7¿ù 16ÀÏ 11:43 str*
hp3:[/home/XXXXXX/tristam] str
abcd?
hp3:[/home/XXXXXX/tristam]

rgbi3307의 이미지

[jungjj@localhost test]$ gcc -o str4 str4.c
[jungjj@localhost test]$ ./str4
abcd
ABCD
buf=abcd, size=4, len=4
[jungjj@localhost test]$ cat ./str4.c
#include

char S2[4] = "ABCD";

int main (void)
{
char s[4] = "abcd";
char b;
char c;

s[4] = 0;
b = '?';
S2[4] = 0;
c = '!';

puts (s);
puts (S2);

printf ("buf=%s, size=%d, len=%d\n", s, sizeof(s), strlen(s));

return 0;
}
[jungjj@localhost test]$
[jungjj@localhost test]$ uname -a
Linux localhost.localdomain 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux
[jungjj@localhost test]$

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

powersys의 이미지

실습해보면 뭐달라지는게 있나요?
뻔한결과인데 머하러 실습하나요..

저게 정상으로 나오든 비정상으로나오든 님이 주장하시는바가 틀린게 맞아지나요?

비슷한코드가 오류나는걸 보고싶으신건가요? 자신이 확신하는바가 있다면..
구체적으로 말씀을하세요.

yielding의 이미지

기분 나쁘시겠지만 rgbi3307님의 글을 읽으면서 제가 느낀바를 적으면
1) C 언어의 기본이 약하다.
2) 네트웍 프로그램을 제대로 해본 경험이 없다.
3) 대화의 자세가 부족하다. 싸움만 건다.
입니다.

답변을 성의있게 다신건 보이는데 처음부터 질문의 요지에서 빗나갔고 다른 분들의 정확한 답변이 자신의 것과 다르다는 이유로 흥분을 하고 계세요. 다른 분들의 답과 글을 잘 읽어보세요

Life rushes on, we are distracted

Life rushes on, we are distracted

rgbi3307의 이미지

다만, 님들의 답변태도에 맞는 답글을 하고 있을 뿐입니다.

다른분들의 답과 글은 잘 읽어 보고 있습니다.

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

powersys의 이미지

님. 배열.
a[4]; 선언 하면요..

배열맴버가 a[0], a[1], a[2], a[3] 밖에 없습니다.

a[4]는 할당영역이 아닙니다.
즉, 사용하면 사망하는수가 있습니다.
물론 많은경우 안죽고 정상으로 나오기도 합니다.
이걸여테 우기셔서 많은사람들이 황당해하고 있습니다.
너무 기초적인거라 사람들이 개그하는줄 착각한겁니다..

jick의 이미지

정말로 10년동안 IT 하신 분 맞아요?

* 개그죠? 개그 하신 거죠? 제발 개그라고 말해줘요. 아아아아아악~~~~

rgbi3307의 이미지

2469인 것으로 봐서... 이곳에서 많은 활동을 하신분 같은데,
그럴수록 답변을 성의있게 하셔야 되는것 아닌가요?
그리고,
제가 지금가지 얘기해온 내용을 다 읽어 보기는 했는지요?

From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

antaran의 이미지

[antaran-sol10:/export/home/antaran/antaran] cat test.cpp
#include

int main()
{
int n= 1234 ;
char star[128] ;

for(int i= 0 ; i < 128 ; ++i)
{
star[i]= '*' ;
}

printf("before %d\n", n) ;

star[128]= 0 ;

printf("after %d\n", n) ;

printf("adress of star[128]= %x\n", star + 128) ;
printf("adress of n= %x\n", &n) ;

return 0 ;
}
[antaran-sol10:/export/home/antaran/antaran] CC test.cpp
[antaran-sol10:/export/home/antaran/antaran] a.out
before 1234
after 1024
adress of star[128]= 8046fe0
adress of n= 8046fe0
[antaran-sol10:/export/home/antaran/antaran]

보시듯이 star[128]은 char star[128]에선 정한 범위 128개를 넘어섰습니다. C배열 인덱스가 0부터 시작하는 건 알고 계시나요? 0~127이 배열의 공간이고 star[128]은 129번째로서 배열 공간을 넘어섰습니다.
보시듯이 int n의 시작 주소와 동일하죠.
이걸 보시고도 정말 아무렇지 않으시다면 답이 없네요.
휴... 정말 한국 IT의 현주소를 보는 것 같네요. ㅠ.ㅠ

P.S.
뭔가 이미 끝난 얘긴가요? 뒷북 치는게 아닌가 살짝 생각이 드네요.

tristam의 이미지

RGB 님께서 뜬금없이 어셈코드를 제시하신 이후로 별다른 답글을 달지 않고 계셔서요.

왜그렇게 되는지 얘기 되어져야 한다고 하시는데...왜그렇게 쓰면 안되는지 혼자만 모르고 계신것 같아요.

crimsoncream의 이미지

상대방의 글에 틀린 점이 있다고 생각하면 정확히 지적을 해주셔야지 이런 식으로 비꼬는 건 좋은 태도가 아닌거 같군요.
기술적인 내용으로 풀어갈 수 있는 글을 굳이 상대방 감정을 상하게 할 필요는 없을 텐데요.

=======================================================================
오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

spbrain의 이미지

하루 안들어 왔는데 제가 답변한 글이 이렇게 논란이 될지 정말정말 놀랐습니다.
참고로 저는 결과가

[root@localhost ~]# ./b
0MG, where's my flag??
s=abcd, S2=ABCD, size=4, len=4
[root@localhost ~]# uname -a
Linux localhost.localdomain 2.6.25-14.fc9.i686 #1 SMP Thu May 1 06:28:41 EDT 2008 i686 i686 i386 GNU/Linux
[root@localhost ~]#

로 나왔는데 컴파일 버전 문제인지 잘 모르겠네요.
그래서 lst 파일로 뽑아봤습니다.

   1                    .file   "b.c"
   9                .Ltext0:
  10                .globl S2
  11                    .data
  14                S2:
  15 0000 41424344      .ascii  "ABCD"
  16                    .section    .rodata
  17                .LC1:
  18 0000 304D472C      .string "0MG, where's my flag??"
  18      20776865
  18      72652773
  18      206D7920
  18      666C6167
  19                .LC2:
  20 0017 733D2573      .string "s=%s, S2=%s, size=%d, len=%d\n"
  20      2C205332
  20      3D25732C
  20      2073697A
  20      653D2564
  21                .LC0:
  22 0035 61626364      .string "abcd"
  22      00
  23                    .text
  24                .globl main
  26                main:
  27                .LFB2:
  28                    .file 1 "b.c"
   1:b.c           **** #include <stdio.h>
   2:b.c           ****
   3:b.c           **** char S2[4] = "ABCD";
   4:b.c           ****
   5:b.c           **** int main (void)
   6:b.c           **** {
  29                    .loc 1 6 0
  30 0000 8D4C2404      leal    4(%esp), %ecx
  31                .LCFI0:
  32 0004 83E4F0        andl    $-16, %esp
  33 0007 FF71FC        pushl   -4(%ecx)
  34                .LCFI1:
  35 000a 55            pushl   %ebp
  36                .LCFI2:
  37 000b 89E5          movl    %esp, %ebp
  38                .LCFI3:
  39 000d 51            pushl   %ecx
  40                .LCFI4:
  41 000e 83EC24        subl    $36, %esp
  42                .LCFI5:
   7:b.c           ****     char flag = 1;
  43                    .loc 1 7 0
  44 0011 C645FA01      movb    $1, -6(%ebp)
   8:b.c           ****     char s[4] = "abcd";
  45                    .loc 1 8 0
  46 0015 A1350000      movl    .LC0, %eax
  46      00
  47 001a 8945F6        movl    %eax, -10(%ebp)
   9:b.c           ****     char flag2 = 1;
  48                    .loc 1 9 0
  49 001d C645FB01      movb    $1, -5(%ebp)
  10:b.c           ****
  11:b.c           ****     s[4] = 0;
  50                    .loc 1 11 0
  51 0021 C645FA00      movb    $0, -6(%ebp)
  12:b.c           ****     S2[4] = 0;
  52                    .loc 1 12 0
  53 0025 C6050400      movb    $0, S2+4
  53      000000
  13:b.c           ****
  14:b.c           ****     if (flag == 1 && flag2 == 1)
  54                    .loc 1 14 0
  55 002c 807DFA01      cmpb    $1, -6(%ebp)
  56 0030 7513          jne .L2
  57 0032 807DFB01      cmpb    $1, -5(%ebp)
  58 0036 750D          jne .L2
  15:b.c           ****         puts (s);
  59                    .loc 1 15 0
  60 0038 8D45F6        leal    -10(%ebp), %eax
  61 003b 890424        movl    %eax, (%esp)
  62 003e E8FCFFFF      call    puts
  62      FF
  63 0043 EB0C          jmp .L3
  64                .L2:
  16:b.c           ****     else
  17:b.c           ****         printf ("0MG, where's my flag??\n");
  65                    .loc 1 17 0
  66 0045 C7042400      movl    $.LC1, (%esp)
  66      000000
  67 004c E8FCFFFF      call    puts
  67      FF
  68                .L3:
  18:b.c           ****
  19:b.c           ****     printf ("s=%s, S2=%s, size=%d, len=%d\n", s, S2, sizeof(s), strlen(s));
  69                    .loc 1 19 0
  70 0051 8D45F6        leal    -10(%ebp), %eax
  71 0054 890424        movl    %eax, (%esp)
  72 0057 E8FCFFFF      call    strlen
  72      FF
  73 005c 89442410      movl    %eax, 16(%esp)
  74 0060 C744240C      movl    $4, 12(%esp)
  74      04000000
  75 0068 C7442408      movl    $S2, 8(%esp)
  75      00000000
  76 0070 8D45F6        leal    -10(%ebp), %eax
  77 0073 89442404      movl    %eax, 4(%esp)
  78 0077 C7042417      movl    $.LC2, (%esp)
  78      000000
  79 007e E8FCFFFF      call    printf
  79      FF
  20:b.c           ****
  21:b.c           ****     return 0;
  80                    .loc 1 21 0
  81 0083 B8000000      movl    $0, %eax
  81      00
  22:b.c           **** }
  82                    .loc 1 22 0
  83 0088 83C424        addl    $36, %esp
  84 008b 59            popl    %ecx
  85 008c 5D            popl    %ebp
  86 008d 8D61FC        leal    -4(%ecx), %esp
  87 0090 C3            ret
  88                .LFE2:
 143                .Letext0:
DEFINED SYMBOLS
                            *ABS*:00000000 b.c
     /tmp/cc4CBryU.s:14     .data:00000000 S2
     /tmp/cc4CBryU.s:26     .text:00000000 main
 
UNDEFINED SYMBOLS
puts
strlen
printf

위에서 다음 라인에 주목해서

 7:b.c           ****     char flag = 1;
  43                    .loc 1 7 0
  44 0011 C645FA01      movb    $1, -6(%ebp)
   8:b.c           ****     char s[4] = "abcd";
  45                    .loc 1 8 0
  46 0015 A1350000      movl    .LC0, %eax
  46      00
  47 001a 8945F6        movl    %eax, -10(%ebp)
   9:b.c           ****     char flag2 = 1;
  48                    .loc 1 9 0
  49 001d C645FB01      movb    $1, -5(%ebp)
  10:b.c           ****
  11:b.c           ****     s[4] = 0;
  50                    .loc 1 11 0
  51 0021 C645FA00      movb    $0, -6(%ebp)

s[4]의 주소(-6(%ebp))가 flag의 주소와 일치하여 곂쳐지는 문제가 발생하는것을 알수 있습니다. 컴파일 버전문제가 있는지는 확실하지는 않지만 범용적으로 쓸수 없는것은 확실합니다.
spbrain의 이미지

자료 찾아보다가 참고로 Stack의 dummy가 2.96이전과 이후가 틀리다고 하네요
이전은 4byte align으로 이후는 16byte align으로 바뀌었네요. 자료 찾아보면서 공부가되었네요..
자료는 http://ttongfly.net/zbxe/?module=file&act=procFileDownload&file_srl=42563&sid=b5ebcc1967a12bde497e6f7ab567d2a0
보안자료네요..(참고로 한글임)
저도 테스트 해 보았는데

void function(int a, int b, int c)
{
    char buffer1[1];
    char buffer2[7];
}
 
void main() {
    function(1,2,3);
}

결과

  1:c.c           **** void function(int a, int b, int c)
   2:c.c           **** {
  15                    .loc 1 2 0
  16 0000 55            pushl   %ebp
  17                .LCFI0:
  18 0001 89E5          movl    %esp, %ebp
  19                .LCFI1:
  20 0003 83EC10        subl    $16, %esp
  21                .LCFI2:
   3:c.c           ****     char buffer1[1];
   4:c.c           ****     char buffer2[7];
   5:c.c           **** }

20 0003 83EC10 subl $16, %esp
이 곳을 보면 stack메모리가 16byte가 할당되어 8byte가 더미가 생긴다는것을 알 수 있습니다.
오묘한 컴파일러의 세계.ㅡㅡ;

dipole의 이미지

끼어드는게 잘하는건지는 모르겠지만...

int main(int argv, int *arg[])
{
char buf[4] = { "abcd" };
char buf1[4] = { "efgh" };

printf("%s\n", buf1);
printf("%s\n", buf);

printf("%p\n", buf);
printf("%p\n", buf1);

return 0;
}

[root@1600 ~]$ ./a.out
efghabcd
abcd
0xbed21b7c
0xbed21b78

뭔가 문제가 생길수 있는 코드라는 것이 보입니다
그 문제를 수정하기 위해서 어떤 선택을 하시겠습니까?
1. buf1[4]=0; // 코드 삽입
2. char buf1[5] = { "efgh" }; // 배열 크기 수정

1번을 선택한 경우 결과값은
[root@1600 ~]$ ./a.out
efgh

0xbe86cb7c
0xbe86cb78

너는 누구냐?

mach의 이미지

<span>str_len</span>=read(sock,message,sizeof(message)-1);
이부분에서 마지막부분에 왜-1을 하는지??

해석: message라는 버퍼의 마지막 바이트는 수신시에는 항상 비워두겠다. 수신메시지 버퍼크기가 N이라면 최대 N-1만큼만 수신하겠다는 얘기지요. 마지막 1바이트는 수신용도로는 사용하지 않겠다라는 의미이기도 합니다.
이 1바이트는 수신용도 외에, 특별한 목적(후 처리시의 편의 등)으로 사용하겠다는 얘기입니다.

뒷부분 message[<span>str_len</span>]=0;
여기서 문자열 마지막에 0을 넣어서 문자열의 끝임을 지정해주는데...

혹시나 해서 기술하는데,...
str_len의 리턴값은, 다음 3가지 부류를 가질 수 있는데
1) -1
read() 시스템 호출 오류나 시그널발생시
2) 0
원격지가 연결종료, 유닉스/리눅스에서는 소켓도 파일로 보게 하는 개념으로 인하여 EOF로 해석하기도 하지요.
3) 0< str_len <= sizeof(message)-1
이는 무엇인가 읽혔다는(1바이트이상~최대이하) 것을 의미합니다. 스트림의 특성상 그 리턴값은, 최소 1보다 크거나 같고, 최대 읽고자 하는 바이트보다 작거나 같은 크기를 가질 수 있습니다.

질문에서 예시한 코드의 경우 "message라는 변수의 크기보다 1 만큼 작은 값을 최대값으로 지정"했으므로, message의 크기가 10이라면, 아래와 같이 유추됩니다.
예시)
char message[10]; // 메시지 크기는 최대 10 !
...
str_len=read(sock,message,9); // sizeof(message)-1 == 9 , 항상, 최대 메시지 크기보다 1작은 값, 즉, 9보다는 작거나 같은 만큼 읽겠다.
/* 혹시나해서 기술하자면, 스트림에서 읽고 있으므로, read()가 성공적으로 데이터를 수신해도 그 크기는 1~(최대읽고자 한크기)가 될 수 있음을 명심해야 합니다. message[0]~message[8]까지 9바이트가 사용되겠습니다.
*/
if(str_len == 0 ) // 연결종료 처리
{
//위의 2)번 상황
...
message[str_len] = 0; // message[0] = 0; 이므로 별 문제는 없겠습니다만, 이 부분은 이미, 연결종료 이후의 상황이겠지요. 처리 전략에 따라 차이가 있겠습니다.
}
else if (str_len < 0 ) // read()오류 또는, 서버가 운영체제시스템을 경유하여 시그널을 수신함, 이에 따른 처리
{
//위의 1)번 상황
...
message[str_len] = 0; // message[-1] = 0; 이므로 이 부분이 수행된다면 문제가 있습니다.
}
else {
// 위의 3)번 상황
...
message[str_len] = 0; // 1바이트 수신시 : message[1] = 0; ==> 문제 없음
// 2바이트 수신시 : message[2] = 0; ==> 문제 없음
// ...
// 최대 수신가능한 크기 수신시 즉,
// 위의 가정에 의해, sizeof(message) == 10이고, <== 변수 선언에 의해
// 최대 가능한 리턴값은 read()의 세번째 파라메터인 sizeof(message)-1에 의해 9이므로,
// message[9] = 0; 은 문제를 일으키지 않습니다.
// 0~8까지 최대 9바이트의 수신후 10번째(인덱스 9)에는 0을 채우겠다는 얘기지요.

}
<span>왜 수신받을때 마지막부분을 왜 안받는건지??</span>
이 경우에는, 0으로 끝나는 문자열(c언어의 문자열 개념)을 처리하기 시도로 보입니다.
즉, 수신버퍼를 서비스처리버퍼로 동시에 사용하고자 하는 의도가 엿보입니다.
이는, 스트링위주의 프로토콜을 설계하고 이를 구현하고자 한 시도에서 많이 사용되는 방법중 하나입니다.
그러나, 바이너리 위주/혼용 프로토콜에서는 이런 접근은 곤란한 경우를 유발할 수 있습니다.
또한, 적법한 클라이언트(스트링 위주로 작성된)와 동작시에는 무리없겠지만, 악의(?)를 가진 클라이언트에 의해 빈틈이 생기기 쉬운 서버 코드 스타일입니다. 가급적, 통신 루틴의 하부구조에서는 I/O되는 데이터가 바이너리라고(!) 가정하고 프로그램해야 안전한 코드가 될 확률이 높아집니다.
학습등의 특별한 목적이나, 비정상 동작해도 좋다 또는 문자열만 송수신된다등 확신등을 가정하는 경우에는 위 코드 스타일을 사용해도 좋겠습니다. 그렇지 않다면, 가끔(?) 기대하지 않은 상황을 접할 수 있을 수 있음을 말씀드립니다.

 <span>이렇게되면 마지막 전송내용이 0이 되는게 아닌건지??</span>

이는 괜한 우려입니다. 심지어 클라이언트가 "aaa\0bbb"로 보내도 수신된 원본 데이터에 영향은 생기지 않겠지요. 단지, 후 처리시에, 중간에 있는 '\0' 때문에 좀... 고민되겠습니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

freestyle의 이미지

char message[5];
int str_len;
 
str_len = read(sock, message, sizeof(message));  // sizeof에서 -1을 안 했을 때
 
"123"이라는 문자열이 송신측에서 전송되면,
 
message 메모리 구조
['1']['2']['3']['\0'][ ] // str_len = 4
 
message[4] = 0;
 
message 메모리 구조
['1']['2']['3']['\0']['\0']
 
str_len은 4인데, 실제 출력해보면 문자 3개가 나오는 게 싫었던 거겠죠?
 
 
"1234"이라는 문자열이 송신측에서 전송되면,
 
message 메모리 구조
['1']['2']['3']['4']['\0'] // str_len = 5
 
message[5] = 0; // index 5는 message 영역을 초과하므로 문제되는 코드입니다.

그렇다면 message[str_len-1]을 하면?
"12345"가 수신측에서 전송되면,
 
message 메모리 구조
['1']['2']['3']['4']['5'] // str_len = 5
 
message[5-1] = 0;
 
['1']['2']['3']['4']['\0'] // 문자열로 완성되는군요.
 
""가 수신측에서 전송되면,
 
message 메모리 구조
['\0'][][][][] // str_len = 1
 
message[1-1] = 0;
 
 
그럼 sizeof(message)-1과 str_len-1과는 완전 동일한 건가요?

--------------------------
Go to the U-City

----------------------------------------------------------------------------------------
Don't Feed the Trolls!
----------------------------------------------------------------------------------------

ymir의 이미지

read/write 는 buffer 에 저장된 데이터 중에서, 지정한 길이만큼의 데이터를 보내거나/읽어들입니다.
물론 중간에, interrupt 등으로 return 하는 case 도 있기 때문에, n bytes 를 read/write 한다고 해서..
n bytes 가 return 된다는 보장이 없습니다.

그래서 데이터의 정확성이 보장되어야 하는 케이스에는, 여러번 read/write 를 호출하여 원하는 만큼의 데이터
를 모두 쓰거나, 읽었는지 확인하는 코드를 만들기도 합니다.

peer 가 5 바이트를 보냈다고 해도, 내가 read 에서 4 만큼만 읽었다면..
내 buffer 에는 4 바이트이 데이터가 들어가고, 한 바이트는 socket buffer 에 남아 있습니다.
그래서 다시, read 를 호출해야 남은 데이터를 마저 읽어들일 수 있습니다.

Quote:

message 메모리 구조
['1']['2']['3']['\0'][ ] // str_len = 4

전송된 데이터가, '1','2','3' 의 3 byte 이기 때문에..
read 는 3 을 리턴할 것이고, 따라서 str_len 은 3이 됩니다.

message 에는 '1','2','3' 만 들어가 있고, 나머지 바이트들은 message 버퍼 초기상태 그대로입니다.
\0 으로 초기화 하지 않았다면 undefined 입니다.

그래서 읽은 길이 뒤에 \0 을 추가해서 null-terminate 해주어야, 안심하고 문자열 함수로 쓸 수 있습니다.

그런데, 서버에서 5 bytes 를 보냈고, 내가 read() 에서 최대 버퍼크기인 5 bytes 를 읽도록 해서, message 버퍼에 5 bytes 가 다 들어가 버렸다면..
string 조작 함수를 쓰기 위해서는 null-terminate 를 해 주어야 하는데...
이미 message buffer 가 가득차서, 해 줄 수 있는 공간이 없게 됩니다..
(이런 경우에는 더 큰 버퍼로 복사해서 사용하거나, 길이 정보를 딸려 보내서 strn* 과 같은 함수들만 사용하도록 해야 할겁니다.)

그래서 read() 의 buffer 크기에 -1 을 해서 일부러 한 바이트 줄여, 마지막 바이트에는 데이터로 채우지 않는 것입니다.

만약 서버가 null-terminate 포함하여 strlen("123")+1 바이트 만큼의 데이터를 보내기로 약속한 경우라면..
read 버퍼에서 -1 을 해줄 필요는 없겠죠. (단, 양쪽의 송/수신 버퍼 크기는 동일하고, null 을 포함한 메시지 크기는 버퍼보다 작거나 같다는 전제하에서..)

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

freestyle의 이미지

음...

Quote:
"123"이라는 문자열이 송신측에서 전송되면,

message 메모리 구조
['1']['2']['3']['\0'][ ] // str_len = 4

위의 제 글을 인용해 답변해 주신 아래 글에서

Quote:
전송된 데이터가, '1','2','3' 의 3 byte 이기 때문에..
read 는 3 을 리턴할 것이고, 따라서 str_len 은 3이 됩니다.

라고 해주셨는데,

"123"이라는 문자열을 송신측에서 보내면,
'1', '2', '3', '\0' EOF 같은 신호 이렇게 전송이 되잖아요 ㅡㅡa

그러니까 제가 문자열이라고 표현한 이유는 송신측에서 '\0'을 포함한 데이터를
보냈다고 말한 것인데...

그럼 '1', '2', '3', '\0'이 들어가는 게 맞지 않나요?

-------------------------------
Go to the U-City

----------------------------------------------------------------------------------------
Don't Feed the Trolls!
----------------------------------------------------------------------------------------

ymir의 이미지

문자열에 terminate 문자 '\0' 는 포함되지 않습니다.
그래서 위 쓰레드에서 write 할 때 strlen("123")+1 을 하기로 했다면.. 이라고 언급한 것이구요..

아시다시피 데이터들은 모두 bit stream 의 연속입니다.

이를 읽고 해석하는 방법에 따라, char, short, int 등으로 이름지어서 사용하는 것이구.
socket 에 들어가 있는 데이터들 역시 bit stream 에 불과하기 때문에..
사실 socket 상에 'string data'라고 하는 건 엄밀히 말해서 송수신되는 모든 데이터들은..
char 배열에 저장하여 문자열 관련 함수로 조작할 수 있는 데이터들이다.. 정도로 약속한 것이라고 해석할 수 있습니다.

아래에서 편의상 문자열이라고 얘기하고 표시하고 있기는 하지만, socket 입장에서는 n bits 의 길이를 가진 스트림일 뿐입니다.

본래의 내용으로 돌아와서..

char msg[5] = "123\0\0";

라는 데이터가 있고.. 데이터 송 수신에 별다른 문제가 없다고 했을 때..

서버측에서 write(fd, msg, sizeof(msg)) 라고 한 경우에는...
socket 에는 '123\0\0' 이라는 데이터가 들어가게 됩니다.

client 측에서 char buf[5] 라는 버퍼가 'abcde' 라고 초기화 되어 있다고 가정했을 때..

read(fd, buf, 1);
read(fd, buf, 3);
read(fd, buf, sizeof(buf));

를 각각 호출한 경우에는...

각각 '1bcde', '123de', '123\0\0' 가 들어가게 되고, 실제로 읽은 데이터 크기 1, 3, 5 를 리턴하게 됩니다. 이 경우 strlen(buf) 는 undef, undef, 3 이 되겠죠..

만약 write(fd, msg, strlen(msg)) 라고 한 경우에는...
'123' 이라는 데이터만 들어가게 되고..

read(fd, buf, 1);
read(fd, buf, 3);
read(fd, buf, sizeof(buf));

를 각각 호출한 경우에는...

각각 '1bcde', '123de', '123de' 가 들어가게 되고, 실제로 읽은 데이터 크기 1, 3, 3 를 리턴하게 됩니다.
읽어들이지 않은 데이터들은 socket 에 모두 그대로 남아 있고, 다시 read 를 호출하여 읽어들일 수 있습니다.
이 경우 strlen(buf) 는 모두 undef 입니다. 그래서 읽어들인 데이터의 뒤에 '\0' 을 넣어 명시적으로 terminate 시켜주는 것입니다.

그래서 사전에 이러한 사항들을 세부적으로 정하고 프로그램을 작성하게 되는거죠.

데이터를 송수신 할 때에, 구현하기에 따라 다르겠지만, '문자열 길이만큼만 전송하고 null terminate 는 알아서 한다' 라던가, 반드시 '문자열길이+1(\0) 만큼의 데이터를 송수신 하기로 한다' 라던가 하는 등의 형태로 얼마든지 정의할 수 있습니다.

특별한 약속이 없다면, socket 에서 읽은 문자열로 추정되는 데이터의 끝에 '0\' 가 포함되어 있는지 확인해서, 처리를 해주면 되겠죠. 본래의 질문글에서 제시한 것과 같은 형태로 말이죠..

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

mach의 이미지

"123"이라는 문자열을 송신측에서 보내면,
'1', '2', '3', '\0'<span> EOF</span> 같은 신호 이렇게 전송이 되잖아요 ㅡㅡa

스트림 TCP에서는 EOF는(또는, 다른 유사 의미를 가지는 문자 조차) 전송되지 않습니다.
* 참고로, 원격지의 연결종료로 인해, 리턴되는 read()시의 0값 리턴도, 원격지로 부터 온 값이 아니고, 로컬 운영체제가 0을 리턴해주는 것입니다.

그러니까 제가 문자열이라고 표현한 이유는 송신측에서 '\0'을 포함한 데이터를
보냈다고 말한 것인데...
 
그럼 '1', '2', '3', '\0'이 들어가는 게 맞지 않나요?

\0까지 처리하기 위해, +1을 해주어야 할 것입니다.
예를들어,
* 송신측 코드 예시
char *str = "123"; // 내부표현은 ['1']['2']['3']['\0'].....
...
write(sock, str, strlen(str)+1);
...

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

mach의 이미지

그럼 sizeof(message)-1과 str_len-1과는 완전 동일한 건가요?

라는 질문에 답변드리겠습니다.

1) sizeof(message)-1은 수신용으로 프로그래머가 미리 할당한 버퍼의 크기 값에서 1빠지는 값이고,
2) str_len - 1은 수신된 데이터 길이 - 1 입니다.

예시했던 예제를
그대로 사용해서 다시 설명드리자면,......

char message[5];
int str_len;
 
str_len = read(sock, message, sizeof(message));  // sizeof에서 -1을 안 했을 때

위 코드에서, sizeof(message)는 5라는 상수값을 가지게 된다.
따라서, 위에서

...
str_len = read(sock, message, sizeof(message));  // sizeof에서 -1을 안 했을 때
...

라는 코드는 다음과 완전 일치하는 코드이다.
...
str_len = read(sock, message, 5));  // sizeof에서 -1을 안 했을 때
                                                    // 아울러, read()의 해석은 최대 5바이트(1~5)를 읽어라! 이며,
                                                    // 5바이트를<span> 반드시 </span>읽어라! 라고 해석하면 안됩니다.(이런 의미를 주려면, 달리 부가 코드가 더 필요합니다)
...

그런데, str_len이 가질 수 있는 값의 범위는 -1, 0이라는 두가지 특별한 경우를 제외하고도,
정상 수신의 결과로, 1,2,3,4,5라는 5가지 값을 가질 수 있다.
즉, 5-1 == { 1 or 2 or 3 or 4 or 5} - 1라는 질문으로 해석됩니다.(-1,0의 경우 제외해도)
한가지 경우, 5바이트 수신시를 제외하고는 어긋나는 결과입니다.
따라서, 아래에 인용된, 다음 질문의 답변은 '아니요' 입니다.

...
그럼 sizeof(message)-1과 str_len-1과는 완전 동일한 건가요?

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

tristam의 이미지

이렇게되면 마지막 전송내용이 0이 되는게 아닌건지??

이부분에 대한 부연 설명을 드리자면 수신 버퍼(message)를 10바이트 할당했다면, 10-1=9 크기 만큼 읽기 때문에 실제로
소켓 버퍼에서 수신 버퍼로 읽어들이는 크기는 9바이트입니다. 따라서 송신자가 10바이트를 보냈다고 한다면 9바이트만
읽었기 때문에 나머지 1바이트는 소켓 버퍼에 남아있게 되죠.

9바이트를 읽고 10번째에 0x00을 넣기 때문에 마지막 데이터가 0이 되는것은 아닙니다.

소켓 버퍼의 나머지 1바이트를 버리느냐 사용하느냐는 수신자 마음이겠죠.
그러나 보통 얼마를 수신해야 할지 아는 상태로 프로토콜 설계가 되는 경우가 대부분이기 때문에 저런 경우 나머지 1바이트도 읽게 될겁니다. 뭐 이는 전적으로 설계자의 마음이죠. 얼마를 수신해야 할지 모른다면 EOF가 올때까지 읽어야 할거구요.
나머지 1바이트가 필요 없으면 버리면 됩니다.

freestyle의 이미지

수정

-------------------------------
Go to the U-City

----------------------------------------------------------------------------------------
Don't Feed the Trolls!
----------------------------------------------------------------------------------------

spbrain의 이미지

답글로 다시 올렸습니다. 글 지우는 기능 어떻게하나요...없나요??ㅠㅠ

rocketman의 이미지

ㅎㅎ

댓글 달기

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