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 close
system call?
c
sockets
networking
tepang
sumber
sumber
Jawaban:
Ini dijelaskan dalam panduan jaringan Beej.
shutdown
adalah cara yang fleksibel untuk memblokir komunikasi di satu atau kedua arah. Ketika parameter kedua adalahSHUT_RDWR
, itu akan memblokir pengiriman dan penerimaan (seperticlose
). Namun,close
adalah 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).sumber
shutdown
kedua arah tetapi tidakclose
adalah jika Anda membuatFILE
referensi ke soket menggunakanfdopen
. Jika Andaclose
socket, file yang baru dibuka dapat diberikan fd yang sama, dan penggunaan selanjutnyaFILE
akan membaca / menulis tempat yang salah yang bisa sangat buruk. Jika Anda baru sajashutdown
, penggunaan berikutnyaFILE
hanya akan memberikan kesalahan sampaifclose
dipanggil.shutdown
: untuk memberi sinyal EOF ke rekan dan masih dapat menerima data yang tertunda yang dikirim rekan.Tidak ada jawaban yang ada yang memberi tahu orang bagaimana
shutdown
danclose
bekerja pada tingkat protokol TCP, jadi perlu menambahkan ini.Koneksi TCP standar diakhiri oleh finalisasi 4 arah:
Namun, ada cara "muncul" lain untuk menutup koneksi TCP:
Dalam pengujian saya dengan Wireshark, dengan opsi soket default,
shutdown
mengirim 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, AndaReceive
akan 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
close
saat 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:
shutdown
sebelumnyaclose
jika memungkinkanImplementasi 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.
sumber
shutdown()
koneksi dan kemudian tidak lagi hidup. Anda masih memiliki deskriptor file. Anda masih dapatrecv()
dari buffer penerima. Dan Anda masih perlu meneleponclose()
untuk membuang file descriptor.Ada beberapa batasan dengan
close()
yang bisa dihindari jika seseorang menggunakannyashutdown()
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. Denganshutdown()
satu dapat memulai urutan dekat TCP normal mengabaikan jumlah referensi.Parameternya adalah sebagai berikut:
int how
dapat:SHUT_RD
atau0
Penerimaan lebih lanjut tidak diizinkanSHUT_WR
atau1
Pengiriman lebih lanjut tidak diizinkanSHUT_RDWR
atau2
Pengiriman dan penerimaan lebih lanjut tidak diizinkansumber
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.
sumber
Saya juga telah sukses menggunakan linux
shutdown()
dari satu pthread untuk memaksa pthread lain yang saat ini diblokirconnect()
untuk dibatalkan lebih awal.Di bawah OS lain (setidaknya OSX), saya menemukan panggilan
close()
sudah cukup untukconnect()
gagal.sumber
"shutdown () sebenarnya tidak menutup deskriptor file — itu hanya mengubah kegunaannya. Untuk membebaskan deskriptor socket, Anda perlu menggunakan close ()." 1
sumber
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.
sumber
dalam pengujian saya.
close
akan mengirim paket sirip dan menghancurkan fd segera ketika soket tidak dibagi dengan proses lainshutdown
SHUT_RD , proses masih dapat memulihkan data dari soket, tetapirecv
akan mengembalikan 0 jika TCP buffer kosong. Setelah rekan mengirim lebih banyak data,recv
akan 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 kosongshutdown
SHUT_RDWR (sama dengan menggunakan SHUT_RD dan SHUT_WR ) akan mengirim paket pertama jika rekan mengirim lebih banyak data.sumber
close()
mengirim RST di Linux alih-alih FIN.recv()
akan mengembalikan data lagi' tidak benar. 2. Perilaku jika peer mengirim lebih banyak data setelahSHUT_RD
itu tergantung pada platform.