connect() 하기 전에 select() 하는 방법... 올바른 방법인가요???
글쓴이: trymp / 작성시간: 금, 2012/09/14 - 3:35오후
네트워크 소켓통신에서 connect() 함수를 사용하면 오랜 시간동안 blocking 될 소지가 있잖아요.
물론 비동기소켓옵션으로 걸기도 하지만, 이런 것을 방지하기 위해서
어떤 오픈소스의 소스코드를 보니 socket 을 이용해서 connect() 로 서버에 접속하기 전에
select() 로 readset 을 걸어서 여기서 통과하면 connect() 하도록 해 놓았더라구요.
이 방법이 맞는 방법인가요???
select() 를 호출하면 알아서 tcp syn 패킷이라도 쏴서 접속이 가능한지 판단하는 건가요??
어떻게 접속이 될거라고 판단을 하죠..??
고수님들의 조언 부탁드립니다.
Forums:
"select() 를 호출하면 알아서 tcp syn
"select() 를 호출하면 알아서 tcp syn 패킷이라도 쏴서 접속이 가능한지 판단하는 건가요??"
아닙니다. select()는 FD의 상태를 체크하는것 뿐입니다.
"물론 비동기소켓옵션으로 걸기도 하지만, 이런 것을 방지하기 위해서, ... select() 로 readset 을 걸어서"
소스를 잘못 이해하신 부분일거라 추측합니다. 분명,
select()를 하기전에 connect()를 한번이라도 호출하는 경우가 있을것입니다.
Non-Blocking connect()를 하는 방법은, 다음과 같습니다.
#1. 소켓 생성
#2. fcntl()로 SOCK_NONBLOCK을 설정
#3. connect()를 일단 호출.
#4. connect()가 에러를 EINPROGRESS 을 리턴했다면
#5. 그 소켓을 write FD set에 넣고 select()호출.
#6. select()가 리턴되면, 체크해봐서 #3으로 돌아감.
connect() 함수의 맨페이지에 충분한 설명이 나와있습니다.
위 설명에 나와있듯이, writing이 가능한지 여부로 connect()가 완료되었는지 확인할수 있습니다.
왜, 인터넷에 read FD로 넣어서 검색하는 코드들이 있는지 모르겠는데(저도 그런 코드들을 몇 봤습니다.)
read event는 socket buffer(커널속)에 일정 크기(소켓 윈도우 크기 이상)가 넘지 않으면, 발생되지 않습니다.
즉, select()가 read-event를 리턴했다는말은,
서버가 이미 accept()를 호출했고, 일방적으로 데이터를 보내기시작했다는 의미입니다. 그 데이터가 이미 클라이언트의 소켓버퍼에 쌓여있다는 말이기도 합니다.
참고로, select()를 호출하기도 전에, 처음 호출했던 connect()가 설령 EINPROGRESS를 리턴했다고,
그것이 connect()가 실패했다는말은 아닙니다.
에러코드자체가 의미했듯이, connect()를 호출한순간 SYN패킷보내고, ACK를 기다리는 동안, 보통은 프로세스/쓰레드를 block 함으로써 기다리는데,
그냥...진행중이다, 완료는 안됐다..라고 알려주는것 뿐이죠.
즉, 클라이언트에서 아직 connect()를 다시 호출하여, success를 리턴하기전에도,
이미 서버는 accept()를 호출하여, TCP세션은 이미 만들어져서, 데이터를 보내고 있는상황일수도 있겠죠.
암튼, connect()를 대기하는 소켓을 select()의 read set에 넣는것은
꼭, 서버가 연결되자마자, 데이터를 보내기 시작할것이다라는 가정하에서만 동작할 로직처럼 보입니다.(솔직히, 직접 테스트는 안해봤습니다만, 머리속 의견입니다.)
반면 write event는 write 소켓 버퍼(커널속)가 일정크기 이상 비어있을때 발생하기에,
TCP세션이 만들어진순간, 서버가 데이터를 보냈던, 안보냈던 상관없이
바로 write event를 발생할것입니다. (연결하자마자는, 버퍼가 항상 비어있으니)
그래서, 맨페이지에서도
write set을 이용하여, connect()의 성공여부를 확인하라고 설명이 나온듯합니다.
그리고, (이 역시나 설명이 나와있듯이)
select()가 그 소켓FD가 write-able 하다고 알려줘도,
getsockopt() with SO_ERROR를 사용하여, 정말 그전에 호출했던(EINPROGRESS를 리턴했던) connect()가 성공했는지 안했는지를
확인하세요.
만약 SO_ERROR가 0이었다면 (아무 에러가 없었다면), connect()를 호출하여,새로운 FD를 받고, 통신을 시작하면 될듯합니다.
간단히 쓰려고 시작했는데, 설명하다보니 길어졌네요.
왠간하면, man page에 다 있습니다.
man page를 잘 읽어보세요.
즉, 짤게말하자면, "connect() 하기 전에
즉, 짤게말하자면,
"connect() 하기 전에 select() 하는 방법... 올바른 방법인가요???"
아닙니다. select()하기전에 반드시 NON-BLOCKING으로 connect()를 initiate하셔야합니다.
그리고, read set을 이용하지말고, write set과 getsockopt()로 connect()의 성공여부를 판단하셔야합니다.
댓글 달기