tutup vs shutdown socket?

214

Dalam C, saya mengerti bahwa jika kita menutup soket, artinya soket tersebut akan hancur dan dapat digunakan kembali nanti.

Bagaimana dengan shutdown? Deskripsi mengatakan itu menutup setengah dari koneksi duplex ke soket itu. Tetapi apakah soket itu akan hancur seperti closesystem call?

tepang
sumber
Itu tidak dapat digunakan kembali nanti. Sudah ditutup. Jadi. Selesai
Marquis of Lorne

Jawaban:

191

Ini dijelaskan dalam panduan jaringan Beej. shutdownadalah cara yang fleksibel untuk memblokir komunikasi di satu atau kedua arah. Ketika parameter kedua adalah SHUT_RDWR, itu akan memblokir pengiriman dan penerimaan (seperti close). Namun, closeadalah cara untuk benar-benar menghancurkan soket.

Dengan shutdown, Anda masih dapat menerima data tertunda yang sudah dikirim rekan (terima kasih kepada Joey Adams karena mencatat ini).

Matthew Flaschen
sumber
23
Perlu diingat, bahkan jika Anda menutup () soket TCP, toh itu tidak serta merta dapat digunakan kembali, karena itu akan berada dalam keadaan TIME_WAIT sementara OS memastikan tidak ada paket luar biasa yang mungkin membingungkan sebagai informasi baru jika Anda adalah untuk segera menggunakan kembali soket itu untuk sesuatu yang lain.
alesplin
91
Perbedaan besar antara shutdown dan tutup pada soket adalah perilaku ketika soket dibagi oleh proses lain. Shutdown () memengaruhi semua salinan socket saat close () hanya memengaruhi deskriptor file dalam satu proses.
Zan Lynx
5
Salah satu alasan Anda mungkin ingin menggunakan shutdownkedua arah tetapi tidak closeadalah jika Anda membuat FILEreferensi ke soket menggunakan fdopen. Jika Anda closesocket, file yang baru dibuka dapat diberikan fd yang sama, dan penggunaan selanjutnya FILEakan membaca / menulis tempat yang salah yang bisa sangat buruk. Jika Anda baru saja shutdown, penggunaan berikutnya FILEhanya akan memberikan kesalahan sampai fclosedipanggil.
R .. GitHub BERHENTI MEMBANTU ICE
25
Komentar tentang TIME_WAIT salah. Itu berlaku untuk port, bukan untuk soket. Anda tidak dapat menggunakan kembali soket.
Marquis of Lorne
48
-1. Baik pos ini maupun tautannya mengabaikan alasan konseptual penting yang ingin digunakan shutdown: untuk memberi sinyal EOF ke rekan dan masih dapat menerima data yang tertunda yang dikirim rekan.
Joey Adams
144

Tidak ada jawaban yang ada yang memberi tahu orang bagaimana shutdowndan closebekerja pada tingkat protokol TCP, jadi perlu menambahkan ini.

Koneksi TCP standar diakhiri oleh finalisasi 4 arah:

  1. Setelah peserta tidak memiliki data lagi untuk dikirim, ia mengirim paket FIN ke yang lain
  2. Pihak lain mengembalikan ACK untuk FIN.
  3. Ketika pihak lain juga selesai mentransfer data, ia mengirim paket FIN lain
  4. Peserta awal mengembalikan ACK dan menyelesaikan transfer.

Namun, ada cara "muncul" lain untuk menutup koneksi TCP:

  1. Seorang peserta mengirim paket RST dan meninggalkan koneksi
  2. Sisi lain menerima RST dan kemudian meninggalkan koneksi juga

Dalam pengujian saya dengan Wireshark, dengan opsi soket default, shutdownmengirim paket FIN ke ujung lain tetapi hanya itu yang dilakukannya. Sampai pihak lain mengirimi Anda paket FIN, Anda masih dapat menerima data. Setelah ini terjadi, Anda Receiveakan mendapatkan hasil ukuran 0. Jadi, jika Anda adalah orang pertama yang mematikan "kirim", Anda harus menutup soket setelah Anda selesai menerima data.

Di sisi lain, jika Anda menelepon closesaat koneksi masih aktif (sisi lain masih aktif dan Anda mungkin memiliki data yang belum terkirim di buffer sistem juga), paket RST akan dikirim ke sisi lain. Ini bagus untuk kesalahan. Misalnya, jika Anda berpikir pihak lain memberikan data yang salah atau menolak memberikan data (serangan DOS?), Anda dapat langsung menutup soket.

Pendapat saya tentang aturan adalah:

  1. Pertimbangkan shutdownsebelumnya closejika memungkinkan
  2. Jika Anda selesai menerima (0 data ukuran diterima) sebelum Anda memutuskan untuk mematikan, tutup koneksi setelah pengiriman terakhir (jika ada) selesai.
  3. Jika Anda ingin menutup koneksi secara normal, matikan koneksi (dengan SHUT_WR, dan jika Anda tidak peduli tentang menerima data setelah titik ini, dengan SHUT_RD juga), dan tunggu sampai Anda menerima data ukuran 0, dan kemudian tutup stopkontak.
  4. Bagaimanapun, jika ada kesalahan lain terjadi (timeout misalnya), cukup tutup soketnya.

Implementasi ideal untuk SHUT_RD dan SHUT_WR

Berikut ini belum diuji, percaya dengan risiko Anda sendiri. Namun, saya percaya ini adalah cara yang masuk akal dan praktis untuk melakukan sesuatu.

Jika TCP stack menerima shutdown hanya dengan SHUT_RD, itu akan menandai koneksi ini karena tidak ada lagi data yang diharapkan. Segala permintaan yang tertunda dan selanjutnya read(terlepas dari utas mana pun mereka berada) kemudian akan dikembalikan dengan hasil ukuran nol. Namun, koneksi masih aktif dan dapat digunakan - Anda masih dapat menerima data OOB, misalnya. Juga, OS akan menjatuhkan data apa pun yang diterimanya untuk koneksi ini. Tapi itu saja, tidak ada paket yang akan dikirim ke sisi lain.

Jika TCP stack menerima shutdown hanya dengan SHUT_WR, itu akan menandai koneksi ini karena tidak ada lagi data yang dapat dikirim. Semua permintaan penulisan yang tertunda akan selesai, tetapi permintaan penulisan berikutnya akan gagal. Selain itu, paket FIN akan dikirim ke pihak lain untuk memberi tahu mereka bahwa kami tidak memiliki lebih banyak data untuk dikirim.

Mesin Tanah
sumber
2
"Jika Anda menelepon dekat ketika koneksi masih hidup" adalah tautologis, dan itu tidak menyebabkan RST dikirim. (1) tidak perlu. Dalam (4), batas waktu tidak selalu berakibat fatal pada koneksi dan tidak berubah-ubah menunjukkan Anda dapat menutupnya.
Marquis of Lorne
4
@ EJP Tidak, ini tidak tautologis. Anda dapat shutdown()koneksi dan kemudian tidak lagi hidup. Anda masih memiliki deskriptor file. Anda masih dapat recv()dari buffer penerima. Dan Anda masih perlu menelepon close()untuk membuang file descriptor.
Pavel Šimerda
Ini adalah jawaban terbaik mengenai format kawat. Saya tertarik dengan detail lebih lanjut tentang mematikan dengan SHUT_RD. Tidak ada pensinyalan TCP untuk tidak mengharapkan lebih banyak data, bukan? Bukankah hanya FIN untuk pensinyalan karena tidak mengirim lebih banyak data?
Pavel Šimerda
3
@ PavelŠimerda Ya TCP tidak memberi sinyal karena tidak mengharapkan lebih banyak data. Ini harus dipertimbangkan dalam protokol tingkat tinggi. Menurut pendapat saya, ini secara umum tidak perlu. Anda dapat menutup pintu, tetapi Anda tidak dapat menghentikan orang menaruh hadiah di depan pintu. Ini adalah keputusan MEREKA, bukan milikmu.
Earth Engine
1
@ EJP Apakah Anda memiliki argumen yang sebenarnya? Kalau tidak, aku tidak tertarik.
Pavel Šimerda
35

Ada beberapa batasan dengan close()yang bisa dihindari jika seseorang menggunakannya shutdown()sebagai gantinya.

close()akan mengakhiri kedua arah pada koneksi TCP. Terkadang Anda ingin memberi tahu titik akhir lain bahwa Anda selesai mengirim data, tetapi tetap ingin menerima data.

close()mengurangi jumlah referensi deskriptor (dipertahankan dalam entri tabel file dan menghitung jumlah deskriptor saat ini terbuka yang mengacu pada file / socket) dan tidak menutup socket / file jika deskriptor bukan 0. Ini berarti bahwa jika Anda forking, pembersihan terjadi hanya setelah jumlah referensi turun menjadi 0. Dengan shutdown()satu dapat memulai urutan dekat TCP normal mengabaikan jumlah referensi.

Parameternya adalah sebagai berikut:

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

int how dapat:

SHUT_RDatau 0 Penerimaan lebih lanjut tidak diizinkan

SHUT_WRatau 1 Pengiriman lebih lanjut tidak diizinkan

SHUT_RDWRatau 2 Pengiriman dan penerimaan lebih lanjut tidak diizinkan

Milan
sumber
8
Kedua fungsi tersebut untuk tujuan yang sepenuhnya berbeda. Fakta bahwa penutupan akhir pada soket memulai urutan pematian jika seseorang belum dimulai tidak mengubah fakta bahwa penutupan adalah untuk membersihkan struktur data soket dan pematian adalah untuk memulai urutan pematian tingkat tcp.
Len Holgate
25
Anda tidak bisa 'menggunakan shutdown saja'. Anda bisa menggunakannya juga. tetapi Anda harus menutup soket beberapa saat.
Marquis of Lorne
15

Ini mungkin platform spesifik, entah bagaimana saya meragukannya, tapi bagaimanapun, penjelasan terbaik yang saya lihat ada di sini di halaman msdn ini di mana mereka menjelaskan tentang shutdown, opsi berlama-lama, penutupan soket dan urutan pemutusan koneksi umum.

Singkatnya, gunakan shutdown untuk mengirim urutan shutdown pada level TCP dan gunakan close untuk membebaskan sumber daya yang digunakan oleh struktur data socket dalam proses Anda. Jika Anda belum mengeluarkan urutan penutupan eksplisit pada saat Anda menelepon dekat maka satu dimulai untuk Anda.

Len Holgate
sumber
9

Saya juga telah sukses menggunakan linux shutdown()dari satu pthread untuk memaksa pthread lain yang saat ini diblokir connect()untuk dibatalkan lebih awal.

Di bawah OS lain (setidaknya OSX), saya menemukan panggilan close()sudah cukup untuk connect()gagal.

Toby
sumber
7

"shutdown () sebenarnya tidak menutup deskriptor file — itu hanya mengubah kegunaannya. Untuk membebaskan deskriptor socket, Anda perlu menggunakan close ()." 1


sumber
3

Menutup

Setelah selesai menggunakan soket, Anda dapat menutup deskriptor file dengan tutup; Jika masih ada data yang menunggu untuk dikirim melalui koneksi, biasanya tutup mencoba menyelesaikan transmisi ini. Anda dapat mengontrol perilaku ini menggunakan opsi soket SO_LINGER untuk menentukan periode waktu habis; lihat Opsi Soket.

Mematikan

Anda juga dapat mematikan hanya penerimaan atau transmisi pada suatu koneksi dengan memanggil shutdown.

Fungsi shutdown mematikan koneksi soket. Argumennya bagaimana menentukan tindakan apa yang harus dilakukan: 0 Berhenti menerima data untuk soket ini. Jika data lebih lanjut tiba, tolak saja. 1 Berhenti mencoba mengirim data dari soket ini. Buang semua data yang menunggu untuk dikirim. Berhenti mencari pengakuan data yang sudah dikirim; jangan kirim ulang jika hilang. 2 Hentikan penerimaan dan transmisi.

Nilai kembali adalah 0 pada keberhasilan dan -1 pada kegagalan.

Neha Agrawal
sumber
2

dalam pengujian saya.

close akan mengirim paket sirip dan menghancurkan fd segera ketika soket tidak dibagi dengan proses lain

shutdown SHUT_RD , proses masih dapat memulihkan data dari soket, tetapi recvakan mengembalikan 0 jika TCP buffer kosong. Setelah rekan mengirim lebih banyak data, recvakan mengembalikan data lagi.

shutdown SHUT_WR akan mengirim paket sirip untuk menunjukkan pengiriman Lebih Lanjut tidak diizinkan. peer dapat memulihkan data tetapi akan memulihkan 0 jika buffer TCP-nya kosong

shutdown SHUT_RDWR (sama dengan menggunakan SHUT_RD dan SHUT_WR ) akan mengirim paket pertama jika rekan mengirim lebih banyak data.

simpx
sumber
Saya baru saja mencobanya dan close()mengirim RST di Linux alih-alih FIN.
Pavel Šimerda
Juga apakah Anda benar-benar ingin menyatakan bahwa setelah mematikan sisi bacaan, program akan menerima lebih banyak data?
Pavel Šimerda
@ PavelŠimerda saya pikir itu mungkin tergantung pada kondisi tcp untuk mengirim RST atau FIN? saya tidak yakin.
simpx
1. 'Setelah rekan mengirim lebih banyak data, recv()akan mengembalikan data lagi' tidak benar. 2. Perilaku jika peer mengirim lebih banyak data setelah SHUT_RDitu tergantung pada platform.
Marquis of Lorne
@ EJP Saya mencoba sendiri, maaf saya lupa platform yang saya lakukan tes ini, centos atau Mac. dan inilah yang saya dapatkan
simpx