kapatma soketine karşı kapat?


214

C'de, bir soketi kapatırsak, soketin yok edileceği ve daha sonra tekrar kullanılabileceği anlamına gelir.

Kapanmaya ne dersiniz? Açıklamada, bu sokete çift yönlü bağlantının yarısının kapatıldığı belirtildi. Ancak bu soket closesistem çağrısı gibi yok edilecek mi?


Daha sonra tekrar kullanılamaz. Kapalı. Bitirdi. Bitti.
Lorne Marquis

Yanıtlar:


191

Bu, Beej'in ağ rehberinde açıklanmaktadır . shutdownbir veya her iki yönde iletişimi engellemenin esnek bir yoludur. İkinci parametre olduğunda SHUT_RDWR, hem gönderme hem de alma (gibi close) engeller . Ancak, closebir soketi gerçekten yok etmenin yolu.

Bununla birlikte shutdown, emsalinin zaten gönderdiği bekleyen verileri alabileceksiniz (bunu bildirdiği için Joey Adams'a teşekkürler).


23
Bir TCP soketini kapatsanız bile () yine de hemen yeniden kullanılabilir olmayacaktır, çünkü OS bir TIME_WAIT durumunda olacaktır, ancak işletim sistemi, bu soketi hemen başka bir şey için tekrar kullanacaklardı.
alesplin

91
Bir soket üzerindeki kapatma ve kapatma arasındaki büyük fark, soketin diğer işlemler tarafından paylaşılmasıdır. Shutdown () , soketin tüm kopyalarını etkilerken close () bir işlemde yalnızca dosya tanımlayıcısını etkiler.
Zan Lynx

5
shutdownHer iki yönü de kullanmak isteyebileceğiniz ancak kullanamayacağınız closebir sebep FILE, soketi kullanarak kullanarak bir referans yaptıysanız fdopen. Eğer closesoket, yeni açılan bir dosya aynı fd atanmış olabilir ve daha sonra kullanmak FILEçok kötü olabilir yanlış yeri okur / yazar. Eğer sadece shutdown, sonradan kullanımı FILEsadece denilene kadar hata verecektir fclose.
R .. GitHub BUZA YARDIMCI DURDUR

25
TIME_WAIT ile ilgili yorum yanlış. Bu, yuvalar için değil bağlantı noktaları için geçerlidir. Yuvaları tekrar kullanamazsınız.
Lorne Marquis

48
-1. Hem bu yazı hem de bağlantı kullanmak istemek için önemli bir kavramsal neden shutdownatlamaktadır: EOF'yi eşe işaret etmek ve eşin gönderdiği bekleyen verileri almaya devam etmek.
Joey Adams

144

Mevcut cevapların hiçbiri insanlara TCP protokolü düzeyinde nasıl çalıştığını shutdownve closeçalıştığını söylemediğinden , bunu eklemeye değer.

Standart TCP bağlantısı 4 yönlü sonlandırma ile sonlandırılır:

  1. Bir katılımcı gönderilecek daha fazla veriye sahip olmadığında, diğerine bir FIN paketi gönderir
  2. Diğer taraf FIN için bir ACK döndürür.
  3. Karşı taraf da veri aktarımını bitirdiğinde, başka bir FIN paketi gönderir
  4. İlk katılımcı bir ACK döndürür ve aktarımı sonlandırır.

Ancak, TCP bağlantısını kapatmanın başka bir "acil" yolu vardır:

  1. Bir katılımcı bir RST paketi gönderir ve bağlantıdan çıkar
  2. Diğer taraf bir RST alır ve bağlantıyı da terk eder

Wireshark ile yaptığım testte, varsayılan soket seçenekleriyle, shutdowndiğer uca bir FIN paketi gönderir , ancak hepsi bu. Karşı taraf size FIN paketini gönderene kadar hala veri alabilirsiniz. Bu gerçekleştiğinde, Receive0 boyutta bir sonuç elde edersiniz. Dolayısıyla, "gönder" i ilk kapatan sizseniz, veri almayı bitirdiğinizde soketi kapatmalısınız.

Öte yandan, closebağlantı hala etkinken ararsanız (diğer taraf hala aktiftir ve sistem arabelleğinde gönderilmemiş verileriniz olabilir), diğer tarafa bir RST paketi gönderilir. Bu hatalar için iyidir. Örneğin, karşı tarafın yanlış veri sağladığını veya veri sağlamayı reddettiğini (DOS saldırısı?) Düşünüyorsanız, soketi hemen kapatabilirsiniz.

Kurallar hakkındaki görüşüm:

  1. Mümkün olduğunca shutdownönce düşününclose
  2. Kapatmaya karar vermeden önce (0 boyutlu veri alındı) almayı bitirdiyseniz, son gönderim (varsa) bittikten sonra bağlantıyı kapatın.
  3. Bağlantıyı normal olarak kapatmak istiyorsanız, bağlantıyı kapatın (SHUT_WR ile ve bu noktadan sonra SHUT_RD ile veri almayı umursamıyorsanız) ve 0 boyutlu veri alana kadar bekleyin ve ardından priz.
  4. Her durumda, başka bir hata oluşursa (örneğin zaman aşımı), soketi kapatmanız yeterlidir.

SHUT_RD ve SHUT_WR için ideal uygulamalar

Aşağıdakiler test edilmemiştir, kendi sorumluluğunuzdadır. Ancak, bunun bir şeyler yapmanın makul ve pratik bir yolu olduğuna inanıyorum.

TCP yığını yalnızca SHUT_RD ile kapatma alırsa, bu bağlantı daha fazla veri beklenmiyor olarak işaretlenir. Bekleyen ve sonraki readistekler (hangi iş parçacığında olursa olsunlar) daha sonra sıfır boyutlu sonuç döndürür. Ancak, bağlantı hala etkin ve kullanılabilir - örneğin, yine de OOB verilerini alabilirsiniz. Ayrıca, işletim sistemi bu bağlantı için aldığı verileri bırakacaktır. Ama hepsi bu, diğer tarafa hiçbir paket gönderilmeyecek.

TCP yığını yalnızca SHUT_WR ile kapatma alırsa, daha fazla veri gönderilemediğinden bu bağlantı işaretlenecektir. Bekleyen tüm yazma istekleri tamamlanacak, ancak sonraki yazma istekleri başarısız olacaktır. Ayrıca, gönderilecek daha fazla veriye sahip olmadığımızı bildirmek için başka bir tarafa bir FIN paketi gönderilecektir.


2
"eğer bağlantı hala canlıyken kapatırsanız" totolojiktir ve bir RST'nin gönderilmesine neden olmaz. (1) gerekli değildir. (4) 'te zaman aşımlarının mutlaka bağlantı için ölümcül olması gerekmez ve değişmez bir şekilde bağlantıyı kapatabileceğinizi göstermez.
Lorne Markiz

4
@EJP Hayır, totolojik değil. shutdown()Bağlantıyı kurabilirsiniz ve artık canlı değildir. Hala dosya tanımlayıcıya sahipsiniz. Yine recv()de alıcı tampondan yapabilirsiniz. Ve yine close()de dosya tanımlayıcıyı atmak için çağırmanız gerekir .
Pavel Šimerda

Bu, tel formatıyla ilgili en iyi cevaptır. SHUT_RD ile kapatma hakkında daha fazla bilgi almak istiyorum. Daha fazla veri beklemediğiniz için TCP sinyali yok, değil mi? Daha fazla veri göndermediğini bildirmek için yalnızca FIN yok mu?
Pavel Šimerda

3
@ PavelŠimerda Evet TCP daha fazla veri beklemediği için sinyal vermiyor. Bu, üst düzey protokollerde dikkate alınmalıdır. Benim düşünceme göre, bu genellikle gerekli değildir. Kapınızı kapatabilirsiniz, ancak kapının önüne hediyeler koyarak insanları durduramazsınız. Bu onların KARARI, senin değil.
Dünya Motoru

1
@EJP Gerçek bir tartışmanız var mı? Aksi takdirde ilgilenmiyorum.
Pavel Šimerda

35

Bunun yerine close(), bununla birlikte bazı sınırlamalar vardır shutdown().

close()TCP bağlantısındaki her iki yönü de sonlandıracaktır. Bazen diğer uç noktaya veri göndermeyi bitirdiğinizi, ancak yine de veri almak istediğinizi söylemek istersiniz.

close()tanımlayıcı başvuru sayısını azaltır (dosya tablosu girişinde korunur ve şu anda açık olan bir dosya / sokete başvuran tanımlayıcı sayısını sayar) ve tanımlayıcı 0 değilse soketi / dosyayı kapatmaz. temizleme yalnızca referans sayısı 0'a düştükten sonra gerçekleşir. shutdown()Biri ile referans sayısını yoksayarak normal TCP kapatma dizisi başlatılabilir.

Parametreler aşağıdaki gibidir:

int shutdown(int s, int how); // s is socket descriptor

int how olabilir:

SHUT_RDveya 0 Diğer alacaklara izin verilmiyor

SHUT_WRveya 1 Diğer gönderimlere izin verilmiyor

SHUT_RDWRveya 2 Diğer gönderme ve alma işlemlerine izin verilmiyor


8
İki işlev tamamen farklı amaçlar içindir. Bir soket üzerindeki son kapatmanın, henüz başlatılmamışsa bir kapatma dizisi başlatması, kapatmanın soket veri yapılarını temizlemek için olduğu anlamına gelir ve kapatma, tcp seviyesi kapatma dizisini başlatmak içindir.
Len Holgate

25
'Bunun yerine kapatmayı kullanamazsınız'. Siz de kullanabilirsiniz . ancak soketi bir süre kapatmalısınız.
Lorne Marquis,

15

Bu platforma özgü olabilir, bir şekilde şüpheliyim, ama yine de, gördüğüm en iyi açıklama burada kapatma, oyalama seçenekleri, soket kapatma ve genel bağlantı sonlandırma dizileri hakkında açıkladıkları bu msdn sayfasında .

Özetle, TCP düzeyinde bir kapatma dizisi göndermek için shutdown komutunu kullanın ve işleminizdeki soket veri yapıları tarafından kullanılan kaynakları boşaltmak için close tuşunu kullanın. Close aradığınız zamana kadar kesin bir kapatma dizisi vermediyseniz, sizin için bir kapatma dizisi başlatılır.


9

Ayrıca, shutdown()şu anda engellenen başka bir pthread'i connect()erken iptal etmek için bir pthread'den linux altında başarılı oldum .

Diğer işletim sistemleri (en azından OSX) altında, arama başarısız close()olmak için yeterli bulundu connect().


7

"shutdown () aslında dosya tanımlayıcısını kapatmaz; yalnızca kullanılabilirliğini değiştirir. Bir yuva tanımlayıcıyı serbest bırakmak için close () kullanmanız gerekir." 1


3

Kapat

Bir soket kullanmayı bitirdiğinizde, dosya tanımlayıcısını close ile kapatabilirsiniz; Bağlantı üzerinden iletilmeyi bekleyen bir veri varsa, normalde close bu iletimi tamamlamaya çalışır. Bir zaman aşımı süresi belirtmek için SO_LINGER soket seçeneğini kullanarak bu davranışı denetleyebilirsiniz; bkz. Soket Seçenekleri.

Kapat

Ayrıca, kapatmayı çağırarak yalnızca bir bağlantıdaki alımı veya iletimi kapatabilirsiniz.

Kapatma fonksiyonu soket bağlantısını kapatır. Hangi eylemin gerçekleştirileceğini nasıl belirlediği argümanı: 0 Bu soket için veri almayı durdur. Daha fazla veri gelirse, reddedin. 1 Bu soketten veri aktarmayı deneyin. Gönderilmeyi bekleyen verileri atın. Zaten gönderilen verilerin onayını aramayı bırakın; kaybolursa tekrar iletmeyin. 2 Hem alımı hem de iletimi durdurun.

Dönüş değeri başarıda 0, hatada -1'dir.


2

testimde.

close soket diğer süreçlerle paylaşılmadığında hemen fin paketi gönderecek ve fd'yi hemen yok edecektir

shutdown SHUT_RD , işlem yine de yuvadan veri alabilir, ancak recvTCP arabelleği boşsa 0 döndürür. Eş daha fazla veri gönderdikten sonra, recvverileri tekrar döndürür.

shutdown SHUT_WR , Diğer gönderimlere izin verilmediğini belirtmek için fin paketi gönderir. eş verileri geri gönderebilir ancak TCP arabelleği boşsa 0 değerini geri gönderir

shutdown SHUT_RDWR (her ikisini de kullanmak eşit SHUT_RD ve SHUT_WR akran gönderme daha fazla veri varsa ilk paket gönderecektir).


Sadece denedim ve close()Linux yerine FIN yerine RST gönderdim.
Pavel Šimerda

Ayrıca, okuma tarafını kapattıktan sonra programın daha fazla veri alacağını gerçekten belirtmek istediniz mi?
Pavel Šimerda

@ PavelŠimerda RST veya FIN göndermek tcp durumuna bağlı olabilir düşünüyorum? Emin değilim.
16:50

1. 'Akran daha fazla veri gönderdikten sonra, recv()tekrar veri döndürür' doğru değil. 2. Eş daha sonra daha fazla veri gönderirse davranış SHUT_RDplatforma bağlıdır.
Lorne Marquis

@EJP Kendimi denedim, üzgünüm bu testi hangi platformda yaptığımı unuttum, centos veya Mac. ve ben de bunu aldım
simpx
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.