Windows Sockets API programı kullanarak tcp sunucusuna veri kopyaladığınızda yavaş performans oluşur

Bu makalede, Windows YuvaLARı API'sini kullanarak tcp sunucusuna veri kopyaladığınızda yavaş performansın oluşması sorunu için geçici çözümler sağlanır.

Şunlar için geçerlidir: Windows Server 2012 R2, Windows 10 - tüm sürümler
Özgün KB numarası: 823764

Belirtiler

Windows Yuvaları API'sini kullanan bir program çalıştırdığınızda, tcp sunucusuna veri kopyalarken yavaş performansla karşılaşabilirsiniz.

Microsoft Ağ İzleyicisi gibi bir ağ algılayıcısı ile bir ağ izlemesi yaparsanız, TCP sunucusu gecikmeli bildirim zamanlayıcısında (gecikmeli ACK zamanlayıcı olarak da bilinir) tcp veri akışındaki son TCP kesimine bir TCP ACK kesimi gönderir. Varsayılan olarak, Windows işletim sistemleri için bu zamanlayıcının değeri 200 milisaniyedir (ms). 64 kilobayt (KB) veri göndermek için tipik bir veri akışı aşağıdaki diziye benzer:

İstemci-Sunucu> 1460 bayt
İstemci-Sunucu> 1460 bayt
Sunucu İstemcisi> ACK
İstemci-Sunucu> 1460 bayt
İstemci-Sunucu> 1460 bayt
Sunucu İstemcisi> ACK
....
İstemci-Sunucu> 1460 bayt
İstemci-Sunucu> 1460 bayt
Sunucu İstemcisi> ACK-PUSH
İstemci-Sunucu> 1296 bayt
-> gecikmeli ACK 200 ms

Neden

Bu sorun, Windows YuvaLARı API'sinin ve afd.sys mimari davranışı nedeniyle oluşur. Aşağıdaki koşulların tümü doğruysa bu sorun oluşur:

  • Windows Yuvaları programı engelleyici olmayan yuvalar kullanır.

  • Tek bir gönderme çağrısı veya WSASend çağrısı, temel alınan yuva gönderme arabelleğinin tamamını doldurur.

    Örneğin, program, yuva başlatma yordamları setsockopt sırasında varsayılan yuva gönderme arabelleğinin 32 KB olarak değiştirilmesi için Windows Yuvaları işlevini kullanır:

    setsockopt( sock, SOL_SOCKET, 32768, (char *) &val, sizeof( int ));
    

    Daha sonra program veri gönderdiğinde bir gönderme çağrısı veya WSASend çağrısı gönderir ve her gönderme sırasında 64 KB veri gönderir:

    send(socket, pWrBuffer, 65536, 0);
    

    Bu senaryoda, program 64 KB veri gönderme çağrısını her verdiğinde, temel alınan 32 KB yuva arabelleği doldurulmuşsa program bir SOCKET_ERROR hata kodu döndürür. WSAGetLastError işlevini çağırdıktan sonra program WSAEWOULDBLOCK hata kodunu alır. Çoğu program yuvanın durumunu denetlemek için Windows Yuvaları seçme işlevini kullanır. Bu senaryoda select işlevi, istemci bekleyen TCP ACK kesimini alıncaya kadar yuvayı yazılabilir olarak raporlamaz. Varsayılan olarak bir Windows ortamında gecikmeli bildirim algoritması nedeniyle bu işlem 200 ms kadar sürebilir.

  • Uzak TCP sunucusu, istemci son TCP kesimini göndermeden önce tüm TCP kesimlerini kabul eder.

Geçici Çözüm

Bu sorunu geçici olarak çözmek için aşağıdaki yöntemlerden birini kullanın.

Yöntem 1: Engelleme yuvalarını kullanma

Bu sorun yalnızca engelleyici olmayan yuvalarda oluşur. Engelleyen yuva kullandığınızda, afd.sys yuva arabelleği farklı işlediğinden bu sorun oluşmaz. Engelleme ve engellemeyen yuva programlama hakkında daha fazla bilgi için Microsoft Platform SDK belgelerine bakın.

Yöntem 2: Yuva gönderme arabellek boyutunu program gönderme arabellek boyutundan daha büyük hale getirin

Yuva gönderme arabelleğinde değişiklik yapmak için, geçerli yuva gönderme arabellek boyutunu (SO_SNDBUF) belirlemek için Windows Yuvaları getsockopt işlevini kullanın ve ardından yuva gönderme arabellek boyutunu ayarlamak için işlevini kullanın setsockopt . İşiniz bittiğinde, SO_SNDBUF değeri program gönderme arabellek boyutundan en az 1 bayt büyük olmalıdır.

Gönderme çağrısını veya WSASend çağrısını değiştirerek SO_SNDBUF değerinden en az 1 bayt daha küçük bir arabellek boyutu belirtin. Bu makalenin "Neden" bölümündeki önceki örnekte setsockopt çağrısını aşağıdaki değere değiştirebilirsiniz.

setsockopt( sock, SOL_SOCKET, 65537, (char *) &val, sizeof( int ));

veya aşağıdaki değere gönderme çağrısını değiştirebilirsiniz:

send(socket, pWrBuffer, 32767, 0);

Bu değerlerin herhangi bir bileşimini de kullanabilirsiniz.

Yöntem 3: TCP sunucusundaki TCP/IP ayarlarını değiştirme

Önemli

Bu bölüm, yöntem veya görev, kayıt defterini nasıl değiştireceğinizin anlatıldığı adımları içermektedir. Ancak kayıt defterini hatalı biçimde değiştirirseniz önemli sorunlar oluşabilir. Bu nedenle bu adımları dikkatle uyguladığınızdan emin olun. Ek koruma için kayıt defterini değiştirmeden önce yedeklemeyi unutmayın. Böylece, bir sorun oluşursa kayıt defterini daha sonra geri yükleyebilirsiniz. Kayıt defterini yedekleme ve geri yükleme hakkında daha fazla bilgi için Microsoft Bilgi Bankası'ndaki makaleyi görüntülemek üzere aşağıdaki makale numarasına tıklayın:
322756 Windows'da kayıt defterini yedekleme ve geri yükleme

Gelen TCP kesimlerini hemen onaylamak için TCP sunucusundaki TCP/IP ayarlarını değiştirin. Bu geçici çözüm, büyük bir istemci yükleme tabanına sahip olan ve programın davranışını değiştiremeyeceğiniz bir ortamda en iyi şekilde çalışır. Uzak TCP sunucusunun Windows tabanlı bir sunucuda çalıştığı senaryolar için uzak sunucunun kayıt defterini değiştirmeniz gerekir. Diğer işletim sistemleri için gecikmeli bildirim zamanlayıcısını değiştirme hakkında bilgi için işletim sisteminin belgelerine bakın.

Windows 2000 çalıştıran bir sunucuda şu adımları izleyin:

  1. Kayıt Defteri Düzenleyici (Regedit.exe) başlatın.
  2. Şu kayıt defteri alt anahtarını bulup tıklayın: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. Düzenle menüsünde Değer Ekle'ye tıklayın ve aşağıdaki kayıt defteri değerini oluşturun:
    Değer adı: TcpDelAckTicks
    Veri türü: REG_DWORD
    Değer verileri: 0
  4. Kayıt Defteri Düzenleyicisi'nden çıkın.
  5. Bu değişikliğin etkili olması için Windows'ı yeniden başlatın.

Windows XP veya Windows Server 2003 çalıştıran bir sunucuda şu adımları izleyin:

  1. Kayıt Defteri Düzenleyicisi'ni başlatın.
  2. Şu kayıt defteri alt anahtarını bulup tıklayın: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. Düzen menüsünde Yeni'nin üzerine gelin ve DWORD Değeri'ne tıklayın.
  4. Yeni değeri TcpAckFrequency olarak adlandırın ve 1 değerini atayın.
  5. Kayıt Defteri Düzenleyicisi'nden çıkın.
  6. Bu değişikliğin etkili olması için Windows'ı yeniden başlatın.

Yöntem 4: Engelleyici olmayan yuvalar için afd.sys arabelleğe alma davranışını değiştirme

Önemli

Bu bölüm, yöntem veya görev, kayıt defterini nasıl değiştireceğinizin anlatıldığı adımları içermektedir. Ancak kayıt defterini hatalı biçimde değiştirirseniz önemli sorunlar oluşabilir. Bu nedenle bu adımları dikkatle uyguladığınızdan emin olun. Ek koruma için kayıt defterini değiştirmeden önce yedeklemeyi unutmayın. Böylece, bir sorun oluşursa kayıt defterini daha sonra geri yükleyebilirsiniz. Kayıt defterini yedekleme ve geri yükleme hakkında daha fazla bilgi için, Microsoft Bilgi Bankası'ndaki makaleyi görüntülemek üzere aşağıdaki makale numarasını tıklatın: 322756 Windows'da kayıt defterini yedekleme ve geri yükleme

Not

Bu kayıt defteri anahtarı yalnızca Windows Server 2003 Service Pack 1 ve sonraki hizmet paketleri için kullanılabilir.

  1. Başlat'a tıklayın, regedit.exeyazın ve tamam'a tıklayın.
  2. Aşağıdaki kayıt defteri alt anahtarını bulup tıklayın:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
  3. Düzen menüsünde Yeni'nin üzerine gelin ve DWORD Değeri'ne tıklayın.
  4. Yeni değeri NonBlockingSendSpecialBuffering olarak adlandırın ve 1 değerini atayın.
  5. Kayıt Defteri Düzenleyici'dan çıkın.
  6. Bu değişikliğin etkili olması için Windows'ı yeniden başlatın.

Durum

Microsoft bu sorunun "Uygulandığı öğe" bölümünde listelenen Microsoft ürünlerinde bulunduğunu onaylamıştır.

Başvurular

328890 Windows XP ve Windows Server 2003'te TCP Bildirimi (ACK) davranışını denetlemek için yeni kayıt defteri girdisi