일방적인 연결 종료-close() 의 문제점

close  호출 후

- read buffer는 close와 동시에 모두 없어짐

- write buffer는 안의 내용을 모두 내보낸 다음에 파일의 마지막을 의미하는 'EOF'를 전송 후 스트림이 종료된다. 

※ EOF가 스트림 종료가 아닌 정확히는 파일의 마지막을 의미한다. 데이터를 전송하고, close하게 되면 언젠가 출력스트림의 내용이 모두 빠져나가고 파일의 마지막을 의미하는 'EOF'가 보내지게 된다.

BUF_SIZE만큼 (지속적으로) 전송하고 마지막 write은 BUF_SIZE을 못 채울 수도 있지만 마지막 BUF_SIZE에 대한 read의 block은 해제된다.

또한 EOF가 온 경우에도 read는 해제된다. 따라서 이 둘을 구분하기 위해 read의 return 값이 0일때와 0이 아닐 때를 구분하여 EOF(이때 read의 return값은 0, error일 경우 -1 반환)가 올 때, 모든 write(전송)을 마쳤음을 인식하도록 한다.


- close를 호출한 측에서 buffer를 닫게되고, EOF를 통해 상대에게 출력 스트림을 닫았음을 알리게 된다. 

- close는 소켓의 완전 소멸을 의미한다.

- close를 통해 read / write buffer를 모두 닫게 되면  외부로부터의 데이터는 차단되게 된다. <-- 문제점

메시지를 지속적으로 받거나 두 개의 패킷으로 나눠서 오는 경우는 다른 문제 --> read를 while로 구현


cf) 일반 file descriptor 와 socket file descriptor 는 구분하지 않는다.







half-close

- 이러한 일방적인 연결 종료의 문제점으로 인해 하나의 스트림을 유지시켜놓을 필요성이 있다.

- 종료를 원하다는 것은, 더이상 전송할 데이터가 존재하지 않는 상황

- 다만 송신측에서 상대방도 종료를 원하는 지 확인되지 않은 상황이므로, 입력 스트림은 종료시키지 않을 필요가 있다.

- 때문에 일반적으로 Half-close라 하면, 입력 스트림을 유지한 채, 출력 스트림만 종료하는 것을 의미한다.

- 이 때 shutdown()을 사용한다.

- 데이터를 모두 보내고, 출력스트림(shutdown())을 종료할 경우, 파일 출력 마지막에 EOF를 전송하여 출력스트림을 닫았음을 의도적으로 알릴 수 있다. 

※EOF가 반드시 스트림 종료를 의미하는 것이 아니다. 송신측이 close()나 shutdown()을 호출하지 않으면 어느 스트림도 닫히지 않는다.

- 수신측에선 어차피 상대가 출력스트림을 닫았으므로 출력스트림과 입력스트림 모두 닫기위해 close를 호출한다.

- 송신측에서 EOF를 받게되면 나머지 입력스트림에 대한 half-close를 해준다. * close 또는 shutdown(sd,SHUT_RDWR)해도 무방




shutdown(sock fd, int howto)

성공 시 0, 실패 시 -1반환




close는 아예 연결을 끊는 것

close 호출되면 상대방에게 EOF가 날라감.

EOF가 데이터 전송이 끝났다는 의미.

shutdown에서도 똑같이 EOF를 날려서 한쪽 스트림을 닫을 수 있다.


EOF를 보내고 잘 받았는지 확인하기 위해 상대는 Thank you 메시지를 보내준다.


이번 실습은 서버와 클라이언트 다른 디렉토리 상황에서 실시해보기 175 176

성공하면 receive.dat가 클라이언트 디렉토리에 생성됨

-thank you 확인

-close 로 바꾸고 에러확인


'CS & IT실무' 카테고리의 다른 글

IO 멀티플렉싱  (0) 2018.11.13
네트워크 바이트 순서와 인터넷 주소 변환  (0) 2018.10.04
소켓에 할당되는 IP주소와 PORT번호  (0) 2018.10.04

바이트 순서와 네트워크 바이트 순서

네트워크 바이트 순서와 인터넷 순서가 어떤 관계가 있는지

시스템에 따라 Big Endian / Little Endian 사용 방식이 다르다.

Big Endian - 상위 바이트의 값을 작은 번지수에 저장

ex. 0x12345678 은 가장 낮은 번지부터 0x12 0x34 0x56 0x78 순서로 담긴다. (상위 바이트는 12이다.)


Little Endian - 상위 바이트의 값을 큰 번지수에 저장

ex. 0x12345678은 0x78 0x56 0x34 0x12 순서로 담긴다.


데이터 송수신 과정에서 데이터 저장방식의 차이 문제가 발생할 수 있음 따라서 네트워크 데이터 송수신을 할 땐 약속이 필요함

보내는 쪽, 받는 쪽 모두 BigEndian / LittleEndian이든 상관없이 보내는 바이트의 순서는 낮은 주소부터 전송(빅엔디안)한다는 것을 알려야한다.

네트워크 바이트 순서는 BigEndian 기준! (낮은 주소의 데이터 번지부터 전송!) 낮은 주소를 significant byte로 취급하라고 알려줌 )




바이트 순서의 변환

htons - host의 메모리 속 데이터 순서가 bigendian이든 littleendian이든 상관없이 network byte(bigendian) 타입으로 바꿔라


이식성을 위해서 변경되지 않는다 하더라도 변경을 취해준다!


0x1234에서 1 하나는 4bit이고 12가 1byte를 나타낸다.



문자열 정보를 네트워크 바이트 순서의 정수로 변환


inet_addr(const char* string)

( ).( ).( ).( ) 형태의 문자열을 4바이트의 정수 배열(in_addr_t: 32비트 정수형)로 바꿔주는 함수이다. 

성공 시 Big Endian으로 변환된 32비트 정수값

실패 시 INADDR_NONE 반환

또한 표현 가능 범위를 체크하여 비트 수를 초과하는 값의 변환을 시도하면 INADDR_NONE반환


inet_aton(const char* string, struct in_addr* addr);

기능상으로 inet_addr 함수와 동일하다. 다만 in_addr형 구조체 변수에 변환의 결과가 저장된다는 점에서 차이를 보인다.

(struct sockaddr_in 안에 struct in_addr형 sin_addr 변수/ struct in_addr 안에는 in_addr_t 타입 s_addr변수가 있음)

성공시 1(true), 실패 시 0(false) 반환


주로 inet_addr를 많이 사용. inet_aton을 지원하지 않는 경우가 많기 때문


inet_ntoa

32bit ip 주소를 ASCII형(. . . .)으로 변환


Note. 항상 네트워크 바이트 순서로 저장을 함 -> htonl 이용


inet_ntoa 에 (객체).sin_addr 를 인자로 넘김





인터넷 주소의 초기화


memset 을 하는이유 : sockaddr_in 구조체 변수의 마지막 8byte 변수는 사용하지 않기 때문에 모두 0으로 채울 필요가 있다.

(sockaddr_in 구조체 변수).sin_addr.s_addr = inet_addr(문자열 형태의 IP주소)   - 여기서 문자열 형태의 IP주소는 "( ).( ).( ).( )" 형태

atoi로 문자열을 정수형으로 변환

Q. 왜 s_addr에는 htons로 변경하지 않나 ?

inet_addr에서 자동적으로 big endian 형식의 32bit 정수형을 반환함


이렇게 초기화한 값을 bind에 할당하게 됨


서버에서 주소정보를 설정하는 이유! (초기화한 값을 bind의 인자로 넘긴 경우)

초기화한 IP, PORT로 들어오는 데이터는 내게로 다 보내라!


클라이언트에서 주소정보를 설정하는 이유(초기화한 값을 connect의 인자로 넘긴 경우)

초기화한 IP, PORT로 연결을 해라



INADDR_ANY

현재 실행중인 컴퓨터의 IP를 소켓에 부여할 때 사용되는 것이 INADDR_ANY이다.

이는 서버 프로그램의 구현에 주로 사용된다.

이때 htonl(INADDR_ANY)형태로 대입한다.

4Byte unsigned int로 들어가 있으므로 데이터 순서만 정렬해서 넣어줌


이미 정해진 것을 사용하겠다는 의미

그러나 컴퓨터에 network interface card가 두개가 있을 땐 직접 지정해줘야 한다.

이때 INADDR_ANY를 쓰면 어느 것이 들어갈 지 모른다.


'CS & IT실무' 카테고리의 다른 글

IO 멀티플렉싱  (0) 2018.11.13
Half-Close - shutdown()  (0) 2018.10.05
소켓에 할당되는 IP주소와 PORT번호  (0) 2018.10.04

+ Recent posts