Önerim için lütfen son bölümü okuyun: "SO_LINGER zaman aşımı 0 ile ne zaman kullanılmalı?" .
Buna gelmeden önce küçük bir ders:
- Normal TCP sonlandırma
TIME_WAIT
FIN
, ACK
veRST
Normal TCP sonlandırma
Normal TCP sonlandırma sırası şuna benzer (basitleştirilmiş):
İki eşimiz var: A ve B
- Bir çağrı
close()
- Bir gönderir
FIN
B'ye
- Bir gider
FIN_WAIT_1
devlet
- B alır
FIN
- B gönderir
ACK
, A'ya
- B gider
CLOSE_WAIT
devlet
- A alır
ACK
- Bir gider
FIN_WAIT_2
devlet
- B aramaları
close()
- B gönderir
FIN
, A'ya
- B gider
LAST_ACK
devlet
- A alır
FIN
- Bir gönderir
ACK
B'ye
- Bir gider
TIME_WAIT
devlet
- B alır
ACK
- B duruma gider
CLOSED
- yani soket tablolarından kaldırılır
TIME_WAIT
Dolayısıyla, sonlandırmayı başlatan akran - yani close()
ilk arama - sonuçtaTIME_WAIT
.
Neden olduğunu anlamak için TIME_WAIT
Eyaletin dostumuz olduğunu , lütfen Stevens ve diğerleri tarafından yayınlanan "UNIX Ağ Programlama" üçüncü baskısındaki 2.7 bölümünü okuyun (sayfa 43).
Ancak, çok sayıda yuva olması bir sorun olabilir. TIME_WAIT
sonunda yeni bağlantıların kabul edilmesini engelleyebileceğinden, bir sunucudaki durumdaki olabilir.
Bu sorunu çözmek için, SO_LINGER soket seçeneğini çağırmadan önce zaman aşımı 0 ile ayarlamayı öneren birçok kişi gördüm close()
. Ancak bu, TCP bağlantısının bir hata ile sonlandırılmasına neden olduğu için kötü bir çözümdür.
Bunun yerine, uygulama protokolünüzü, bağlantı sonlandırması her zaman istemci tarafından başlatılacak şekilde tasarlayın. Müşteri kalan tüm verileri ne zaman okuduğunu her zaman bilirse, sonlandırma sırasını başlatabilir. Örnek olarak, bir tarayıcı Content-Length
tüm verileri ne zaman okuduğunu HTTP başlığından bilir ve kapatmayı başlatabilir. (HTTP 1.1'de olası bir yeniden kullanım için bir süre açık tutacağını ve ardından kapatacağını biliyorum.)
Sunucunun bağlantıyı kapatması gerekiyorsa, uygulama protokolünü, sunucunun istemciden aramasını isteyeceği şekilde tasarlayın close()
.
SO_LINGER zaman aşımı 0 ile ne zaman kullanılır?
Yine, ayarı "UNIX Ağ Programlama" üçüncü baskı, sayfa 202-203, uygun SO_LINGER
öncesinde çağrılmasına zaman aşımı 0 ile close()
normal bir sonlandırma dizisi neden olur değil başlatılması.
Bunun yerine, bu seçeneği eş ayarlaması ve arama close()
, RST
bir hata durumunu gösteren bir (bağlantı sıfırlama) gönderir ve bu, diğer uçta nasıl algılanacaktır. Genellikle "Eşler tarafından bağlantı sıfırlama" gibi hatalar görürsünüz.
Bu nedenle, normal durumda , bir sunucu uygulamasında SO_LINGER
aramadan önce zaman aşımı 0 ile ayarlamak gerçekten kötü bir fikirdir close()
- bundan böyle iptalli kapatma olarak adlandırılır .
Bununla birlikte, belirli bir durum zaten bunu yapmayı garanti eder:
- Sunucu uygulama yanlış bir istemci (zaman aşımına vb geçersiz veri döndürür) Eğer başarısız bir yakın sıkışmış kalmamak için mantıklı
CLOSE_WAIT
ya kadar biten TIME_WAIT
devlet.
- Şu anda binlerce istemci bağlantısı bulunan sunucu uygulamanızı yeniden başlatmanız gerekiyorsa, bu soket seçeneğini binlerce sunucu soketini (sunucu tarafından
TIME_WAIT
arama yaparken close()
) önlemek için ayarlamayı düşünebilirsiniz çünkü bu, sunucunun yeni istemci bağlantıları için kullanılabilir bağlantı noktalarını almasını engelleyebilir. yeniden başlatıldıktan sonra.
- Yukarıda bahsi geçen kitabın 202. sayfasında özellikle şöyle der: "Bu özelliğin başarısız bir kapanış göndermek için kullanılmasını sağlayan belirli durumlar vardır. Bir örnek,
CLOSE_WAIT
sıkışmış bir terminale veri göndermeye çalışırken sonsuza kadar askıda kalabilen bir RS-232 terminal sunucusudur. bağlantı noktası, ancak RST
bekleyen verileri atması gerekirse sıkışmış bağlantı noktasını düzgün şekilde sıfırlar . "
Sorunuza çok güzel bir cevap verdiğine inandığım bu uzun yazıyı tavsiye ederim .