Kapan opsi TCP SO_LINGER (0) diperlukan?

96

Saya rasa saya memahami arti formal dari opsi tersebut. Di beberapa kode lama yang saya tangani sekarang, opsi digunakan. Pelanggan mengeluhkan RST sebagai respon terhadap FIN dari sisinya pada koneksi yang ditutup dari sisinya.

Saya tidak yakin saya dapat menghapusnya dengan aman, karena saya tidak mengerti kapan harus digunakan.

Bisakah Anda memberi contoh kapan opsi akan diperlukan?

dimba
sumber
1
Anda harus menghapusnya. Ini tidak boleh digunakan dalam kode produksi. Satu-satunya saat saya melihatnya digunakan adalah sebagai hasil dari tolok ukur yang tidak valid.
Marquis dari Lorne

Jawaban:

83

Alasan umum untuk menyetel waktu SO_LINGERtunggu nol adalah untuk menghindari koneksi dalam jumlah besar yang berada dalam TIME_WAITstatus, mengikat semua sumber daya yang tersedia di server.

Ketika koneksi TCP ditutup dengan bersih, ujung yang memulai penutupan ("tutup aktif") berakhir dengan koneksi diam TIME_WAITselama beberapa menit. Jadi, jika protokol Anda adalah protokol di mana server memulai koneksi untuk menutup, dan melibatkan koneksi jangka pendek dalam jumlah yang sangat besar, maka mungkin rentan terhadap masalah ini.

Ini bukan ide yang bagus - TIME_WAIT ada karena suatu alasan (untuk memastikan bahwa paket yang tersesat dari koneksi lama tidak mengganggu koneksi baru). Ide yang lebih baik untuk mendesain ulang protokol Anda ke protokol di mana klien memulai koneksi dekat, jika memungkinkan.

kafe
sumber
4
Saya sangat setuju. Saya telah melihat aplikasi pemantauan yang memulai banyak (beberapa ribu koneksi berumur pendek setiap X detik), dan memiliki masalah untuk skala lebih besar (seribu koneksi lebih banyak). Saya tidak tahu kenapa, tapi aplikasinya tidak responsif. Seseorang menyarankan SO_LINGER = true, TIME_WAIT = 0 untuk membebaskan sumber daya OS dengan cepat, dan setelah penyelidikan singkat kami mencoba solusi ini dengan hasil yang sangat bagus. TIME_WAIT tidak lagi menjadi masalah untuk aplikasi ini.
bartosz.r
24
Saya tidak setuju. Protokol tingkat aplikasi yang berada di atas TCP harus dirancang sedemikian rupa sehingga klien selalu memulai penutupan koneksi. Dengan begitu, TIME_WAITkeinginan klien tidak akan merugikan. Ingat seperti yang tertulis dalam "Pemrograman Jaringan UNIX" edisi ketiga (Stevens dkk) halaman 203: "Status TIME_WAIT adalah teman Anda dan ada untuk membantu kami. Daripada mencoba menghindari keadaan, kami harus memahaminya (Bagian 2.7) . "
mgd
8
Bagaimana jika klien ingin membuka 4000 koneksi setiap 30 detik (aplikasi pemantauan ini adalah klien! Karena memulai koneksi)? Ya, kami dapat mengubah aplikasi, menambahkan beberapa agen lokal di infrastruktur, mengubah model menjadi push. Tetapi jika kita sudah memiliki aplikasi seperti itu dan terus bertambah, maka kita bisa membuatnya bekerja dengan cara tuning twe linger. Anda mengubah satu parameter, dan Anda tiba-tiba memiliki aplikasi yang berfungsi, tanpa menginvestasikan anggaran untuk mengimplementasikan arsitektur baru.
bartosz.r
4
@ bartosz.r: Saya hanya mengatakan bahwa menggunakan SO_LINGER dengan batas waktu 0 seharusnya menjadi pilihan terakhir. Sekali lagi, dalam "Pemrograman Jaringan UNIX" edisi ketiga (Stevens et al) halaman 203 juga dikatakan bahwa Anda berisiko mengalami kerusakan data. Pertimbangkan untuk membaca RFC 1337 di mana Anda dapat melihat mengapa TIME_WAIT adalah teman Anda.
mgd
7
@caf Tidak, solusi klasiknya adalah kumpulan koneksi, seperti yang terlihat di setiap API TCP tugas berat, misalnya HTTP 1.1.
Marquis dari Lorne
191

Untuk saran saya, silakan baca bagian terakhir: “Kapan menggunakan SO_LINGER dengan waktu tunggu 0” .

Sebelum kita sampai pada kuliah kecil itu tentang:

  • Pengakhiran TCP normal
  • TIME_WAIT
  • FIN, ACKdanRST

Pengakhiran TCP normal

Urutan terminasi TCP normal terlihat seperti ini (disederhanakan):

Kami memiliki dua rekan: A dan B

  1. A panggilan close()
    • A mengirim FINke B
    • A masuk ke FIN_WAIT_1negara bagian
  2. B menerima FIN
    • B mengirim ACKke A
    • B masuk ke CLOSE_WAITnegara bagian
  3. A menerima ACK
    • A masuk ke FIN_WAIT_2negara bagian
  4. B. panggilan close()
    • B mengirim FINke A
    • B masuk ke LAST_ACKnegara bagian
  5. A menerima FIN
    • A mengirim ACKke B
    • A masuk ke TIME_WAITnegara bagian
  6. B menerima ACK
    • B pergi ke CLOSEDnegara - yaitu dihapus dari tabel soket

WAKTU MENUNGGU

Jadi rekan yang memulai penghentian - yaitu panggilan close()pertama - akan berakhir di TIME_WAITstatus.

Untuk memahami mengapa TIME_WAITnegara adalah teman kita, silakan baca bagian 2.7 dalam "Pemrograman Jaringan UNIX" edisi ketiga oleh Stevens et al (halaman 43).

Namun, ini bisa menjadi masalah dengan banyak soket di dalamnya TIME_WAIT status di server karena pada akhirnya dapat mencegah koneksi baru diterima.

Untuk mengatasi masalah ini, saya telah melihat banyak yang menyarankan untuk menyetel opsi soket SO_LINGER dengan batas waktu 0 sebelum menelepon close(). Namun, ini adalah solusi yang buruk karena menyebabkan koneksi TCP diakhiri dengan kesalahan.

Sebaliknya, rancang protokol aplikasi Anda sehingga penghentian koneksi selalu dimulai dari sisi klien. Jika klien selalu tahu ketika telah membaca semua data yang tersisa, ia dapat memulai urutan penghentian. Sebagai contoh, browser mengetahui dari Content-Lengthheader HTTP ketika telah membaca semua data dan dapat memulai penutupan. (Saya tahu bahwa di HTTP 1.1 itu akan membuatnya tetap terbuka untuk sementara waktu untuk kemungkinan digunakan kembali, dan kemudian menutupnya.)

Jika server perlu menutup koneksi, rancang protokol aplikasi sehingga server meminta klien untuk menelepon close() .

Kapan menggunakan SO_LINGER dengan waktu tunggu 0

Sekali lagi, menurut "Pemrograman Jaringan UNIX" edisi ketiga halaman 202-203, pengaturan SO_LINGERdengan batas waktu 0 sebelum panggilan close()akan menyebabkan urutan penghentian normal tidak dimulai.

Sebaliknya, rekan yang mengatur opsi ini dan memanggil close()akan mengirim RST(reset koneksi) yang menunjukkan kondisi kesalahan dan ini akan dianggap di ujung lain. Anda biasanya akan melihat kesalahan seperti "Sambungan disetel ulang oleh rekan".

Oleh karena itu, dalam situasi normal, adalah ide yang sangat buruk untuk menyetel SO_LINGERdengan batas waktu 0 sebelum menelepon close()- mulai sekarang disebut abortive close - dalam aplikasi server.

Namun, situasi tertentu tetap harus dilakukan:

  • Jika klien aplikasi server Anda berperilaku tidak semestinya (waktu habis, mengembalikan data yang tidak valid, dll.) Penutupan yang gagal masuk akal untuk menghindari macet CLOSE_WAITatau berakhir di TIME_WAITstatus.
  • Jika Anda harus memulai ulang aplikasi server Anda yang saat ini memiliki ribuan koneksi klien, Anda dapat mempertimbangkan untuk mengatur opsi soket ini untuk menghindari ribuan soket server masuk TIME_WAIT(saat menelepon close()dari ujung server) karena ini mungkin mencegah server mendapatkan port yang tersedia untuk koneksi klien baru setelah di-restart.
  • Pada halaman 202 di buku tersebut secara khusus mengatakan: "Ada keadaan tertentu yang mengharuskan menggunakan fitur ini untuk mengirim penutupan yang gagal. Salah satu contohnya adalah server terminal RS-232, yang mungkin hang selamanya saat CLOSE_WAITmencoba mengirimkan data ke terminal yang macet port, tetapi akan dengan benar mengatur ulang port yang macet jika harus RSTmembuang data yang tertunda. "

Saya akan merekomendasikan ini artikel panjang yang saya percaya memberikan jawaban yang sangat baik untuk pertanyaan Anda.

mgd
sumber
6
TIME_WAITadalah teman hanya jika tidak mulai menimbulkan masalah: stackoverflow.com/questions/1803566/…
Pacerier
2
jadi bagaimana jika Anda menulis web server? bagaimana Anda "memberi tahu klien untuk memulai penutupan"?
Shaun Neal
2
@ShaunNeal Anda jelas tidak. Tetapi klien / browser yang ditulis dengan baik akan memulai penutupan. Jika klien tidak berperilaku baik, untungnya kami memiliki pembunuhan TIME_WAIT untuk memastikan kami tidak kehabisan deskriptor soket dan port sementara.
mgd
Ini adalah jawaban yang sangat bagus. Terima kasih!
Juraj Martinka
17

Ketika berlama-lama aktif tetapi waktu tunggu nol, tumpukan TCP tidak menunggu data yang tertunda dikirim sebelum menutup koneksi. Data bisa hilang karena ini tetapi dengan menyetel berlama-lama dengan cara ini Anda menerima ini dan meminta agar koneksi segera disetel ulang daripada ditutup dengan anggun. Hal ini menyebabkan RST dikirim daripada FIN biasa.

Terima kasih kepada EJP atas komentarnya, lihat di sini untuk detailnya.

Len Holgate
sumber
1
Saya mengerti ini. yang saya minta adalah contoh "realistis" ketika kita ingin menggunakan pengaturan ulang paksa.
dimba
5
Kapanpun Anda ingin membatalkan koneksi; jadi jika protokol Anda gagal validasi dan Anda memiliki klien yang membicarakan sampah pada Anda tiba-tiba Anda akan membatalkan koneksi dengan RST, dll.
Len Holgate
5
Anda mengacaukan batas waktu nol berlama-lama dengan berlama-lama. Linger off berarti close () tidak memblokir. Berlama-lama dengan waktu tunggu positif berarti tutup () memblokir hingga batas waktu. Berlama-lama dengan waktu tunggu nol menyebabkan RST, dan inilah pertanyaannya.
Marquis dari Lorne
2
Ya, Anda benar. Saya akan menyesuaikan jawaban untuk memperbaiki terminologi saya.
Len Holgate
6

Apakah Anda dapat menghapus sisa kode Anda dengan aman atau tidak tergantung pada jenis aplikasi Anda: apakah itu "klien" (membuka koneksi TCP dan menutupnya secara aktif terlebih dahulu) atau apakah itu "server" (mendengarkan TCP terbuka dan menutupnya setelah pihak lain memulai penutupan)?

Jika aplikasi Anda memiliki cita rasa "klien" (menutup dulu) DAN Anda memulai & menutup sejumlah besar koneksi ke server yang berbeda (misalnya, saat aplikasi Anda adalah aplikasi pemantauan yang mengawasi jangkauan dari sejumlah besar server yang berbeda) aplikasi Anda memiliki masalah bahwa semua koneksi klien Anda macet dalam status TIME_WAIT. Kemudian, saya akan merekomendasikan untuk mempersingkat waktu tunggu ke nilai yang lebih kecil daripada default untuk tetap mematikan dengan baik tetapi membebaskan sumber daya koneksi klien sebelumnya. Saya tidak akan menyetel batas waktu ke 0, karena 0 tidak dimatikan dengan baik dengan FIN tetapi gagal dengan RST.

Jika aplikasi Anda memiliki rasa "klien" dan harus mengambil sejumlah besar file kecil dari server yang sama, Anda tidak boleh memulai koneksi TCP baru per file dan berakhir dengan koneksi klien dalam jumlah besar di TIME_WAIT, tetapi biarkan koneksi tetap terbuka dan ambil semua data melalui koneksi yang sama. Opsi berlama-lama dapat dan harus dihapus.

Jika aplikasi Anda adalah sebuah "server" (tutup kedua sebagai reaksi terhadap penutupan peer), pada close () koneksi Anda dimatikan dengan baik dan sumber daya dibebaskan karena Anda tidak memasuki status TIME_WAIT. Linger sebaiknya tidak digunakan. Tetapi jika aplikasi server Anda memiliki proses pengawasan yang mendeteksi koneksi terbuka yang tidak aktif menganggur untuk waktu yang lama ("panjang" harus ditentukan), Anda dapat mematikan koneksi yang tidak aktif ini dari sisi Anda - melihatnya sebagai jenis penanganan kesalahan - dengan pematian yang gagal. Ini dilakukan dengan menyetel linger timeout ke 0. close () kemudian akan mengirim RST ke klien, memberitahunya bahwa Anda marah :-)

Grandswiss
sumber
1

Di server, Anda mungkin ingin mengirim RSTalih-alih FINsaat memutuskan klien yang berperilaku tidak semestinya. Lompatan itu FIN-WAITdiikuti oleh TIME-WAITstatus soket di server, yang mencegah menipisnya sumber daya server, dan, karenanya, melindungi dari serangan penolakan layanan semacam ini.

Maxim Egorushkin
sumber
1

Saya suka pengamatan Maxim bahwa serangan DOS dapat menghabiskan sumber daya server. Itu juga terjadi tanpa musuh yang benar-benar jahat.

Beberapa server harus berurusan dengan 'serangan DOS yang tidak disengaja' yang terjadi ketika aplikasi klien memiliki bug dengan kebocoran koneksi, di mana mereka terus membuat koneksi baru untuk setiap perintah baru yang mereka kirim ke server Anda. Dan kemudian mungkin akhirnya menutup koneksi mereka jika mereka terkena tekanan GC, atau mungkin koneksi akhirnya timeout.

Skenario lain adalah ketika skenario 'semua klien memiliki alamat TCP yang sama'. Kemudian koneksi klien hanya dapat dibedakan dengan nomor port (jika terhubung ke satu server). Dan jika klien mulai berputar cepat membuka / menutup koneksi karena alasan apa pun, mereka dapat menghabiskan ruang tuple (addr klien + port, server IP + port).

Jadi saya pikir server mungkin paling disarankan untuk beralih ke strategi Linger-Zero ketika mereka melihat sejumlah besar soket dalam status TIME_WAIT - meskipun itu tidak memperbaiki perilaku klien, ini mungkin mengurangi dampaknya.

Tim Lovell-Smith
sumber
0

Soket pendengar pada server dapat menggunakan linger dengan waktu 0 untuk memiliki akses untuk mengikat kembali ke soket segera dan untuk mengatur ulang klien yang koneksinya belum selesai terhubung. TIME_WAIT adalah sesuatu yang hanya menarik ketika Anda memiliki jaringan multi-jalur dan dapat berakhir dengan paket yang tidak dipesan atau berurusan dengan pemesanan paket jaringan yang aneh / waktu kedatangan.

Gregg Wonderly
sumber