SO_LINGER (0) TCP seçeneği ne zaman gereklidir?


96

Sanırım seçeneğin biçimsel anlamını anlıyorum. Şu anda kullandığım bazı eski kodlarda seçenek kullanılıyor. Müşteri, yanına yakın bağlantıda FIN'e yanıt olarak RST'den şikayet eder.

Ne zaman kullanılması gerektiğini anlamadığım için güvenle çıkarabileceğimden emin değilim.

Lütfen seçeneğin ne zaman gerekli olacağına dair bir örnek verebilir misiniz?


1
Onu çıkarmalısın. Üretim kodunda kullanılmamalıdır. Kullanıldığını gördüğüm tek zaman geçersiz bir kıyaslama sonucuydu.
Marquis of Lorne

Yanıtlar:


83

SO_LINGERZaman aşımını sıfıra ayarlamanın tipik nedeni TIME_WAIT, bir sunucudaki mevcut tüm kaynakları birbirine bağlayan çok sayıda bağlantıdan kaçınmaktır .

Bir TCP bağlantısı temiz bir şekilde kapatıldığında, kapatmayı ("etkin kapatma") başlatan uç, bağlantı TIME_WAITbirkaç dakika oturarak sona erer . Dolayısıyla, protokolünüz sunucunun bağlantıyı yakın başlattığı ve çok sayıda kısa süreli bağlantı içeriyorsa, bu soruna duyarlı olabilir.

Yine de bu iyi bir fikir değil - TIME_WAITbir nedenle var (eski bağlantılardan gelen başıboş paketlerin yeni bağlantılarla çakışmamasını sağlamak için). Mümkünse, protokolünüzü istemcinin bağlantıyı kapatacağı şekilde yeniden tasarlamak daha iyi bir fikirdir.


4
Tamamen katılıyorum. Birçoğunu başlatan (her X saniyede birkaç bin kısa süreli bağlantı) ve daha büyük ölçeklendirme sorunu olan (bin bağlantı daha fazla) bir izleme uygulaması gördüm. Nedenini bilmiyorum ama aplikatör yanıt vermedi. Birisi OS kaynaklarını hızlı bir şekilde serbest bırakmak için SO_LINGER = true, TIME_WAIT = 0 önerdi ve kısa bir araştırmadan sonra bu çözümü çok iyi sonuçlarla denedik. TIME_WAIT artık bu uygulama için bir sorun değil.
bartosz.r

24
Katılmıyorum. TCP'nin üzerinde oturan bir uygulama düzeyi protokolü, istemcinin bağlantıyı her zaman yakın başlatacağı şekilde tasarlanmalıdır. Bu şekilde, TIME_WAITmüşteriye hiçbir zarar vermeden oturur. "UNIX Ağ Programlama" üçüncü baskı (Stevens ve diğerleri) sayfa 203'te söylendiği gibi hatırlayın: "TIME_WAIT durumu arkadaşınızdır ve bize yardım etmek için vardır. Durumdan kaçınmaya çalışmak yerine, onu anlamalıyız (Bölüm 2.7) . "
MGD

8
Bir istemci her 30 saniyede bir 4000 bağlantı açmak isterse (bu izleme uygulaması bir istemcidir! Çünkü bağlantıyı başlatır)? Evet, uygulamayı yeniden gözden geçirebilir, altyapıya bazı yerel ajanlar ekleyebilir, modeli zorlamak için değiştirebiliriz. Ama zaten böyle bir uygulamamız varsa ve bu uygulama büyürse, twe linger'ı ayarlayarak çalışmasını sağlayabiliriz. Bir parametreyi değiştirirsiniz ve yeni mimariyi uygulamak için bir bütçe ayırmadan birdenbire çalışan bir uygulamanız olur.
bartosz.r

4
@ bartosz.r: Sadece SO_LINGER'ı 0 zaman aşımı ile kullanmanın gerçekten son çare olduğunu söylüyorum. Yine, "UNIX Ağ Programlama" üçüncü baskı (Stevens ve diğerleri), sayfa 203'te, aynı zamanda veri bozulma riski taşıdığınızı da söylüyor. TIME_WAIT'in neden arkadaşınız olduğunu görebileceğiniz RFC 1337'yi okuyun.
mgd

7
@caf Hayır, klasik çözüm, her ağır hizmet tipi TCP API'de görüldüğü gibi bir bağlantı havuzu olacaktır, örneğin HTTP 1.1.
Marquis of Lorne

191

Ö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, ACKveRST

Normal TCP sonlandırma

Normal TCP sonlandırma sırası şuna benzer (basitleştirilmiş):

İki eşimiz var: A ve B

  1. Bir çağrı close()
    • Bir gönderir FIN B'ye
    • Bir gider FIN_WAIT_1devlet
  2. B alır FIN
    • B gönderir ACK , A'ya
    • B gider CLOSE_WAITdevlet
  3. A alır ACK
    • Bir gider FIN_WAIT_2devlet
  4. B aramaları close()
    • B gönderir FIN , A'ya
    • B gider LAST_ACKdevlet
  5. A alır FIN
    • Bir gönderir ACK B'ye
    • Bir gider TIME_WAITdevlet
  6. 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_WAITEyaletin 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-Lengthtü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(), RSTbir 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_LINGERaramadan ö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_WAITya kadar biten TIME_WAITdevlet.
  • Ş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_WAITarama 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_WAITsıkışmış bir terminale veri göndermeye çalışırken sonsuza kadar askıda kalabilen bir RS-232 terminal sunucusudur. bağlantı noktası, ancak RSTbekleyen 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 .


6
TIME_WAITsadece sorun yaratmaya başlamadığında
arkadaştır

2
peki ya bir web sunucusu yazıyorsanız? müşteriye bir kapanış başlatmasını nasıl söylersiniz?
Shaun Neal

2
@ShaunNeal belli ki yok. Ancak iyi yazılmış bir istemci / tarayıcı kapanışı başlatacaktır. İstemci iyi davranmıyorsa, neyse ki soket tanımlayıcılarının ve geçici bağlantı noktalarının tükenmemesini sağlamak için TIME_WAIT suikastimiz var.
mgd

Bu harika bir cevap. Teşekkür ederim!
Juraj Martinka

17

Kalma açıkken zaman aşımı sıfır olduğunda, TCP yığını bağlantıyı kapatmadan önce bekleyen verinin gönderilmesini beklemez. Bu nedenle veriler kaybolabilir, ancak bu şekilde oyalanarak bunu kabul ediyor ve bağlantının zarif bir şekilde kapatılmak yerine hemen sıfırlanmasını istiyorsunuz. Bu, normal FIN yerine bir RST'nin gönderilmesine neden olur.

EJP'ye yorumu için teşekkürler, ayrıntılar için buraya bakın.


1
Bunu anladım. Donanımdan sıfırlamayı kullanmak istediğimizde sorduğum "gerçekçi" bir örnek.
dimba

5
Bir bağlantıyı ne zaman iptal etmek isterseniz; bu nedenle, protokolünüz doğrulamayı geçemezse ve bir müşteriniz size saçma sapan konuşursa, aniden bir RST vb. ile bağlantıyı kesersiniz.
Len Holgate

5
Sıfır gecikme zaman aşımını oyalanmakla karıştırıyorsunuz. Linger off, close () işlevinin engellemediği anlamına gelir. Pozitif bir zaman aşımı ile oyalanmak, zaman aşımına kadar blokların kapatılması () anlamına gelir. Sıfır zaman aşımı ile oyalanmak RST'ye neden olur ve soru bununla ilgili.
Marquis of Lorne

2
Evet, haklısın. Cevabı terminolojimi düzeltecek şekilde ayarlayacağım.
Len Holgate

6

Kodunuzdaki boşluğu güvenli bir şekilde kaldırıp kaldıramayacağınız, uygulamanızın türüne bağlıdır: bu bir "istemci" mi (TCP bağlantılarını açıp aktif olarak kapatarak) veya bir "sunucu" mu (bir TCP açık ve diğer taraf kapanışı başlattıktan sonra kapatır)?

Uygulamanız bir "istemci" tarzına sahipse (önce kapanıyor) VE farklı sunuculara çok sayıda bağlantı başlatıp kapatıyorsanız (örneğin, uygulamanız çok sayıda farklı sunucunun erişilebilirliğini denetleyen bir izleme uygulaması olduğunda) uygulamanız tüm istemci bağlantılarınızın TIME_WAIT durumunda takılı kalması sorunu yaşıyor. Ardından, zaman aşımını varsayılan değerden daha küçük bir değere kısaltarak yine de düzgün bir şekilde kapatmanızı ancak istemci bağlantı kaynaklarını daha erken boşaltmanızı öneririm. 0 FIN ile nazikçe kapanmadığı, ancak RST ile iptal edildiği için zaman aşımını 0 olarak ayarlamazdım.

Uygulamanız bir "istemci" tarzına sahipse ve aynı sunucudan çok büyük miktarda küçük dosya alması gerekiyorsa, dosya başına yeni bir TCP bağlantısı başlatmamalı ve TIME_WAIT içinde çok sayıda istemci bağlantısıyla sonuçlanmamalısınız, ancak bağlantıyı açık tutun ve tüm verileri aynı bağlantı üzerinden alın. Linger seçeneği kaldırılabilir ve kaldırılmalıdır.

Uygulamanız bir "sunucu" ise (eşin kapanmasına tepki olarak yakın saniye), kapatıldığında () bağlantınız nazikçe kapatılır ve TIME_WAIT durumuna girmediğiniz için kaynaklar serbest bırakılır. Linger kullanılmamalıdır. Ancak, sunucu uygulamanızda uzun süre boşta kalan etkin olmayan açık bağlantıları algılayan bir denetim süreci varsa ("uzun" tanımlanacaktır), bu etkin olmayan bağlantıyı kendi tarafınızdan kapatabilirsiniz - bunu bir tür hata işleme olarak görün - başarısız bir kapatma ile. Bu, kalma zaman aşımını 0 olarak ayarlayarak yapılır. Close () istemciye bir RST göndererek ona kızgın olduğunuzu söyler :-)


1

Sunucularda, hatalı çalışan istemcilerin bağlantısını kesmek RSTyerine göndermek isteyebilirsiniz FIN. Atlama O FIN-WAITizledi TIME-WAITdolayısıyla sunucu kaynaklarını tüketen ve gelen önler, reddi hizmet bu tür saldırılara karşı korur sunucuda soket devletler.


1

Maxim'in DOS saldırılarının sunucu kaynaklarını tüketebileceğine dair gözlemini seviyorum. Aynı zamanda gerçekten kötü niyetli bir düşman olmadan da gerçekleşir.

Bazı sunucular, istemci uygulamasında sunucunuza gönderdikleri her yeni komut için yeni bir bağlantı oluşturmaya devam ettikleri bağlantı sızıntısı ile ilgili bir hata olduğunda ortaya çıkan 'kasıtsız DOS saldırısı' ile uğraşmak zorundadır. Ve sonra belki de sonunda GC basıncına ulaşırlarsa bağlantılarını kapatırlar veya belki de bağlantılar sonunda zaman aşımına uğrar.

Diğer bir senaryo, 'tüm istemcilerin aynı TCP adresine sahip olduğu durumdur. Bu durumda, istemci bağlantıları yalnızca bağlantı noktası numaralarına göre ayırt edilebilir (tek bir sunucuya bağlanırlarsa). Ve istemciler herhangi bir nedenle bağlantıları açıp / kapatmaya başlarsa, (istemci adresi + bağlantı noktası, sunucu IP + bağlantı noktası) tuple alanını tüketebilirler.

Bu yüzden, sunuculara TIME_WAIT durumunda çok sayıda soket gördüklerinde Linger-Zero stratejisine geçmeleri tavsiye edilir - istemci davranışını düzeltmese de, etkiyi azaltabilir.


0

Bir sunucudaki dinleme soketi, sokete hemen bağlanmaya erişmek ve bağlantıları henüz tamamlanmamış istemcileri sıfırlamak için 0 zamanı ile linger kullanabilir. TIME_WAIT, yalnızca çok yollu bir ağınız olduğunda ve yanlış sıralı paketlerle sonuçlanabilecek veya başka bir şekilde tek ağ paketi sıralaması / varış zamanlamasıyla uğraştığınızda ilginç olan bir şeydir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.