네트워크 플밍에 대한 질문이예요(소켓관련~~)

익명 사용자의 이미지

프락시 서버를 구현하려구 플밍을 하구있는데요

i/o multiplexing 방법을 사용하려구 합니다

TCP/IP 소켓프로그래밍 C version에 있는 코드입니다.

이 책에 있는 i/o 멀티플렉싱 소스코드를 참조하실 분은

www.mkp.com/socket를 가보시면 TCPEchoServer-Select.c를 참조하시면 됩
니다.

이 책의 설명을 보면 select( )함수가 식별자 리스트를 준비하구요

그 식별자에 대한 입.출력을 연산이 블록되지않을 것이라는 보장아래

진행시킨다고 나와있습니다..

근데 저는 블록킹이 되던데요..... 도대체 어떤 방식으루 넌블록킹을

멀티플렉싱이 지원하는지 알구싶구요

잠깐 소스코드를 보시면요....
{
if ((recvMsgSize = recv(clntSocket, Buffer, BUFSIZE, 0)) < 0)
DieWithError("recv() failed");

while (recvMsgSize > 0)
{
if ((recvMsgSize = recv(clntSocket, Buffer, BUFSIZE,0)) < 0)
DieWithError("recv() failed");
}
closesocket(clntSocket);
}

while루프안에서 recv가 있잖아요 이 recv가 클라이언트로부터 데이터를

계속 받으려구 블록킹이됩니다. 블록되지않구 진행이 되어야 하는거 아닌
가요?

저의 환경은 프락시 서버구현중에 클라이언트 사이드를 구현하려구 하는거
거든요 그럼 답변 부탁드립니다..

그리구 멀티플렉싱 많이 찾아봤는데요 정확하게 어떻게 넌블록킹을

지원하는지에 대한 설명이 나와있는 곳은 없더군요 설명 자세하게 나온

싸이트 있음 알려주시구 편안한 밤 되세요~~^^

익명 사용자의 이미지

코드 내에 select ()가 없네요.. -_-;
select ()를 이용하여 파일기술자들의 I/O변화를 감지할 수 있습니다.
fd_set이라는 구조체를 사용하여 I/O를 감시할 파일기술자들을 지정합니
다.
fd_set의 사용의 편의성을 위해 다음과 같은 매크로가 있습니다.

FD_ZERO fd_set 초기화(?)
FD_SET fd_set에 파일기술자 추가
FD_CLR fd_set에서 파일기술자 제거
FD_ISSET 해당파일기술자의 I/O에 변화가 있는지 없는지....

fd_set에 등록(?)된 파일기술자들에 대해 I/O변화가 일어나게되면
블럭되어있던 select ()는 리턴을 하게되고, 등록된 파일기술자들을
FD_ISSET을 이용하여 비교해서 처리(read/recv)하면 됩니다.
select ()는 읽기/쓰기/예외상태변화를 감지 할 수 있고,
님께서 원하시는기능은 읽기겠죠.
서버소켓의 read가 감지되었다면 accept ()하여 클라이언트의 접속을 받
고,
클라이언트소켓의 read가 감지되었다면 read/recv하시면 되겠습니다.

그럼 이만..

익명 사용자의 이미지

먼저 답변 감사드립니다.. 근데 select() 용법은 저두 알구있는데요

저 위의 코드에서 안나타난건...
{
if ( select( ) == 0 )
{
//select를 감지 못한것에 대한 코드
}
else
{
//select를 감지했을때의 코드
//여기에 제가 위에쓴 코드가 들어가거든요
(정확하게는 함수호출..)
}
}

이런식으루 작성을 했는데 그냥 안되는 부분만 보여드린거구요

정확한 문제는 recv에서 블록이 되어버린다는겁니다.

클라이언트(저는 웹브라우저를 클라이언트로 쓰구요)로부터 계속

recv를 받으려구 server측이 블록이 되어서 더이상 진행이 안되거든요

non-blocking recv방법을 써야하는건가요?

정확한 i/o 멀티플렉싱의 사용법을 잘 모르겠습니다.

익명 사용자의 이미지

if ( select () == 0 )

이 부분이 좀 의아하네요. select ()가 0을 리턴하는경우는 타임아웃이
설정되어 타임아웃될때라고 알고있는데요..

그리고 첫 글에서의 코드를 보면 else부분에서 같은 소켓기술자로 recv
를 두번 합니다. while ()이전의 recv호출시 클라이언트로부터 전송된
데이터를 모두 recv했다면 while ()안의 recv는 블럭되겠죠.
그리고 while ()은 클라이언트의 연결이 종료되거나 소켓에러가 나기 전
까지는 계속 recv를 호출하므로 해당 연결이 종료되기 전까지는 계속
블럭/recv됩니다. (구조적인 약간의 문제가 있는듯... )

물론 recv를 setsockopt ()를 이용해서 타임아웃이나 넌블럭으로 설정 할
수 있습니다만....

만약 코드의 첫번째 recv에서 블럭이 되어 진행이 되지 않는다면 select()
에서 감지한 read변화에 해당하는 클라이언트 소켓이 아닌 다른 클라이언
트의 소켓기술자로부터 recv를 하게되어 블럭이 될 수도 있습니다.

백문이 불여 일타라구... 예제로 한번 짜 봤습니다.
약간의 버그나 문제점등이 있을 수 있습니다.

#include
#include
#include
#include
#include

#define CLIENT_COUNT 10
#define SERVER_PORT 5555

int main ()
{
int i = 1, len, client_num = 0;
int server_fd, client_fd[CLIENT_COUNT];
char recv_buff[2048] = { '\0' };
struct sockaddr_in server_addr, client_addr;
fd_set read_fdset;

if ( ( server_fd = socket ( PF_INET,SOCK_STREAM, 0 ) ) < 0 )
{
perror ("server socket create fail");
return 1;
}

setsockopt ( server_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &i,
sizeof ( i ) );

memset ( &server_addr, 0x00, sizeof ( server_addr ) );
len = sizeof ( server_addr );
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl ( INADDR_ANY );
server_addr.sin_port = htons ( SERVER_PORT );

if ( bind ( server_fd, (struct sockaddr *) &server_addr, len ) <
0 )
{
perror ("bind fail");
return 1;
}

listen ( server_fd, 5 );

while ( 1 )
{
FD_ZERO ( &read_fdset );
// 서버소켓 파일기술자 등록
FD_SET ( server_fd, &read_fdset );

// 클라이언트소켓 파일기술자 등록
for ( i = 0 ; i < client_num ; i ++ )
FD_SET ( client_fd[i], &read_fdset );

if ( select ( 10, &read_fdset, 0, 0, 0 ) < 0 )
{
perror ("select fail");
return 1;
}

// 서버소켓 파일기술자에서 read가 감지되면..
if ( FD_ISSET ( server_fd, &read_fdset ) )
{
if ( ( client_fd[client_num ++] = accept ( server_fd,
(struct sockaddr *) &client_addr, &len ) ) < 0 )
{
perror ("accept fail");
return 1;
}

if ( client_num > CLIENT_COUNT )
{
printf ("Client Socket Full.\n");
close ( client_fd[-- client_num] );
}
// 접속후 작업..
}
// 클라이언트소켓 파일기술자에서 read가 감지되면..
else
{
// read가 감지된 클라이언트소켓 파일기술자를 찾는다.
for ( i = 0 ; i < client_num ; i ++ )
{
if ( FD_ISSET ( client_fd[i], &read_fdset ) )
{
memset ( recv_buff, 0x00, 2048 );
// Disconnection or Socket Fail
if ( recv ( client_fd[i], recv_buff, 2048, 0 )
<= 0 )
{
close ( client_fd[i] );
client_fd[i] = client_fd[-- client_num];
continue;
}
// recv이후 처리
printf ("Recv [%d] %s\n", i, recv_buff );
}
}
}
}

close ( server_fd );

return 0;
}

PS 다 써놓고 나니 좀 비는것이 있네요..
select ()의 첫번째 인자는.. fd_set에 들어가는 소켓기술자들중
가장 큰 번호여야합니다. 간단히 테스트용으로 그냥 10으로
했는데요..
이 부분은 가장 큰 파일기술자 번호로 바꿔야합니다.. ^^;
역시.. 버그소년이라서리.. ^^;

익명 사용자의 이미지

select ()의 첫번째 인자는.. fd_set에 들어가는 소켓기술자들중
가장 큰 번호여야합니다.
이것에 대해서 궁금해서 여쭈어 봅니다.
모든 책에 위의 글처럼 나와있느데..이것의 의미가..
sock_fd=socket(...,..,..);
이런게 반복될때마다..프로세서가 리턴해 주는 sock_fd값은 이전값보다
어느정도(??) 더해진 값으로 리턴되기 때문에..라고 생각하면 무리가 잇는
것인가요?
-->참고
책을 찿아보면 , select가 세 벡터(아마두 read,write,exception)의 모든
가능한 벡터위치들을 탐색하는 것을 피하게 하기 위하여...max를 둔다.
<--
이런 질문을 드리는 이유는 [2061]에 질문을 올리고, 생각하니,
이 sock_fd를 메세지로 전달해도 , 다른 프로세스에서 통신이 가능한가?
지금 짜고 있는데요..가능하다면 왜..
가능하지 않다면 왜??
그런것인지...알고 싶어서 입니다.

그럼 무식한(^^*)중생을 위하여....답변 부탁드립니다
^^*

익명 사용자의 이미지

음... 테스트 해 보심 아시겠지만..

소켓이나 기타 파일기술자를 하나 생성하면

처음에는 3입니다.

0은 표준 입력, 1은 표준 출력, 2는 표준에러입니다.

기본적으로 프로세스가 생성되면 위의 파일기술자 3개는 기본적으로

생성되며 (데몬형태의 실행을 위해 위 3개의 파일기술자를 닫을 수 있

습니다.) 실행 이후 파일기술자가 생성되면 생성될때마다 정확히 1씩

증가된 파일기술자를 리턴합니다.

그러므로 질문하신것처럼 전혀 다른 프로세스에게 파일기술자 번호를

알려준다고 해도 입출력은 할 수 없습니다.

단, fork ()형태로 엄마, 자식형태라면 가능합니다.

(fork는 똑같은 프로세스를 복제하고, 메모리도 독립적으로 사용하지만

파일기술자는 같이 사용합니다. )

PS 헐헐.. 또 수정을 하게되네요.. ^^;
select에 파일기술자중 가장 큰 것을 넣는 이유라면..
기본적으로 프로세스가 열수있는 파일기술자가 제한이 되어있습니다.
보통 1024정도... select란 녀석이 0부터 1024까지의 모든 파일기술
자를 몽땅 검사하게 되면 힘들겠죠..(?) ^^;
파일기술자의 번호 매김이 위에설명한 바와같이 되므로 가장 큰 수
까지의 파일기술자면 검사하고 있으면 좀더 수월하기 때문입니다.
그럼 이만.. ^^;

익명 사용자의 이미지

버그소년 wrote..
음... 테스트 해 보심 아시겠지만..

소켓이나 기타 파일기술자를 하나 생성하면

처음에는 3입니다.

0은 표준 입력, 1은 표준 출력, 2는 표준에러입니다.

기본적으로 프로세스가 생성되면 위의 파일기술자 3개는 기본적으로

생성되며 (데몬형태의 실행을 위해 위 3개의 파일기술자를 닫을 수 있

습니다.) 실행 이후 파일기술자가 생성되면 생성될때마다 정확히 1씩

증가된 파일기술자를 리턴합니다.

보통의 경우에는 프로세스가 생성되면 0, 1, 2 세개의 디스크립터가

기본적으로 오픈되어 있는 상태지요.

이건 프로세스 생성시에 디스크립터가 생성되어서 그런 것이 아니라

프로세스를 생성한 프로세스 (보통은 쉘이 되겠죠?) 의 디스크립터가

이 세개를 오픈해 놓고 있어서 그런 것입니다.

그리고 대부분의 경우 open()이나 socket() 등으로 디스크립터를

할당받으면 이전에 받은 것에 비해서 1이 증가하지만,

close() 등을 사용해서 오픈되어 있던 것을 close() 했다면

이전 디스크립터에 비해 1이 증가된 값이 나오지 않을 수도 있죠.

디스크립터는 디스크립터 테이블의 인덱스이고,

디스크립터 생성시는 디스크립터 테이블에서 사용가능한

가장 작은 인덱스를 쓰게 되니까요.


그러므로 질문하신것처럼 전혀 다른 프로세스에게 파일기술자 번호를

알려준다고 해도 입출력은 할 수 없습니다.

단, fork ()형태로 엄마, 자식형태라면 가능합니다.

(fork는 똑같은 프로세스를 복제하고, 메모리도 독립적으로 사용하지만

파일기술자는 같이 사용합니다. )

조금 엄밀히 말한다면 fork() 직후에는 두 프로세스가 동일한

디스크립터 설정을 가지게 되죠.

그 후에 open(), close(), socket() 등을 별개로 수행한다면

동일하지 않을 수 있겠고요. 그리고 엄마와 자식이 동일 디스크립터로

같은 파일을 접근할 수 있는 것은, 디스크립터 테이블에

커널에서 관리하는 파일 테이블에 관한 포인터를 가지고 있기

때문일 것이고요.

그리고 fork()로 연결되지 않은 두개의 별개의 프로세스가

파일 디스크립터를 전달하고 받아서 쓸 수 있는 방법이 있습니다. -_-;;

스티븐스 책에 보시면 대표적으로 유닉스 도메인 소켓을 이용해서

보내는 방법에 대한 설명과 예제가 나와 있습니다.


PS 헐헐.. 또 수정을 하게되네요.. ^^;
select에 파일기술자중 가장 큰 것을 넣는 이유라면..
기본적으로 프로세스가 열수있는 파일기술자가 제한이 되어있습니 다.
보통 1024정도... select란 녀석이 0부터 1024까지의 모든 파일기 술
자를 몽땅 검사하게 되면 힘들겠죠..(?) ^^;
파일기술자의 번호 매김이 위에설명한 바와같이 되므로 가장 큰 수
까지의 파일기술자면 검사하고 있으면 좀더 수월하기 때문입니다.
그럼 이만.. ^^;

댓글 달기

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