2010-10-17 13 views
12

modelini kullanırken yığın taşması C# ve .NET'in System.Net.Sockets.Socket.AcceptAsync yöntemiyle ilgili olarak, hemen kullanılabilir olan SocketAsyncEventArgs durumunu işlemek için "false" değerinin geri getirilmesi gerekir. Eşzamanlı işlenmiş bağlantıdan. Microsoft (System.Net.Sockets.SocketAsyncEventArgs sınıf sayfasında bulunan), taşıma modellerini uygulayan herhangi bir sistemde kullanılabilecek çok sayıda bekleyen bağlantı varsa yığın taşmasına neden olacak örnekler sağlar.System.Net.Sockets.Socket.AcceptAsync modeli

Bu soruna geçici bir çözüm bulmak için diğer fikirler, Socket.AcceptAsync döndürme değerinin false değerine eşit olması koşuluyla ve (ertelenmiş işleme izin vermek için) değeri bozarsa, işleyici yöntemini çağıran bir döngü oluşturmaktır. işlemin eşzamansız olarak tamamlandığını gösterir (true). Bununla birlikte, bu çözüm aynı zamanda SocketAsyncEventArgs ile ilişkili geri dönüşün Socket.AcceptAsync'a aktarılan geridönüşümün, yöntemin sonunda, Socket.AcceptAsync numaralı aramaya da sahip olduğu ve aynı zamanda anında kullanılabilir, eşzamanlı olarak kabul edilen bağlantılar için bir döngüye sahip olması nedeniyle yığın taşması güvenlik açığına neden olur .

Gördüğünüz gibi, bu oldukça sağlam bir sorundur ve henüz System.Threading.ThreadPool'u içermeyen ve tonlarca başka yöntem ve zamanlama işlemi gerçekleştirmeyen iyi bir çözüm bulmuyorum. Görebildiğim kadarıyla, Socket.AcceptAsync ile ilgili asenkron soket modeli, MSDN'deki örneklerde gösterilenden daha fazlasını gerektirir.

Bağlantıları yürütmek ve özyinelemeyi kullanmadan ayrı iş parçacıkları oluşturmaya gerek kalmadan, Socket.AcceptAsync tarafından eşzamanlı olarak kabul edilen hemen bekleyen bağlantıları işlemek için herkesin temiz ve verimli bir çözümü var mı?

+0

Evet MSDN'deki örnekler (bu konuda, ya da en iyi uygulama) her zaman en iyi değildir. Sorunuzu açıklayabilir misiniz? StackOverflowExceptoin neden olmaz asyc soketleri ile çalışmak için bir yol mu arıyorsunuz? – Oded

+0

Bu doğru; Hemen bekleyen bağlantıları işlemek için özyinelemeli bir yol arıyorum. –

+0

Ayrıca bkz http://stackoverflow.com/questions/2002143/asynchronous-sockets-handling-false-socket-acceptasync-values ​​ – Lucero

cevap

7

Ben AcceptAsync kullanmak ister ziyade BeginAcceptEndAccept ve ortak eşzamansız modeli doğru şekilde uygulayın, yani tamamlanan işlemler sırasında geri arama iş parçasındaki geri çağrıları engellemek için CompletedSynchronously denetleyin.

gereksinimi ile ilgili AsyncCallBack CompletedSynchronously


Düzenleme AcceptAsync kullanmak da bakınız:

MSDN documentation açıkça geri arama senkronize olarak tamamlayan işlemleri için çağrılan ALINMAYACAKTIR söylüyor. Bu, geri çağrının her zaman çağrıldığı ortak async modelinden farklıdır.

G/Ç işlemi beklemedeyse, true değerini döndürür. E parametresindeki SocketAsyncEventArgs.Completed olayı işlemin tamamlanması üzerine yükseltilecektir. G/Ç işlemi 'u eşzamanlı olarak tamamladıysa, döndürür. E parametresi SocketAsyncEventArgs.Completed olay yükseltilmiş edilmez ve bir parametre olarak geçirilen e nesne yöntem çağrısı işleminin sonuç almak için geri döner hemen sonra incelenebilir.

Şu anda bir döngü yığınının taşması sorununu nasıl çözmeyeceğini göremiyorum. Belki de soruna neden olan kodda daha spesifik olabilirsiniz?


Düzenleme 2:

static void Main(string[] args) { 
    Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
    listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, 4444)); 
    listenSocket.Listen(100); 
    SocketAsyncEventArgs e = new SocketAsyncEventArgs(); 
    e.Completed += AcceptCallback; 
    if (!listenSocket.AcceptAsync(e)) { 
     AcceptCallback(listenSocket, e); 
    } 
    Console.ReadKey(true); 
} 

private static void AcceptCallback(object sender, SocketAsyncEventArgs e) { 
    Socket listenSocket = (Socket)sender; 
    do { 
     try { 
      Socket newSocket = e.AcceptSocket; 
      Debug.Assert(newSocket != null); 
      // do your magic here with the new socket 
      newSocket.Send(Encoding.ASCII.GetBytes("Hello socket!")); 
      newSocket.Disconnect(false); 
      newSocket.Close(); 
     } catch { 
      // handle any exceptions here; 
     } finally { 
      e.AcceptSocket = null; // to enable reuse 
     } 
    } while (!listenSocket.AcceptAsync(e)); 
} 
+0

Ne yazık ki BeginAccept/EndAccept ihtiyaçlarıma uymuyor, bunun nedeni her işlem için IAsyncResult nesnelerini başlatmanın gerekliliğidir. Sunucum yüksek hacimli bir işlem gerektiriyor ve profil oluşturma bu uygulamanın stresli bir nokta olduğunu ortaya çıkardı. Ve sonra bile, sağlanan çözüm, farklı bir iş parçacığı üzerinde geri çağırma çağırma veya ayrı bir iş parçacığından, hıza kadar çağrıları kabul etmek tuhaf bir sisteme sahip olarak, eşzamanlı tamamlama amacını bozar. Yaptığı diğer cevap aslında sizinkiyle özdeştir. –

+0

Düzenlemenize cevaben, sorun, AcceptAsync işleyicisini aynı iş parçacığından çağırarak hemen kullanılabilen SocketAsyncEventArgs işlemek durumunda ortaya çıkıyor, örneğin döndürme değerinin yanlış olup olmadığını kontrol ediyorsunuz ve sonra işleyiciyi argümanla çağırmanız yeterlidir. Socket.AcceptAsync'de kullanılan SocketAsyncEventArgs.Ve sonra, kabul işleyicisinin içinde, tüm bağlantıların (eşzamanlı veya eşzamansız) doğru şekilde işlendiğinden emin olmak için aynı deseni kullanırsınız. –

+0

Kod örneğiyle ikinci düzenlemenize yanıt olarak, sorumu soruma gönderdiğimde tam da aklımda olan şey buydu. Kodu yayınladığınız için teşekkürler, çünkü bazı insanlar, metinsel olarak karşılaşmaya çalıştığım şeyi anlamamış olabilir. İlk etapta sorunun nasıl çözüleceğine dair düşüncemi tetikleyen yanıtınız olduğuna inanıyorum; Tekrar teşekkürler. Senkronize ve eşzamansız soket işinin karıştırılmasının iğrenç olduğunu söylemem gerekse de, heh heh. Ama hey, bu fikir karşısında. –

1

dikkatle bakmadım ama bu ("yığın dalış" adlı bölüm) yararlı olabilir gibi kokuyor:/

http://blogs.msdn.com/b/mjm/archive/2005/05/04/414793.aspx

+0

Windows Communication Foundation hakkında konuşmuyoruz. Bu benzer bir sorun olsa da, oldukça alakasız. Ayrıca, işleyiciyi ayrı bir iş parçacığı üzerinde başlatarak, eşzamanlı olarak tam eylemleri gerçekleştirme amacını yitiriyor gibi görünüyor. Buna karşılık, ayrı bir iş parçacığına sahip olmak işleyici iş parçacığı üzerinde bir yığın taşmasına neden olabilir. –

+0

Net olmak gerekirse, aradığım bölüm sadece WCF ile ilgili değil, başlangıç ​​sonu deseni ile herhangi bir uyumsuzluk döngüsü yazmakla ilgili. – Brian

+0

Birkaç kez tekrar okudum ve kontrol ettim, sonra da yorumumu netleştirdim. Özür dilemeli olarak çıksa özür dilerim. Sadece aradığım şey değil ve girişinizi takdir ediyorum. –

3

Ben: Böyle kod düşünüyorum (sadece AcceptAsync açısından, geri kalanı sadece bunu denemek için bir çalışma uygulamasını almaya oldu) Bu sorunu, yalnızca döngü yerleşimini değiştirerek çözdü. Kabul işleyicisini kendi içinden yinelemeli olarak çağırmak yerine, kodu bir "do-while" döngüsünde "! Socket.AcceptAsync (args)" koşuluyla sarmalamak, yığının taşmasını engeller.

Bunun arkasındaki mantık, diğer bağlantıların rast gelmesini beklemeden eşzamanlı olarak beklemeden önce, hemen kullanılabilen bağlantıların işlenmesi için geri arama iş parçacığını kullanmanızdır. Etkin bir şekilde havuzlanmış bir iş parçacığı kullanıyor.

Yanıtları takdir ediyorum, ancak hiçbiri hiçbiri benimle tıklanmadı ve gerçekten sorunu çözmedi. Ancak, oradaki bir şey benim fikrimi bu düşünceyi ortaya çıkarıyordu. ThreadPool sınıfıyla manuel olarak çalışmayı önler ve yinelemeyi kullanmaz.

Elbette, eğer birisi daha iyi bir çözüme veya hatta bir alternatife sahipse, bunu duyduğuma sevinirim.

+0

Bu yorumu yazarken bir araya getirdiğim şeylere benziyor. Bu yüzden bir döngünün sorunu nasıl çözmeyeceğini sordum, çünkü bu döngü geri çekilmeye devam ederken özümleme yapılmaz ... – Lucero

+0

@Michael Bunun neredeyse 4 yaşında olduğunun farkındayım ama şu anda aynı sorun ve tam olarak ne yaptığınızı tam olarak anlayamadım. Hatırlıyor musun? – Rotem

+0

@Rotem Evet, yaparım! Temel olarak 'AcletAsync' sözcüğünü 'Completed' olayı geri arama içinden çağırıyordum ve eşzamanlı olarak tamamlandığında aynı olayı geri çağırıyordum. Bu, çok sayıda çağrının eşzamanlı olarak gerçekleştirilmesi halinde, sık sık kullanılan yüklerin altında olduğu gibi bir yığın taşmasına neden olacaktır. Bu şekilde yapmak yerine, şöyle bir döngü yaptım: 'while (! Acceptor.AcceptAsync (state)) {/ * şeyler yap * /}' –

0
newSocket.Send(Encoding.ASCII.GetBytes("Hello socket!")); 
newSocket.Disconnect(false); 
newSocket.Close(); 

Yukarıdaki parçacıkla ilgili sorun, bir sonraki kabul işleminizi engelleyecektir.

daha iyi bir yolu şu şekildedir:

while (true) 
{ 
    if (e.SocketError == SocketError.Success) 
    { 
     //ReadEventArg object user token 
     SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop(); 
     Socket socket = ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket; 

     if (!socket.ReceiveAsync(readEventArgs)) 
     ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessReceiveEx), readEventArgs); . 
    } 
    else 
    { 
     HadleBadAccept(e); 
    } 

    e.AcceptSocket = null; 

    m_maxNumberAcceptedClients.WaitOne(); 
    if (listenSocket.AcceptAsync(e)) 
     break; 
} 
+0

Bu benim 2 yaşındaki bir downvote için bir yorum. Bunu reddettim çünkü sorunun içeriği için bir anlam ifade etmiyor. Bir kod örneği yayınlamadım, bu yüzden bu sorunun cevabını yanıtlıyorsunuz ve sorunun kendisi değil. Bu kabul edilen cevap hakkında iki cümle yorumu olmalıydı. –