Bagaimana cara menutup soket secara paksa di TIME_WAIT?

113

Saya menjalankan program tertentu di linux yang terkadang macet. Jika Anda membukanya dengan cepat setelah itu, ia mendengarkan pada socket 49201 alih-alih 49200 seperti yang terjadi pertama kali. netstat mengungkapkan bahwa 49200 dalam keadaan TIME_WAIT.

Apakah ada program yang dapat Anda jalankan untuk segera memaksa soket keluar dari status TIME_WAIT?

Rehan Khwaja
sumber
1
Jika Anda di sini karena "terlalu banyak TIME_WAITdi server" , lewati saja tiga jawaban pertama yang menghindari pertanyaan alih-alih menjawabnya.
Pacerier

Jawaban:

148
/etc/init.d/networking restart

Biarkan saya uraikan. Transmission Control Protocol (TCP) dirancang untuk menjadi protokol transmisi data dua arah, terurut, dan andal antara dua titik akhir (program). Dalam konteks ini, istilah reliable artinya akan mentransmisikan kembali paket jika hilang di tengah. TCP menjamin keandalan dengan mengirimkan kembali paket Acknowledgement (ACK) untuk satu atau serangkaian paket yang diterima dari rekan.

Ini berlaku untuk sinyal kontrol seperti permintaan / respons penghentian. RFC 793 mendefinisikan status TIME-WAIT sebagai:

TIME-WAIT - mewakili waktu tunggu yang cukup untuk memastikan TCP jarak jauh menerima pemberitahuan permintaan penghentian koneksi.

Lihat diagram keadaan TCP berikut: teks alternatif

TCP adalah protokol komunikasi dua arah, jadi ketika koneksi dibuat, tidak ada perbedaan antara klien dan server. Juga, salah satu dapat memanggil berhenti, dan kedua rekan perlu menyetujui untuk menutup untuk sepenuhnya menutup koneksi TCP yang ada.

Mari kita panggil yang pertama untuk memanggil berhenti sebagai yang lebih dekat aktif, dan rekan yang lain lebih dekat pasif. Ketika semakin dekat aktif mengirim FIN, negara pergi ke FIN-WAIT-1. Kemudian ia menerima ACK untuk FIN yang dikirim dan negara bagian pergi ke FIN-WAIT-2. Setelah menerima FIN juga dari pasif dekat, semakin dekat aktif mengirimkan ACK ke FIN dan negara pergi ke WAKTU-TUNGGU. Jika pasif dekat tidak menerima ACK ke FIN kedua, itu akan mentransmisikan kembali paket FIN.

RFC 793 menetapkan TIME-OUT menjadi dua kali Seumur Hidup Maksimum, atau 2MSL. Karena MSL, waktu maksimum sebuah paket dapat berkeliaran di Internet, diatur ke 2 menit, 2MSL adalah 4 menit. Karena tidak ada ACK ke ACK, semakin dekat aktif tidak dapat melakukan apa pun kecuali menunggu 4 menit jika mematuhi protokol TCP / IP dengan benar, kalau-kalau pengirim pasif belum menerima ACK ke FIN (secara teoritis) .

Pada kenyataannya, paket yang hilang mungkin langka, dan sangat langka jika semuanya terjadi di dalam LAN atau dalam satu mesin.

Untuk menjawab pertanyaan dengan kata demi kata, Bagaimana cara menutup paksa soket di TIME_WAIT ?, saya masih akan tetap menggunakan jawaban asli saya:

/etc/init.d/networking restart

Secara praktis, saya akan memprogramnya sehingga mengabaikan status TIME-WAIT menggunakan opsi SO_REUSEADDR seperti yang disebutkan WMR. Apa sebenarnya yang dilakukan SO_REUSEADDR?

Opsi soket ini memberi tahu kernel bahwa meskipun port ini sibuk (dalam
status TIME_WAIT), silakan melanjutkan dan menggunakannya kembali. Jika sibuk, tetapi dengan keadaan lain, Anda masih akan mendapatkan alamat yang sudah dalam kesalahan penggunaan. Ini berguna jika server Anda telah dimatikan, dan kemudian restart segera saat soket masih aktif di port-nya. Anda harus menyadari bahwa jika ada data tak terduga yang masuk, itu mungkin membingungkan server Anda, tetapi sementara ini mungkin, itu tidak mungkin.

Eugene Yokota
sumber
8
Jawaban yang bagus, tetapi bukan jawaban yang benar untuk pertanyaannya. Memulai kembali jaringan akan berhasil, tetapi begitu juga me-reboot, jadi ini tidak benar.
Chris Huang-Leaver
3
@ Chris Huang-Leaver, pertanyaannya adalah "Apakah ada program yang dapat Anda jalankan untuk segera memaksa soket keluar dari negara TIME_WAIT?" jika me-reboot bisa dianggap menjalankan program, maka itu juga akan menjadi jawaban yang tepat. Menurut Anda mengapa ini tidak benar?
Eugene Yokota
8
WMR memiliki jawaban yang paling berguna (yang saya lakukan ketika saya mengalami masalah seperti ini). Restart jaringan terlalu drastis untuk menjadi solusi, dan bisa memakan waktu lebih lama daripada hanya menunggu waktu habis. Jawaban yang benar untuk pertanyaannya adalah 'Tidak', tetapi SO tidak akan membiarkan Anda mengetik dua jawaban huruf :-)
Chris Huang- Leaver
6
oh oke, lain kali beberapa proses hang pada SIGTERM saya hanya akan menghancurkan komputer saya bukannya memperbaikinya.
Longpoke
Generalisasi ini adalah "restart layanan jaringan". Lokasi spesifik /etc/init.d/networkingadalah platform-spesifik (Debian?) Sehingga baris perintah yang tepat akan berbeda (kadang-kadang agak radikal) untuk sistem lain. Saya setuju dengan komentator lain bahwa ini seperti kerja keras yang berlebihan dan jelas mengganggu layanan jaringan yang tidak terkait.
tripleee
51

Saya tidak tahu apakah Anda memiliki kode sumber dari program tertentu yang sedang Anda jalankan, tetapi jika demikian Anda bisa mengatur SO_REUSEADDR via setsockopt(2)yang memungkinkan Anda untuk mengikat pada alamat lokal yang sama bahkan jika soketnya dalam keadaan TIME_WAIT (kecuali jika itu soket sedang mendengarkan secara aktif, lihat socket(7)).

Untuk informasi lebih lanjut tentang keadaan TIME_WAIT lihat FAQ soket Unix .

WMR
sumber
tetapi saya tidak mendapatkan kesalahan yang sudah terikat. ketika saya menjalankan program lagi itu mendengarkan dalam pos (123456) juga saya dapat melihat bahwa sistem menunjukkan TIME_WAIT untuk port itu tetapi saya masih dapat terhubung. Mengapa?
Jayapal Chandran
2
Bahkan dengan SO_REUSEADDR, masih mungkin untuk mendapatkan kesalahan "Alamat sudah digunakan". Untuk detail, lihat hea-www.harvard.edu/~fine/Tech/addrinuse.html .
Jingguo Yao
@WMR SO_REUSEADDRtidak "menutup" soket. Ini hanya memungkinkan Anda untuk menggunakan kembali yang sudah dibuka. Jadi pertanyaannya masih, "Bagaimana cara menutup soket secara paksa TIME_WAIT?"
Pacerier
Ini adalah jawaban yang benar, tetapi pertanyaannya tidak sepenuhnya benar. Setidaknya memecahkan masalah saya dengan baik (tidak suka me-restart seluruh jaringan yang memutuskan semua koneksi lain juga).
V-Mark
SO_REUSEADDRakan membiarkan bind()melanjutkan; tetapi jika Anda ingin mendengarkan soket itu, listen()akan mengembalikan EADDRINUSEsemua sama. Dengan kata lain, jawaban ini dapat membantu perangkat lunak klien menggunakan porta sesaat, tetapi tidak memecahkan masalah untuk perangkat lunak server.
Will
33

Sejauh yang saya tahu tidak ada cara untuk secara paksa menutup soket di luar penulisan pengontrol sinyal yang lebih baik ke dalam program Anda, tetapi ada file / proc yang mengontrol berapa lama waktu tunggu habis. File tersebut adalah

/proc/sys/net/ipv4/tcp_tw_recycle

dan Anda dapat mengatur batas waktu ke 1 detik dengan melakukan ini:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

Namun, halaman ini berisi peringatan tentang kemungkinan masalah keandalan saat mengatur variabel ini.

Ada juga file terkait

/proc/sys/net/ipv4/tcp_tw_reuse

yang mengontrol apakah soket TIME_WAIT dapat digunakan kembali (mungkin tanpa batas waktu).

Secara kebetulan, dokumentasi kernel memperingatkan Anda untuk tidak mengubah salah satu dari nilai-nilai ini tanpa 'saran / permintaan ahli teknis'. Bukan aku.

Program harus ditulis untuk mencoba mengikat ke port 49200 dan kemudian bertambah 1 jika port sudah digunakan. Oleh karena itu, jika Anda memiliki kontrol kode sumber, Anda dapat mengubah perilaku ini untuk menunggu beberapa detik dan coba lagi di port yang sama, alih-alih bertambah.

Leigh Caldwell
sumber
pikir dua contoh kedua harus s / rw / tw / saya sunting, tetapi tidak cukup rep.
1
Diambil dari dokumentasi kernel: Perhatian. Baik tcp_tw_recycle dan tcp_tw_reuse dapat menyebabkan masalah. Anda tidak boleh mengaktifkan tanpa memahami topologi jaringan di antara node yang menggunakan atau digunakan oleh node di mana parameter diaktifkan. Koneksi yang melalui node yang mengetahui status koneksi TCP, seperti firewall, NAT atau load balancer dapat mulai menjatuhkan frame karena pengaturan. Masalahnya akan terlihat ketika ada jumlah koneksi yang cukup besar.
Mengaturnya agar 1berfungsi untuk koneksi di masa mendatang, tetapi bagaimana dengan koneksi saat ini yang sudah dibuka?
Pacerier
18

Sebenarnya ada cara untuk mematikan koneksi - killcx . Mereka mengklaim itu berfungsi dalam kondisi koneksi apa pun (yang belum saya verifikasi). Anda perlu mengetahui antarmuka di mana komunikasi terjadi, tampaknya menganggap eth0 secara default.

UPDATE: solusi lain adalah cutter yang datang dalam beberapa repositori distro linux.

akostadinov
sumber
3

Opsi lain adalah menggunakan opsi SO_LINGER dengan batas waktu 0. Dengan cara ini, ketika Anda menutup soket ditutup secara paksa, mengirim RST alih-alih masuk ke perilaku penutupan FIN / ACK. Ini akan menghindari status TIME_WAIT, dan mungkin lebih sesuai untuk beberapa penggunaan.


sumber
2
Itu juga kehilangan data keluar yang masih dalam perjalanan, dan dapat menyebabkan kesalahan di ujung lainnya. Tidak direkomendasikan.
user207421
@ EJP Gagal lebih awal hampir selalu merupakan panggilan yang tepat. Jaringan tidak dapat diandalkan, dan pertempuran yang akan memperlambat segalanya. Aplikasi yang mogok tidak dapat berasumsi bahwa data apa pun berhasil keluar dengan aman.
Tobu
1
Sebenarnya, saya akan merekomendasikan hal ini kapan saja ketika titik akhir lainnya adalah buggy, bus gateway industri tertanam yang mengimplementasikan transport lapisan aplikasi yang andal sendiri melalui TCP, di mana transportasi tersebut mencegah koneksi dari yang pernah ditutup kecuali ia menerima RST dan dengan demikian mengisi batas koneksi pada gateway itu. Sana. Saya memberi Anda contoh yang sangat spesifik dan sangat nyata yang, sayangnya, memerlukan peretasan seperti ini.
andyn
@Tobu Networking tidak dapat diandalkan, tetapi TCP mencoba untuk melakukannya, dan membuat yang lebih buruk tidak berarti membuat sesuatu yang lebih baik, dan membiarkan TCP melakukan tugasnya bukan merupakan 'pertarungan' apa pun.
user207421
2

Solusi alternatif adalah dengan memiliki beberapa proxy yang andal atau perangkat lunak penerusan port yang mendengarkan pada port 49200, kemudian meneruskan koneksi ke salah satu dari beberapa contoh program Anda yang kurang dapat diandalkan menggunakan port yang berbeda ... HAPROXY muncul dalam pikiran.

Kebetulan port yang Anda hubungkan cukup tinggi. Anda bisa mencoba menggunakan yang tidak terpakai tepat di atas rentang 0-1024. Sistem Anda cenderung menggunakan nomor port yang lebih rendah sebagai porta sesaat.

andrew pate
sumber
0

TIME_WAIT adalah masalah paling umum dalam arsitektur server klien pemrograman soket. Tunggu beberapa detik mencoba secara berkala adalah solusi terbaik untuk itu. Untuk aplikasi waktu nyata yang mereka perlukan server harus segera bangun. Ada opsi SO_REUSEADDR untuk mereka.


sumber