Tasarım sorunları - Winsock ile TCP üzerinden küçük veri kesimleri gönderme

TCP üzerinden küçük veri paketleri göndermeniz gerektiğinde, Winsock uygulamanızın tasarımı özellikle kritik önem taşır. Gecikmeli bildirim, Nagle algoritması ve Winsock arabelleği oluşturma etkileşimini dikkate almayan bir tasarım performansı önemli ölçüde etkileyebilir. Bu makalede, birkaç örnek olay incelemesini kullanarak bu sorunlar ele alınmaktadır. Ayrıca, winsock uygulamasından verimli bir şekilde küçük veri paketleri göndermeye yönelik bir dizi öneri de türetir.

Orijinal ürün sürümü: Winsock
Özgün KB numarası: 214397

Arka plan

Bir Microsoft TCP yığını bir veri paketi aldığında, 200 ms gecikmeli zamanlayıcı kapanır. Bir ACK gönderildiğinde gecikme süreölçeri sıfırlanır ve sonraki veri paketi alındığında 200 ms'lik bir gecikme daha başlatılır. Hem İnternet hem de intranet uygulamalarında verimliliği artırmak için TCP yığını, alınan veri paketlerinde ne zaman bir ACK gönderilmeye karar vermek için aşağıdaki ölçütleri kullanır:

  • Gecikme süreölçerinin süresi dolmadan önce ikinci veri paketi alınırsa, ACK gönderilir.
  • İkinci veri paketi alınmadan önce ACK ile aynı yönde gönderilecek veriler varsa ve gecikme süreölçerinin süresi dolarsa, ACK veri kesimiyle birlikte geri alınır ve hemen gönderilir.
  • Gecikme süreölçerinin süresi dolduğunda ACK gönderilir.

Tcp yığını, küçük veri paketlerinin ağı tıkamasını önlemek için, nagle algoritmasını varsayılan olarak etkinleştirir. Bu algoritma, birden çok gönderme çağrısından küçük bir veri arabelleği oluşturur ve gönderilen önceki veri paketi için bir ACK uzak ana bilgisayardan alınana kadar göndermeyi geciktirir. Nagle algoritmasının iki özel durumu şunlardır:

  • Yığın, Maksimum İletim Birimi'nden (MTU) daha büyük bir veri arabelleğiyle birleştirilmişse, uzak konaktan ACK beklenmeden hemen tam boyutlu bir paket gönderilir. Ethernet ağında TCP/IP için MTU 1460 bayttır.

  • Yuva TCP_NODELAY seçeneği, nagle algoritmasını devre dışı bırakmak için uygulanır, böylece küçük veri paketleri gecikme olmadan uzak ana bilgisayara teslim edilir.

Uygulama katmanındaki performansı iyileştirmek için Winsock, uygulamadan gelen veri arabelleklerini winsock çekirdek arabelleğine çağrı göndererek kopyalar. Ardından yığın, paketin kabloya ne zaman yerleştirileceğini belirlemek için kendi buluşsal özelliklerini (Nagle algoritması gibi) kullanır. Seçeneğini kullanarak SO_SNDBUF yuvaya ayrılan Winsock çekirdek arabelleği miktarını değiştirebilirsiniz (varsayılan olarak 8K'dır). Gerekirse, Winsock arabellek boyutundan SO_SNDBUF daha fazla arabelleğe alabilir. Çoğu durumda, uygulamadaki gönderme işleminin tamamlanması yalnızca bir uygulama gönderme çağrısındaki veri arabelleğinin Winsock çekirdek arabelleğine kopyalandığını gösterir ve verilerin ağ ortamına isabet ettiğini göstermez. Tek özel durum, Winsock arabelleğinin 0 olarak ayarlanmasını SO_SNDBUF devre dışı bırakmanızdır.

Winsock, uygulamaya gönderme işleminin tamamlanmasını göstermek için aşağıdaki kuralları kullanır (göndermenin nasıl çağrıldığına bağlı olarak, tamamlama bildirimi bir engelleme çağrısından dönen, bir olaya işaret eden veya bildirim işlevi çağıran işlev olabilir vb.):

  • Yuva hala SO_SNDBUF kota içindeyse, Winsock verileri uygulama gönderme işleminden kopyalar ve uygulamaya gönderme işleminin tamamlanmasını gösterir.

  • Yuva kotanın dışındaysa SO_SNDBUF ve yığın çekirdeği arabelleğinde daha önce arabelleğe alınan yalnızca bir gönderme işlemi varsa, Winsock verileri uygulama gönderme işleminden kopyalar ve uygulamaya gönderme işleminin tamamlanmasını gösterir.

  • Yuva kotanın dışındaysa SO_SNDBUF ve yığın çekirdeği arabelleğinde daha önce arabelleğe alınan birden fazla gönderme varsa, Winsock verileri uygulama göndermeden kopyalar. Winsock, yığın yuvayı SO_SNDBUF kotaya veya yalnızca bir bekleyen gönderme koşuluna geri koyacak kadar gönderene kadar uygulamaya gönderme işleminin tamamlanmasını göstermez.

Örnek olay incelemesi 1

Winsock TCP istemcisinin veritabanında depolamak için winsock TCP sunucusuna 10000 kayıt göndermesi gerekir. Kayıtların boyutu 20 bayt ile 100 bayt arasında değişir. Uygulama mantığını basitleştirmek için tasarım aşağıdaki gibidir:

  • İstemci yalnızca göndermeyi engelliyor. Sunucu yalnızca engelleme recv yapar.
  • İstemci yuvası, her kaydın SO_SNDBUF tek bir veri kesiminde dışarı gitmesi için öğesini 0 olarak ayarlar.
  • Sunucu bir döngü içinde çağırır recv . her kaydın tek bir recv çağrıda alınabilmesi için içinde recv gönderilen arabellek 200 bayttır.

Performans

Test sırasında geliştirici istemcinin sunucuya saniyede yalnızca beş kayıt gönderebileceğini bulur. En fazla 976 kb veri (10000 * 100 / 1024) olan toplam 10000 kaydın sunucuya gönderilmesi yarım saatten fazla sürer.

Analysis

İstemci seçeneği ayarlamadığından TCP_NODELAY , Nagle algoritması TCP yığınını bir ACK'nin kabloya başka bir paket gönderebilmesi için beklemeye zorlar. Ancak istemci, seçeneği 0 olarak ayarlayarak SO_SNDBUF Winsock arabelleği almayı devre dışı bırakmıştır. Bu nedenle, 10000 gönderme çağrısının tek tek gönderilmesi ve ACK'ların tek tek gönderilmesi gerekir. Sunucunun TCP yığınında aşağıdakiler oluştuğundan her ACK 200 ms gecikir:

  • Sunucu bir paket aldığında, 200 ms gecikme süreölçeri kapanır.
  • Sunucunun geri bir şey göndermesi gerekmez, bu nedenle ACK geri alınamaz.
  • Önceki paket kabul edilmediği sürece istemci başka bir paket göndermez.
  • Sunucudaki gecikme süreölçeri sona erer ve ACK geri gönderilir.

Nasıl geliştirebilirsiniz?

Bu tasarımda iki sorun vardır. İlk olarak, gecikme süreölçeri sorunu vardır. İstemcinin 200 ms içinde sunucuya iki paket gönderebilmesi gerekir. İstemci varsayılan olarak Nagle algoritmasını kullandığından, yalnızca varsayılan Winsock arabelleği kullanmalı ve 0 olarak ayarlanmamalıdır SO_SNDBUF . TCP yığını En Fazla İletim Birimi'nden (MTU) daha büyük bir arabelleği birleştirdikten sonra, uzak konaktan ACK beklenmeden hemen tam boyutlu bir paket gönderilir.

İkinci olarak, bu tasarım bu kadar küçük boyutlu her kayıt için bir gönderme çağrısında bulunur. Bunu küçük bir boyutta göndermek verimli değildir. Bu durumda, geliştirici her kaydı 100 bayta doldurmayı ve bir istemci gönderme çağrısından bir kerede 80 kayıt göndermek isteyebilir. Sunucuya toplam kaç kaydın gönderileceğini bildirmek için istemci, takip edilecek kayıt sayısını içeren düzeltme boyutundaki bir üst bilgiyle iletişimi başlatmak isteyebilir.

Örnek olay incelemesi 2

Winsock TCP istemci uygulaması, hisse senedi teklifi hizmeti sağlayan bir Winsock TCP sunucu uygulamasıyla iki bağlantı açar. İlk bağlantı, hisse senedi simgesini sunucuya göndermek için bir komut kanalı olarak kullanılır. İkinci bağlantı, hisse senedi teklifini almak için veri kanalı olarak kullanılır. İki bağlantı kurulduktan sonra istemci, komut kanalı aracılığıyla sunucuya bir hisse senedi simgesi gönderir ve hisse senedi teklifinin veri kanalından geri gelmesini bekler. Sonraki hisse senedi simgesi isteğini sunucuya yalnızca ilk hisse senedi teklifi alındıktan sonra gönderir. İstemci ve sunucu ve TCP_NODELAY seçeneğini ayarlamazSO_SNDBUF.

  • Performans

    Test sırasında geliştirici, istemcinin saniyede yalnızca beş teklif alabileceğini bulur.

  • Analysis

    Bu tasarım aynı anda yalnızca bir bekleyen hisse senedi teklifi isteğine izin verir. İlk hisse senedi simgesi, komut kanalı (bağlantı) aracılığıyla sunucuya gönderilir ve bir yanıt hemen veri kanalı (bağlantı) üzerinden sunucudan istemciye geri gönderilir. Ardından, istemci hemen ikinci hisse senedi simgesi isteğini gönderir ve gönderme çağrısındaki istek arabelleği Winsock çekirdek arabelleğine kopyalandığında gönderme hemen döndürür. Ancak istemci TCP yığını, ilk komut kanalı üzerinden gönderme henüz onaylanmadığından, isteği çekirdek arabelleğinden hemen gönderemiyor. Sunucu komut kanalındaki 200 ms gecikme süreölçerinin süresi dolduktan sonra, ilk sembol isteğinin ACK'sı istemciye geri döner. Ardından, ikinci teklif isteği 200 ms gecikdikten sonra sunucuya başarıyla gönderilir. İkinci hisse senedi simgesinin teklifi, şu anda istemci veri kanalındaki gecikme süreölçerinin süresi dolduğundan veri kanalı üzerinden hemen geri gelir. Önceki teklif yanıtı için bir ACK sunucu tarafından alınır. (İstemcinin 200 ms için ikinci bir hisse senedi teklifi isteği gönderemediğini ve bu nedenle istemcideki gecikme süreölçerinin süresinin dolmasına ve sunucuya bir ACK göndermesine zaman verdiğini unutmayın.) Sonuç olarak, istemci ikinci teklif yanıtını alır ve aynı döngüye tabi olan başka bir teklif isteği verebilir.

  • Nasıl geliştirebilirsiniz?

    burada iki bağlantı (kanal) tasarımı gereksizdir. Hisse senedi teklifi isteği ve yanıtı için yalnızca bir bağlantı kullanırsanız, teklif isteğinin ACK'sı teklif yanıtını temel alabilir ve hemen geri gelebilir. Performansı daha da geliştirmek için istemci birden çok hisse senedi teklifi isteğini sunucuya yapılan tek bir gönderme çağrısına katlayabilir ve sunucu da birden çok teklif yanıtını istemciye gönderilen tek bir çağrıya katlayabilir . İki tek yönlü kanal tasarımı herhangi bir nedenle gerekliyse, her iki taraf da küçük paketlerin TCP_NODELAY önceki paket için bir ACK beklemeye gerek kalmadan hemen gönderilebilmesi için bu seçeneği ayarlamalıdır.

Öneriler

Bu iki örnek olay incelemesi hazırlanmış olsa da, bazı en kötü durum senaryolarını göstermeye yardımcı olur. Kapsamlı recvsküçük veri kesiminin ve göndermelerini içeren bir uygulama tasarlarken aşağıdaki yönergeleri dikkate almanız gerekir:

  • Veri kesimleri zaman açısından kritik değilse, uygulama bir gönderme çağrısına geçirmek için bunları daha büyük bir veri bloğuna birleştirmelidir. Gönderme arabelleği winsock çekirdek arabelleğine kopyalanma olasılığı yüksek olduğundan, arabellek çok büyük olmamalıdır. 8K'dan biraz daha az etkili. Winsock çekirdeği MTU'dan daha büyük bir blok aldığı sürece, geriye ne kaldıysa birden çok tam boyutlu paket ve son paket gönderir. Son paket dışında gönderen tarafa 200 ms gecikme süreölçeri isabet etmeyecektir. Tek numaralı bir paket olması durumunda son paket yine de gecikmeli bildirim algoritmasına tabidir. Gönderen uç yığını MTU'dan daha büyük bir blok alırsa Nagle algoritmasını atlayabilir.

  • Mümkünse, tek yönlü veri akışıyla yuva bağlantılarından kaçının. Tek yönlü yuvalar üzerindeki iletişimler Nagle ve gecikmeli bildirim algoritmalarından daha kolay etkilenir. İletişim bir isteği ve yanıt akışını takip ederse, ACK'nin yanıtta piggyback yapılabilmesi için her ikisini de recvs yapmak için tek bir yuva kullanmanız gerekir.

  • Tüm küçük veri kesimlerinin hemen gönderilmesi gerekiyorsa, gönderen uçta seçeneği ayarlayın TCP_NODELAY .

  • Gönderme işleminin Winsock tarafından belirtildiğinde kabloda bir paketin gönderileceğini garanti etmek istemiyorsanız, değerini sıfır olarak ayarlamamalısınız SO_SNDBUF . Aslında, varsayılan 8K arabelleğinin çoğu durumda iyi çalışacağı buluşsal olarak belirlenmiştir ve yeni Winsock arabellek ayarınızın size varsayılandan daha iyi performans sağladığını test etmediğiniz sürece bunu değiştirmemelisiniz. Ayrıca, sıfır olarak ayarlanması SO_SNDBUF çoğunlukla toplu veri aktarımı yapılan uygulamalar için yararlıdır. Bu durumda bile, maksimum verimlilik için çift arabelleğe alma (herhangi bir zamanda birden fazla bekleyen gönderme) ve çakışan G/Ç ile birlikte kullanmanız gerekir.

  • Veri tesliminin garanti edilmesi gerekiyorsa UDP kullanın.

Başvurular

Gecikmeli Bildirim ve Nagle algoritması hakkında daha fazla bilgi için aşağıdakilere bakın:

Braden, R.[1989], RFC 1122, internet konakları için gereksinimler--İletişim Katmanları, İnternet Mühendisliği Görev Gücü.