소켓 대 종료?


214

C에서 소켓을 닫으면 소켓이 파괴되어 나중에 재사용 할 수 있음을 이해했습니다.

종료는 어떻습니까? 설명에 따르면 해당 소켓에 대한 이중 연결의 절반이 닫힙니다. 그러나 소켓이 close시스템 호출 처럼 파괴 됩니까?


나중에 재사용 할 수 없습니다. 닫혔습니다. 끝마친. 끝난.
Lorne의 후작

답변:


191

이것은 Beej의 네트워킹 안내서에 설명 되어 있습니다. shutdown한 방향 또는 두 방향으로 통신을 차단하는 유연한 방법입니다. 두 번째 매개 변수가 SHUT_RDWR인 경우 (와 같은 close) 전송 및 수신을 모두 차단합니다 . 하나,close 실제로 소켓을 파괴하는 방법입니다.

을 사용 shutdown하면 피어가 이미 보낸 보류중인 데이터를 계속받을 수 있습니다 (이 점에 대해 Joey Adams에게 감사드립니다).


23
TCP 소켓을 close ()하더라도 TIME_WAIT 상태에 있기 때문에 TCP 소켓을 어쨌든 즉시 재사용 할 필요는 없지만 OS는 새로운 정보로 혼란 스러울 수있는 미해결 패킷이 있는지 확인합니다. 소켓을 다른 용도로 즉시 재사용해야했습니다.
alesplin

91
소켓에서 종료와 닫기의 큰 차이점은 소켓이 다른 프로세스와 공유 될 때의 동작입니다. shutdown ()은 소켓의 모든 복사본에 영향을 미치며 close ()는 한 프로세스의 파일 설명자에만 영향을줍니다.
Zan Lynx

5
shutdown양방향을 모두 사용하고 싶지만 한 가지 이유 는를 사용하여 소켓을 참조한 close경우 입니다. 당신이 경우 소켓은, 새롭게 문을 연 파일은 같은 파일 디스크립터를 할당 할 수 있고, 후속 사용을 읽을 수는 / 아주 나쁜 될 수있는 잘못된 장소를 작성합니다. 방금의 경우 ,의 후속 사용 은 호출 될 때까지 오류를 발생시킵니다 . FILEfdopencloseFILEshutdownFILEfclose
R .. GitHub 중지 지원 얼음

25
TIME_WAIT에 대한 의견이 잘못되었습니다. 소켓이 아닌 포트에 적용됩니다. 소켓을 재사용 할 수 없습니다.
Lorne의 후작

48
-1. 이 게시물과 링크는 모두 shutdownEOF에게 피어에게 신호를 보내고 피어가 보낸 보류중인 데이터를 수신 할 수 있는 중요한 개념적 이유를 생략합니다 .
Joey Adams

144

기존 답변 중 어느 것도 사람들에게 어떻게 shutdown그리고 어떻게close TCP 프로토콜 수준에서 작동 방식을 않으므로 추가해야합니다.

표준 TCP 연결은 4 방향 종료에 의해 종료됩니다.

  1. 참가자가 더 이상 전송할 데이터가 없으면 FIN 패킷을 상대방에게 보냅니다.
  2. 상대방이 FIN에 대한 ACK를 반환합니다.
  3. 상대방도 데이터 전송을 마치면 다른 FIN 패킷을 보냅니다.
  4. 초기 참가자는 ACK를 반환하고 전송을 마무리합니다.

그러나 TCP 연결을 닫는 또 다른 "비상"방법이 있습니다.

  1. 참가자는 RST 패킷을 보내고 연결을 포기합니다
  2. 다른 쪽은 RST를 수신 한 후 연결을 포기합니다.

Wireshark를 사용한 테스트에서 기본 소켓 옵션 shutdown을 사용하면 다른 쪽 끝으로 FIN 패킷을 보내지 만 그게 전부입니다. 상대방이 FIN 패킷을 보낼 때까지 데이터를 계속받을 수 있습니다. 이 일이 발생 Receive하면 0 크기의 결과를 얻습니다. 따라서 "보내기"를 종료 한 첫 번째 사람이라면 데이터 수신을 마치면 소켓을 닫아야합니다.

반면에 전화하면 close반면, 연결이 여전히 활성 상태 인 동안 (다른 쪽이 여전히 활성 상태이고 시스템 버퍼에 데이터를 보내지 하면 다른쪽으로 RST 패킷이 전송됩니다. 이것은 오류에 좋습니다. 예를 들어 상대방이 잘못된 데이터를 제공했거나 데이터 제공을 거부 한 경우 (DOS 공격?) 소켓을 즉시 닫을 수 있습니다.

규칙에 대한 나의 의견은 다음과 같습니다.

  1. 고려 shutdown하기 전에 close가능하면
  2. 종료하기로 결정하기 전에 (0 크기 데이터 수신) 수신을 완료 한 경우 마지막 송신 (있는 경우)이 끝난 후 연결을 닫으십시오.
  3. 연결을 정상적으로 닫으려면 연결을 종료하고 (SHUT_WR을 사용하고이 시점 이후에 SHUT_RD를 사용하여 데이터 수신에 신경 쓰지 않는 경우) 0 크기의 데이터가 수신 될 때까지 기다렸다가 소켓.
  4. 어쨌든 다른 오류가 발생한 경우 (예 : 시간 초과) 소켓을 닫으면됩니다.

SHUT_RD 및 SHUT_WR에 대한 이상적인 구현

다음은 테스트를 거치지 않았으므로 자신의 책임을 신뢰하십시오. 그러나 나는 이것이 합리적이고 실용적인 방법이라고 생각합니다.

TCP 스택이 SHUT_RD만으로 셧다운을 수신하면 더 이상 데이터가 예상되지 않는 것으로이 연결을 표시해야합니다. 그런 다음 보류중인 read요청 과 후속 요청 (어느 스레드에 관계없이)은 크기가 0 인 결과로 반환됩니다. 그러나 연결은 여전히 ​​활성 상태이며 사용 가능합니다. 예를 들어 여전히 OOB 데이터를 수신 할 수 있습니다. 또한 OS는이 연결에 대해 수신 한 모든 데이터를 삭제합니다. 그러나 그게 전부입니다. 패키지는 다른쪽으로 보내지지 않습니다.

TCP 스택이 SHUT_WR만으로 종료를 수신하면 더 이상 데이터를 전송할 수 없으므로이 연결을 표시해야합니다. 보류중인 모든 쓰기 요청은 완료되지만 후속 쓰기 요청은 실패합니다. 또한 FIN 패킷은 다른쪽으로 보내 져서 더 많은 데이터가 없다는 것을 알려줍니다.


2
"연결이 여전히 활성화되어있는 상태에서 전화를 거는 경우"는 타당성이며 RST가 전송되지 않습니다. (1) 필요하지 않습니다. (4)에서 시간 초과가 반드시 연결에 치명적일 필요는 없으며 변경할 수 없음을 나타내는 것은 아닙니다.
론의 후작

4
@EJP 아니오, 그것은 타인 학적이지 않습니다. 당신은 할 수있는 shutdown()연결을 한 후 더 이상 살아있다. 여전히 파일 디스크립터가 있습니다. recv()수신 버퍼에서 여전히 가능 합니다. 그리고 close()파일 디스크립터를 폐기 하려면 여전히 호출해야합니다 .
Pavel Šimerda

이것은 와이어 형식에 관한 가장 좋은 대답입니다. SHUT_RD로 종료에 대한 자세한 내용을 알고 싶습니다. 더 많은 데이터를 기대하지 않는 TCP 신호가 없습니다. 더 많은 데이터를 보내지 않는다는 신호 전달을위한 FIN 만 있습니까?
파벨시 메르 다

3
@ PavelŠimerda 예 TCP는 더 많은 데이터를 기대하지 않는다는 신호를 보내지 않습니다. 이것은 높은 수준의 프로토콜에서 고려해야합니다. 제 생각에 이것은 일반적으로 필요하지 않습니다. 문을 닫을 수는 있지만 문 앞에 선물을주는 사람들을 막을 수는 없습니다. 이것은 당신의 결정이 아니라 THEIR의 결정입니다.
어스 엔진

1
@EJP 실제 논쟁이 있습니까? 그렇지 않으면 관심이 없습니다.
Pavel Šimerda

35

대신 close()사용하면 피할 수있는 몇 가지 제한 사항 이 있습니다 shutdown().

close()TCP 연결에서 양방향을 종료합니다. 때로는 다른 엔드 포인트에 데이터 전송이 완료되었지만 여전히 데이터를 수신하고 싶다고 말하려고합니다.

close()디스크립터 참조 수 (파일 테이블 항목에서 유지 보수되고 파일 / 소켓을 참조하는 현재 열려있는 디스크립터 수를 카운트)를 줄이고 디스크립터가 0이 아닌 경우 소켓 / 파일을 닫지 않습니다. 이는 포크하는 경우, 정리는 참조 카운트가 0으로 떨어진 후에 만 ​​발생합니다 shutdown(). 참조 카운트를 무시하고 정상적인 TCP 닫기 시퀀스를 시작할 수 있습니다.

매개 변수는 다음과 같습니다.

int shutdown(int s, int how); // s is socket descriptor

int how 될 수 있습니다 :

SHUT_RD또는 0 추가 수신이 허용되지 않습니다

SHUT_WR또는 1 추가 전송이 허용되지 않습니다

SHUT_RDWR또는 2 추가 발신 및 수신이 허용되지 않습니다


8
두 기능은 완전히 다른 목적으로 사용됩니다. 소켓에 대한 마지막 닫기가 아직 시작되지 않은 경우 종료 시퀀스를 시작한다는 사실은 닫기가 소켓 데이터 구조를 정리하기위한 것이며, 종료는 tcp 레벨 종료 시퀀스를 시작하기위한 것입니다.
Len Holgate

25
대신 '종료 사용'을 할 수 없습니다. 사용할 수도 있습니다. 그러나 소켓을 잠시 닫아야합니다.
Lorne의 후작

15

이것은 플랫폼에 따라 다를 수 있지만 어쨌든 의심 스럽지만 어쨌든 내가 본 가장 좋은 설명 은 종료, 느린 옵션, 소켓 클로저 및 일반적인 연결 종료 시퀀스에 대해 설명 하는이 msdn 페이지 입니다.

요약하면 shutdown을 사용하여 TCP 레벨에서 시스템 종료 시퀀스를 전송하고 close를 사용하여 프로세스의 소켓 데이터 구조에서 사용하는 자원을 확보하십시오. close를 호출 할 때까지 명시 적 종료 시퀀스를 발행하지 않은 경우 종료 시퀀스가 ​​시작됩니다.


9

또한 리눅스 shutdown()에서 한 pthread를 사용 하여 다른 pthread를 강제로 차단 하여 성공 했습니다.connect() 일찍 중단 .

다른 OS (최소한 OSX)에서는 호출 close()connect()실패 하기에 충분 하다는 것을 알았습니다 .


7

"shutdown ()은 실제로 파일 디스크립터를 닫는 것이 아니라 단지 그 유용성을 변경합니다. 소켓 디스크립터를 해제하려면 close ()를 사용해야합니다." 1


3

닫기

소켓 사용을 마치면 파일 설명자를 close로 닫을 수 있습니다. 연결을 통해 전송 대기중인 데이터가 여전히 있으면 일반적으로 닫기는이 전송을 완료하려고 시도합니다. SO_LINGER 소켓 옵션을 사용하여 시간 초과 기간을 지정하여이 동작을 제어 할 수 있습니다. 소켓 옵션을 참조하십시오.

일시 휴업

shutdown을 호출하여 연결에서 수신 또는 전송 만 종료 할 수도 있습니다.

종료 기능은 소켓 연결을 종료합니다. 수행 할 조치를 지정하는 방법 인수 : 0이 소켓에 대한 데이터 수신을 중지하십시오. 추가 데이터가 도착하면 거부하십시오. 1이 소켓에서 데이터 전송을 중지하십시오. 전송 대기중인 모든 데이터를 폐기하십시오. 이미 전송 된 데이터의 승인을 찾지 않습니다. 분실 한 경우에는 재전송하지 마십시오. 2 수신과 전송을 모두 중지하십시오.

성공하면 리턴 값은 0이고 실패하면 -1입니다.


2

내 시험에서.

close 소켓이 다른 프로세스와 공유되지 않으면 핀 패킷을 보내고 즉시 fd를 파괴합니다.

shutdown SHUT_RD , 프로세스는 여전히 소켓에서 데이터를 가져올 수 있지만 recvTCP 버퍼가 비어 있으면 0 recv을 반환합니다. 피어가 더 많은 데이터 를 보낸 후 데이터를 다시 반환합니다.

shutdown SHUT_WR 은 추가 전송이 허용되지 않음을 나타 내기 위해 핀 패킷을 전송합니다. 피어는 데이터를 수신 할 수 있지만 TCP 버퍼가 비어 있으면 0을 수신합니다.

shutdown SHUT_RDWR피어가 더 많은 데이터를 보내면 ( SHUT_RDSHUT_WR을 모두 사용함 )은 첫 번째 패킷을 보냅니다.


방금 시도해보고 close()FIN 대신 Linux에서 RST를 보냈습니다.
Pavel Šimerda

또한 읽기 측을 종료 한 후 프로그램이 더 많은 데이터를 수신한다고 말하고 싶습니까?
Pavel Šimerda

@ PavelŠimerda RST 또는 FIN을 보내는 tcp 상태에 따라 달라질 수 있다고 생각합니까? 잘 모르겠습니다.
simpx 2012 년

1. '피어가 더 많은 데이터를 보낸 후 recv()다시 데이터를 반환합니다'가 올바르지 않습니다. 2. 피어가 더 많은 데이터를 보낸 후의 동작 SHUT_RD은 플랫폼에 따라 다릅니다.
Lorne의 후작 5

@ EJP 나는 나 자신을 시험해 보았습니다.이 테스트를 수행하는 플랫폼, centos 또는 Mac을 잊어 버렸습니다. 그리고 이것이 내가 얻은 것
simpx
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.