Đóng vs ổ cắm tắt máy?


214

Trong C, tôi hiểu rằng nếu chúng ta đóng một ổ cắm, điều đó có nghĩa là ổ cắm sẽ bị phá hủy và có thể được sử dụng lại sau này.

Làm thế nào về tắt máy? Mô tả cho biết nó đóng một nửa kết nối song công với ổ cắm đó. Nhưng ổ cắm đó sẽ bị phá hủy như closecuộc gọi hệ thống?


Nó không thể được sử dụng lại sau này. Đóng cửa rồi. Đã kết thúc. Làm xong.
Hầu tước Lorne

Câu trả lời:


191

Điều này được giải thích trong hướng dẫn kết nối mạng của Beej. shutdownlà một cách linh hoạt để chặn liên lạc theo một hoặc cả hai hướng. Khi tham số thứ hai là SHUT_RDWR, nó sẽ chặn cả gửi và nhận (như close). Tuy nhiên, closelà cách để thực sự phá hủy một ổ cắm.

Với shutdown, bạn vẫn có thể nhận dữ liệu đang chờ xử lý mà đồng nghiệp đã gửi (cảm ơn Joey Adams vì đã lưu ý điều này).


23
Hãy ghi nhớ, ngay cả khi bạn đóng () ổ cắm TCP, dù sao thì nó cũng không nhất thiết phải được sử dụng lại, vì nó sẽ ở trạng thái TIME_WAIT trong khi HĐH đảm bảo không có gói tin nổi bật nào có thể bị nhầm lẫn là thông tin mới nếu bạn là ngay lập tức tái sử dụng ổ cắm đó cho một cái gì đó khác.
alesplin

91
Sự khác biệt lớn giữa tắt máy và đóng trên ổ cắm là hành vi khi ổ cắm được chia sẻ bởi các quy trình khác. Tắt máy () ảnh hưởng đến tất cả các bản sao của ổ cắm trong khi close () chỉ ảnh hưởng đến bộ mô tả tệp trong một quy trình.
Zan Lynx

5
Một lý do bạn có thể muốn sử dụng shutdowncả hai hướng nhưng không phải closelà nếu bạn đã FILEtham chiếu đến ổ cắm bằng cách sử dụng fdopen. Nếu bạn closelà ổ cắm, một tệp mới được mở có thể được gán cùng một fd và việc sử dụng tiếp theo FILEsẽ đọc / ghi sai vị trí có thể rất tệ. Nếu bạn chỉ shutdown, việc sử dụng tiếp theo FILEsẽ chỉ đưa ra lỗi cho đến khi fcloseđược gọi.
R .. GitHub DỪNG GIÚP ICE

25
Nhận xét về TIME_WAIT là không chính xác. Điều đó áp dụng cho các cổng, không phải cho ổ cắm. Bạn không thể sử dụng lại ổ cắm.
Hầu tước Lorne

48
-1. Cả bài đăng này và liên kết đều bỏ qua một lý do khái niệm quan trọng cần sử dụng shutdown: để báo hiệu EOF cho bạn bè và vẫn có thể nhận dữ liệu đang chờ xử lý mà bạn bè đã gửi.
Joey Adams

144

Không có câu trả lời nào hiện có cho mọi người biết cách thức shutdownclosehoạt động ở cấp độ giao thức TCP, vì vậy rất đáng để thêm điều này.

Kết nối TCP tiêu chuẩn bị chấm dứt bởi quyết toán 4 chiều:

  1. Khi người tham gia không còn dữ liệu để gửi, nó sẽ gửi gói FIN cho người khác
  2. Bên kia trả lại ACK cho FIN.
  3. Khi bên kia cũng kết thúc truyền dữ liệu, nó sẽ gửi một gói FIN khác
  4. Người tham gia ban đầu trả về một ACK và hoàn tất chuyển khoản.

Tuy nhiên, có một cách "xuất hiện" khác để đóng kết nối TCP:

  1. Một người tham gia gửi một gói RST và từ bỏ kết nối
  2. Phía bên kia nhận được một RST và sau đó cũng từ bỏ kết nối

Trong thử nghiệm của tôi với Wireshark, với các tùy chọn ổ cắm mặc định, shutdownsẽ gửi một gói FIN đến đầu kia nhưng đó là tất cả những gì nó làm. Cho đến khi bên kia gửi cho bạn gói FIN, bạn vẫn có thể nhận dữ liệu. Khi điều này xảy ra, bạn Receivesẽ nhận được kết quả kích thước 0. Vì vậy, nếu bạn là người đầu tiên tắt "gửi", bạn nên đóng ổ cắm sau khi nhận xong dữ liệu.

Mặt khác, nếu bạn gọi closetrong khi kết nối vẫn hoạt động (phía bên kia vẫn hoạt động và bạn cũng có thể có dữ liệu chưa được lưu trong bộ đệm hệ thống), một gói RST sẽ được gửi sang phía bên kia. Điều này là tốt cho các lỗi. Ví dụ: nếu bạn nghĩ rằng bên kia đã cung cấp dữ liệu sai hoặc họ từ chối cung cấp dữ liệu (tấn công DOS?), Bạn có thể đóng ổ cắm ngay lập tức.

Ý kiến ​​của tôi về các quy tắc sẽ là:

  1. Cân nhắc shutdowntrước closekhi có thể
  2. Nếu bạn đã nhận xong (nhận được dữ liệu kích thước 0) trước khi bạn quyết định tắt máy, hãy đóng kết nối sau khi gửi lần cuối (nếu có) kết thúc.
  3. Nếu bạn muốn đóng kết nối bình thường, hãy tắt kết nối (với SHUT_WR và nếu bạn không quan tâm đến việc nhận dữ liệu sau thời điểm này, với SHUT_RD), và đợi cho đến khi bạn nhận được dữ liệu kích thước 0, sau đó đóng ổ cắm.
  4. Trong mọi trường hợp, nếu có bất kỳ lỗi nào khác xảy ra (ví dụ như hết thời gian chờ), chỉ cần đóng ổ cắm.

Triển khai lý tưởng cho SHUT_RD và SHUT_WR

Những điều sau đây chưa được thử nghiệm, tin tưởng vào rủi ro của riêng bạn. Tuy nhiên, tôi tin rằng đây là một cách làm việc hợp lý và thiết thực.

Nếu ngăn xếp TCP chỉ nhận được tắt máy với SHUT_RD, nó sẽ đánh dấu kết nối này là không có thêm dữ liệu dự kiến. Bất kỳ readyêu cầu đang chờ xử lý và tiếp theo (bất kể chủ đề nào họ tham gia) sau đó sẽ được trả về với kết quả không có kích thước. Tuy nhiên, kết nối vẫn hoạt động và có thể sử dụng - ví dụ: bạn vẫn có thể nhận dữ liệu OOB. Ngoài ra, HĐH sẽ bỏ bất kỳ dữ liệu nào nó nhận được cho kết nối này. Nhưng đó là tất cả, không có gói nào sẽ được gửi sang phía bên kia.

Nếu ngăn xếp TCP chỉ nhận được tắt máy chỉ với SHUT_WR, nó sẽ đánh dấu kết nối này vì không thể gửi thêm dữ liệu. Tất cả các yêu cầu ghi đang chờ xử lý sẽ kết thúc, nhưng các yêu cầu ghi tiếp theo sẽ không thành công. Hơn nữa, một gói FIN sẽ được gửi đến một phía khác để thông báo cho họ rằng chúng tôi không có nhiều dữ liệu để gửi.


2
"nếu bạn gọi gần trong khi kết nối vẫn còn hoạt động" là về mặt thời gian và nó không khiến RST được gửi. (1) không cần thiết. Trong (4), thời gian chờ không nhất thiết gây tử vong cho kết nối và không bất biến cho thấy bạn có thể đóng nó.
Hầu tước Lorne

4
@EJP Không, nó không phải là tautological. Bạn có thể shutdown()kết nối và sau đó nó không còn tồn tại. Bạn vẫn có mô tả tập tin. Bạn vẫn có thể recv()từ bộ đệm nhận. Và bạn vẫn cần gọi close()để loại bỏ mô tả tập tin.
Pavel imerda

Đây là câu trả lời tốt nhất liên quan đến định dạng dây. Tôi muốn biết thêm chi tiết về việc tắt với SHUT_RD. Không có tín hiệu TCP vì không mong đợi nhiều dữ liệu hơn, phải không? Không phải chỉ có FIN để báo hiệu cho việc không gửi thêm dữ liệu sao?
Pavel imerda

3
@ PavelŠimerda Có TCP không báo hiệu cho việc không mong đợi nhiều dữ liệu hơn. Điều này nên được xem xét trong các giao thức cấp cao. Theo tôi, điều này nói chung là không cần thiết. Bạn có thể đóng cửa, nhưng bạn không thể ngăn mọi người đặt quà trước cửa. Đây là quyết định CỦA HỌ, không phải của bạn.
Động cơ Trái đất

1
@EJP Bạn có bất kỳ đối số thực tế? Nếu không thì tôi không quan tâm.
Pavel imerda

35

Có một số hạn chế close()có thể tránh được nếu sử dụng shutdown()thay thế.

close()sẽ chấm dứt cả hai hướng trên kết nối TCP. Đôi khi bạn muốn nói với điểm cuối khác rằng bạn đã kết thúc việc gửi dữ liệu, nhưng vẫn muốn nhận dữ liệu.

close() giảm số tham chiếu của bộ mô tả (được duy trì trong mục nhập bảng tệp và đếm số lượng bộ mô tả hiện đang mở đang tham chiếu đến một tệp / ổ cắm) và không đóng ổ cắm / tệp nếu bộ mô tả không bằng 0. Điều này có nghĩa là nếu bạn đang giả mạo, việc dọn dẹp chỉ xảy ra sau khi số tham chiếu giảm xuống còn 0. Với shutdown() một người có thể bắt đầu chuỗi đóng TCP bình thường bỏ qua số tham chiếu.

Các thông số như sau:

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

int how có thể:

SHUT_RDhoặc 0 nhận thêm không được phép

SHUT_WRhoặc 1 gửi thêm không được phép

SHUT_RDWRhoặc 2 gửi và nhận thêm không được phép


8
Hai chức năng dành cho các mục đích hoàn toàn khác nhau. Thực tế là lần đóng cuối cùng trên ổ cắm khởi tạo trình tự tắt máy nếu người ta chưa bắt đầu không thay đổi thực tế rằng việc đóng là để làm sạch cấu trúc dữ liệu ổ cắm và tắt máy là để bắt đầu trình tự tắt mức tcp.
Len Holgate

25
Bạn không thể 'sử dụng tắt máy thay thế'. Bạn có thể sử dụng nó là tốt. nhưng bạn phải đóng ổ cắm một thời gian.
Hầu tước Lorne

15

Đây có thể là nền tảng cụ thể, tôi bằng cách nào đó nghi ngờ nó, nhưng dù sao, lời giải thích tốt nhất tôi đã thấy là ở đây trên trang msDN nơi họ giải thích về tắt máy, tùy chọn nán lại, đóng ổ cắm và trình tự kết nối chung.

Tóm lại, sử dụng tắt máy để gửi một chuỗi tắt ở cấp TCP và sử dụng gần để giải phóng các tài nguyên được sử dụng bởi các cấu trúc dữ liệu ổ cắm trong quy trình của bạn. Nếu bạn chưa đưa ra một chuỗi tắt máy rõ ràng vào thời điểm bạn gọi gần thì một chuỗi sẽ được bắt đầu cho bạn.


9

Tôi cũng đã thành công với linux bằng cách sử dụng shutdown()từ một pthread để buộc một pthread khác hiện bị chặn connect()để hủy bỏ sớm.

Trong các hệ điều hành khác (ít nhất là OSX), tôi thấy việc gọi điện close()là đủ để connect()thất bại.


7

"shutdown () không thực sự đóng bộ mô tả tập tin. Nó chỉ thay đổi khả năng sử dụng của nó. Để giải phóng bộ mô tả ổ cắm, bạn cần sử dụng close ()." 1


3

Đóng

Khi bạn đã sử dụng xong ổ cắm, bạn chỉ cần đóng mô tả tệp của nó với đóng; Nếu vẫn còn dữ liệu chờ truyền qua kết nối, thường đóng sẽ cố gắng hoàn thành việc truyền này. Bạn có thể kiểm soát hành vi này bằng tùy chọn ổ cắm SO_Linger để chỉ định khoảng thời gian chờ; xem Tùy chọn ổ cắm.

Tắt

Bạn cũng có thể tắt chỉ tiếp nhận hoặc truyền trên một kết nối bằng cách gọi tắt máy.

Chức năng tắt máy tắt kết nối của ổ cắm. Đối số của nó làm thế nào chỉ định hành động nào sẽ thực hiện: 0 Dừng nhận dữ liệu cho ổ cắm này. Nếu dữ liệu tiếp theo đến, từ chối nó. 1 Dừng cố gắng truyền dữ liệu từ ổ cắm này. Hủy bỏ bất kỳ dữ liệu chờ đợi để được gửi. Ngừng tìm kiếm xác nhận dữ liệu đã được gửi; không truyền lại nếu nó bị mất. 2 Dừng cả tiếp nhận và truyền.

Giá trị trả về là 0 khi thành công và -1 khi thất bại.


2

trong bài kiểm tra của tôi

close sẽ gửi gói vây và hủy fd ngay lập tức khi socket không được chia sẻ với các tiến trình khác

shutdown SHUT_RD , quá trình vẫn có thể lấy lại dữ liệu từ ổ cắm, nhưng recvsẽ trả về 0 nếu bộ đệm TCP trống. Sau khi ngang hàng gửi thêm dữ liệu, recvsẽ trả lại dữ liệu.

shutdown SHUT_WR sẽ gửi gói vây để cho biết các lần gửi tiếp theo không được phép. ngang hàng có thể recv dữ liệu nhưng nó sẽ recv 0 nếu bộ đệm TCP của nó trống

shutdown SHUT_RDWR (bằng cách sử dụng cả SHUT_RDSHUT_WR ) sẽ gửi gói đầu tiên nếu ngang hàng gửi thêm dữ liệu.


Tôi vừa thử nó và close()gửi RST trên Linux thay vì FIN.
Pavel imerda

Bạn cũng thực sự muốn nói rằng sau khi tắt phần đọc, chương trình sẽ nhận được nhiều dữ liệu hơn?
Pavel imerda

@ PavelŠimerda tôi nghĩ có thể phụ thuộc vào trạng thái tcp để gửi RST hoặc FIN? tôi không chắc.
simpx

1. 'Sau khi ngang hàng gửi thêm dữ liệu, recv()sẽ trả lại dữ liệu' là không chính xác. 2. Hành vi nếu ngang hàng gửi thêm dữ liệu sau SHUT_RDphụ thuộc vào nền tảng.
Hầu tước Lorne

@EJP Tôi đã thử bản thân mình, xin lỗi tôi quên nền tảng nào tôi thực hiện bài kiểm tra này, centos hoặc Mac. và đây là những gì tôi nhận được
simpx
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.