티스토리 뷰
시험문제
앞에서 설명했듯이 , watermark라는 옵션이 있다. 그 소켓을 사용하는 응용프로그램이 만약 100byte를 받아야만 의미가 있는 프로그램이라고 할때 커널 버퍼에 100바이트가 도착하기 전에는 read를 깨우지 않는다. 100byte가 모두 도착한 경우에만 블락에서 깨어나게끔 해서 쓸데없는 오버헤드를 줄인다.
위의 그림은 Readable,Writable,Exception 상황이 어떨때 발생하는지를 나타내고 있다. 즉, select에서 깨어나는 상황을 요약한 것이다.
Readable한 경우
1. 커널 버퍼에 데이터가 도착한 경우
2. 상대방으로 부터 FIN메세지가 도착한 경우
3. (서버의 경우) 상대방으로 부터 연결 요청이 들어온 경우
4. 오류가 발생한 경우 - 앞의 Connection Aborted의 예처럼 , 클라이언트가 3way핸드쉐이킹 과정 맨 마지막 ACK를 서버에게 보내고 나서 그 직후에 RST메세지를 서버에게 보낼경우 서버는 incomplete큐에서 complete큐에 옮겨 담았던 클라이언트의 연결 요청 정보를 accept에서 리턴 시킬때 -1을 리턴시키고 Connection Aborted를 errno에 세팅 한다고 했었다. 그렇기 때문에 서버가 클라이언트로부터 RST를 받은상황에 select 블락으로부터 깨어나서 그 리셋 메세지를 받은 소켓이 적절하게 accept리턴 되게끔 해야한다. 그렇기 때문에 에러를 받은 상황에 readable해 지는것이다.
Writable한 경우
1. 커널 버퍼에 내가 쓰고자 하는 양 만큼의 여유 공간이 남아있을때
2. 하프 클로즈 FIN메세지를 날리려고 할때
3. 오류가 발생한경우(SIGPIPE에러, 앞에서의 예처럼 Reset을 받은 소켓에 한번더 write를 하게 될 경우 SIGPIPE에러가 발생한다고 했다.
서버로부터 리셋 메세지를 받은 상황(에러 발생)에 select가 블락에서 깨어나야 그 다음번 write를 할때 SIGPIPE시그널이 발생해서 오류가 났다는걸 프로세스가 알게 되므로 리셋 메세지를 받은 상황(에러 발생)에서 writable 하다고 판단해야 한다.
즉, 오류가 발생한경우 select 블락에서 깨어나서 적절하게 그 오류가 발생한 소켓에 대해 적절한 처리를 해줘야 하기 때문에 readable,writeable이 select가 블락에서 깨어야할 상황인 것이다.
커널에 이미 파일 보따리의 사이즈가 256이라고 정의 되어 있으므로 우리가 마음대로 이 사이즈를 바꿀수 없다.
최대 256개의 파일들을 동시에 i/o검사를 할 수 있다.
만약에 클라이언트와 서버 사이에 연결이 설정되어서 파이프가 연결 되어있다고 해보자
클라이언트<--------------> 서버 사이에 패킷이 도착하려면 클라이언트가 4개의 라인을 보내는 만큼의 시간이 필요하다고 하자.
클라이언트 ---4----3----2-----1---> 서버
클라이언트가 네번째 패킷을 보내기 시작할때 첫번째 패킷이 서버에 도달할 것이다.
클라이언트 ---8----7----6-----5---> 서버
클라이언트 <---1----2----3-----4--- 서버
우리가 짠 프로그램은 에코 프로그램이므로 서버는 다시 그 패킷의 내용을 클라이언트에게 되돌려주고 소켓은 FULL-DUPLEX이므로 위의 그림처럼 나타나게 된다.
클라이언트가 8번째 줄을 파일에서 읽어 보냈을때 아까 첫번째로 보냈던 라인이 되돌아 오게 된다.
클라이언트는 8번째 라인을 보내고 나서 파일의 끝을 읽어서 EOF이벤트가 발생한다. 여기서는 stdin과 stdout이 기준이 아니라
파일 입출력을 기준으로 한다.
클라이언트는 패킷을 다 보내서 EOF가 발생했다고 하더라도 소켓을 없애버리면 안된다(받아야할 데이터가 남아있으므로)
클라이언트가 종료되어야할 시점은 상대방이 더이상 보낼게 없다는 FIN메세지를 보낼때 이다.
정확히 말하면, 내가 FIN을 보내고 그다음 서버로부터 FIN을 받았을때이다.
내가 half-close를 하지 않았는데 상대방이 FIN을 보내는 경우에 클라이언트를 종료시키면 안된다.
클라이언트가 8번째 라인을 보내고 나서 EOF가 발생해서 프로세스가 종료되기 때문에 FIN메세지를 서버에게 보내게 될것이다.
그리고 나면 프로세스는 종료되는데, 클라이언트 커넥트 소켓은 아직 받아야할 데이터가 있을 수 있기때문에 종료되면 안된다.
상대방으로부터 FIN이 도착하게되면 2ms동안 기다리면서(소켓 살아있음) 나머지 데이터를 마저 받게 된다. 이때
클라이언트의 프로세스는 이미 종료되어있다.
우리가 여태 짰던 서버 프로그램의 에코역할을 하는 str_echo함수를 보자.
여기서 클라이언트로부터 FIN을 받은경우 read에서 EOF가 발생하고 read는 0을 리턴하게 된다.
무슨말이냐면, 클라이언트가 파일에서 데이터를 읽다가 EOF가 발생하게 되면 프로세스가 종료되면서 FIN메세지를 서버에게 보내고
서버는 FIN메세지를 받게 되면 마찬가지로 프로세스를 종료시키고 클라이언트에게 FIN 메세지를 보내게 되면서 마지막 half-close를
마무리 하게 된다.
근데 클라이언트 프로세스가 종료되면서 FIN을 보내버리면 소켓이 없어져 버리므로 클라이언트가 보냈던 패킷을 모두 받지 못한 상태에서
소켓이 사라지게 된다. 그렇기 때문에 소켓을 없애지않고 프로세스를 종료시키면서 FIN을 서버에게 보내기 위해 shutdown시스템콜을
사용 해야 한다. 그냥 close(소켓) 함으로써 FIN메세지를 서버에게 보내면 안된다는 것이다.
close대신 shutdown을 사용해야 마지막 half-close에서 2ms동안 소켓을 없애지 않고 기다리면서 혹시 서버가 보낼 남은 데이터가 있을 경우를
대비하는 것이다.
shutdown을 사용하게 되면 프로세스는 종료되지만 소켓은 2ms동안 살아있게 된다.
shutdown을 해서 FIN만 보내고 나서 상대방이 FIN을 보낼때까지 소켓을 붙잡고 있어야 한다.
shutdown한다고 해서 그 소켓에 대한 reference count가 줄어들지 않기 때문에 데이터를 모두 받고 나서는 마지막엔 close를 함으로써
레퍼런스 카운트를 하나 줄여줘야 한다.
'컴퓨터 공학과 졸업 > 소켓 프로그래밍' 카테고리의 다른 글
개선된 서버 프로그램 (0) | 2017.11.11 |
---|---|
shutdown과 select를 활용한 개선된 에코 클라이언트 프로그램 (0) | 2017.11.10 |
I/O 멀티플렉싱(select) (0) | 2017.11.09 |
I/O모델들과 I/O 멀티플렉싱(select) (0) | 2017.11.08 |
비정상적인 서버 종료 (0) | 2017.11.07 |
- Total
- Today
- Yesterday
- type alias
- typescript
- props
- webpack
- rendering scope
- Next.js
- hydrate
- computed
- design system
- Babel
- async
- react
- reflow
- useRef
- Action
- mobx
- reactdom
- es6
- server side rendering
- return type
- javascript
- state
- storybook
- reducer
- promise
- react hooks
- atomic design
- Polyfill
- await
- useEffect
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |