2010-10-17 44 views
12

Đối với phương thức System.Net.Sockets.Socket.AcceptAsync của C# và .NET, một phương pháp sẽ được yêu cầu để xử lý giá trị trả lại là "sai" để xử lý trạng thái sẵn có ngay lập tức SocketAsyncEventArgs từ kết nối được xử lý đồng bộ. Microsoft cung cấp các ví dụ (được tìm thấy trên trang lớp học System.Net.Sockets.SocketAsyncEventArgs) sẽ gây tràn ngăn xếp nếu có một số lượng lớn các kết nối đang chờ xử lý, có thể được khai thác trên bất kỳ hệ thống nào triển khai mô hình xử lý của chúng.Tràn ngăn xếp khi sử dụng mô hình System.Net.Sockets.Socket.AcceptAsync

Các ý tưởng khác để giải quyết vấn đề này là tạo vòng lặp gọi phương thức xử lý, với điều kiện là giá trị Socket.AcceptAsync trả về bằng false và để ngắt vòng lặp (để cho phép xử lý trì hoãn) nếu giá trị chỉ ra rằng hoạt động đang được hoàn thành không đồng bộ (đúng). Tuy nhiên, giải pháp này cũng gây ra lỗ hổng tràn ngăn xếp do thực tế là cuộc gọi lại được liên kết với số SocketAsyncEventArgs được chuyển đến Socket.AcceptAsync có ở cuối phương thức, gọi tới số Socket.AcceptAsync, cũng có vòng lặp cho các kết nối có sẵn, đồng bộ ngay lập tức .

Như bạn có thể thấy, đây là một vấn đề khá vững chắc và tôi chưa tìm được giải pháp tốt nào không liên quan đến System.Threading.ThreadPool và tạo tấn phương pháp khác và xử lý lịch biểu. Theo như tôi thấy, mô hình socket không đồng bộ liên quan đến Socket.AcceptAsync đòi hỏi nhiều hơn những gì được thể hiện trong các ví dụ trên MSDN.

Có ai có giải pháp sạch và hiệu quả để xử lý ngay các kết nối đang chờ xử lý được chấp nhận đồng bộ từ Socket.AcceptAsync mà không cần tạo chuỗi riêng để xử lý các kết nối và không sử dụng đệ quy không?

+0

Yeah, các ví dụ trên MSDN không phải lúc nào (thực tế hoặc tốt nhất, cho rằng vấn đề) tốt nhất. Bạn có thể làm rõ câu hỏi của bạn? Bạn đang tìm kiếm một cách để làm việc với các ổ cắm asyc mà sẽ không gây ra StackOverflowExceptoin? – Oded

+0

Điều đó là chính xác; Tôi đang tìm một cách không đệ quy để xử lý các kết nối đang chờ xử lý ngay lập tức. –

+0

Xem thêm http://stackoverflow.com/questions/2002143/asynchronous-sockets-handling-false-socket-acceptasync-values ​​ – Lucero

Trả lời

7

tôi sẽ không sử dụng AcceptAsync, nhưng thay vì BeginAccept/EndAccept và triển khai đúng mẫu không đồng bộ chính xác, nghĩa là, kiểm tra CompletedSynchronously để tránh các cuộc gọi lại trong chuỗi gọi lại về các thao tác đã hoàn tất.

cũng Xem AsyncCallBack CompletedSynchronously


Chỉnh sửa liên quan đến nhu cầu sử dụng AcceptAsync:

Các MSDN documentation dứt khoát nói rằng callback sẽ KHÔNG được gọi cho các hoạt động mà hoàn thành đồng bộ. Điều này khác với mẫu không đồng bộ chung, nơi gọi lại luôn được gọi.

Trả về true nếu hoạt động I/O là đang chờ xử lý. SocketAsyncEventArgs. Sự kiện đã hoàn thành trên thông số e sẽ được nâng lên khi hoàn tất thao tác . Trả về sai nếu hoạt động I/O hoàn thành đồng bộ. SocketAsyncEventArgs.Các sự kiện đã hoàn thành trên tham số e sẽ không được nâng lên và đối tượng e được chuyển thành tham số có thể được kiểm tra ngay sau khi gọi phương thức để truy xuất kết quả .

Tôi hiện không thấy cách vòng lặp không giải quyết được sự cố tràn ngăn xếp. Có lẽ bạn có thể cụ thể hơn về mã gây ra sự cố?


Chỉnh sửa 2: Tôi đang nghĩ đến việc mã như thế này (chỉ liên quan đến AcceptAsync, phần còn lại là chỉ để có được một ứng dụng làm việc để thử nó ra với):

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

Thật không may BeginAccept/EndAccept không phù hợp với nhu cầu của tôi, điều này là do yêu cầu để khởi tạo các đối tượng IAsyncResult cho mỗi hoạt động. Máy chủ của tôi yêu cầu khối lượng hoạt động cao và hồ sơ tiết lộ rằng đây là điểm căng thẳng trong ứng dụng. Và thậm chí sau đó, giải pháp được cung cấp đánh bại mục đích hoàn thành đồng bộ bằng cách gọi lại cuộc gọi trên một chủ đề khác hoặc có một số hệ thống lạ giữ các cuộc gọi chấp nhận tới tốc độ, từ một chuỗi riêng biệt. Câu trả lời khác mà một người nào đó thực hiện về cơ bản giống với của bạn. –

+0

Để phản hồi chỉnh sửa của bạn, vấn đề xuất hiện trong trường hợp xử lý các SocketAsyncEventArgs ngay lập tức bằng cách gọi trình xử lý AcceptAsync từ cùng một luồng, ví dụ bạn kiểm tra xem giá trị trả về có sai không, và sau đó chỉ cần gọi trình xử lý với đối số của SocketAsyncEventArgs được sử dụng trong Socket.AcceptAsync.Và sau đó, bên trong trình xử lý chấp nhận, bạn sử dụng cùng một mẫu đó ở cuối để đảm bảo rằng tất cả các kết nối (đồng bộ hoặc không đồng bộ) được xử lý đúng cách. –

+0

Để đối phó với bản chỉnh sửa thứ hai của bạn với mẫu mã, đó là chính xác những gì tôi có trong tâm trí khi tôi đăng câu trả lời cho câu hỏi của mình. Cảm ơn bạn đã đăng mã, bởi vì một số người có thể không hiểu những gì tôi đã cố gắng để có được trên văn bản. Tôi tin rằng đó là phản ứng của bạn đã kích hoạt suy nghĩ của tôi về cách giải quyết vấn đề ngay từ đầu; cảm ơn một lần nữa. Mặc dù tôi phải nói rằng trộn công việc ổ cắm đồng bộ và không đồng bộ là đáng ghét, heh heh. Nhưng hey, nó được ý tưởng trên. –

1

Tôi đã không nhìn cẩn thận, nhưng nó có mùi như thế này có thể hữu ích (xem phần gọi là "chồng lặn"):

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

+0

Chúng tôi không nói về Windows Communication Foundation. Mặc dù đây là một vấn đề tương tự nhưng nó không liên quan. Nó cũng có vẻ như họ chỉ đơn giản là đánh bại mục đích có hành động hoàn thành đồng bộ, bằng cách bắt đầu xử lý trên một chủ đề riêng biệt. Đổi lại, có nó trên một thread riêng biệt chỉ có thể gây ra một tràn ngăn xếp trên thread xử lý. –

+0

Để được rõ ràng, phần tôi gọi ra không chỉ là về WCF, nó về viết bất kỳ vòng lặp async với mô hình bắt đầu-kết thúc. – Brian

+0

Tôi đọc lại nó một vài lần và đã kiểm tra nó, và sau đó làm rõ bình luận của tôi. Lời xin lỗi của tôi nếu nó đến như là miễn nhiệm. Nó không chỉ là những gì tôi đang tìm kiếm, và tôi đánh giá cao đầu vào của bạn. –

3

Tôi có giải quyết vấn đề này bằng cách thay đổi vị trí của vòng lặp. Thay vì đệ quy gọi trình xử lý chấp nhận từ bên trong chính nó, gói mã trong một vòng lặp do-while với điều kiện là "! Socket.AcceptAsync (args)" ngăn chặn tràn ngăn xếp.

Lý do đằng sau việc này là bạn sử dụng chuỗi gọi lại để xử lý các kết nối có sẵn ngay lập tức, trước khi bận tâm chờ đợi không đồng bộ cho các kết nối khác đi qua. Đó là tái sử dụng một thread gộp lại, có hiệu quả.

Tôi đánh giá cao câu trả lời nhưng vì một lý do nào đó mà không ai trong số họ nhấp vào tôi và không thực sự giải quyết vấn đề. Tuy nhiên, có vẻ như một cái gì đó trong đó đã kích hoạt tâm trí của tôi để đưa ra ý tưởng đó. Nó tránh làm việc thủ công với lớp ThreadPool và không sử dụng đệ quy.

Tất nhiên, nếu ai đó có giải pháp tốt hơn hoặc thậm chí là giải pháp thay thế, tôi rất sẵn lòng nghe nó.

+0

Âm thanh khá giống với những gì tôi đã được hack với nhau trong khi bạn đã viết bình luận đó. Đó là lý do tại sao tôi hỏi làm thế nào một vòng lặp sẽ không giải quyết vấn đề, kể từ khi gọi lại không được gọi là trong khi bạn đang looping không có đệ quy ... – Lucero

+0

@ Michael Tôi nhận ra điều này là gần 4 tuổi, nhưng tôi hiện đang giải quyết cùng một vấn đề và tôi không thể thực sự hiểu chính xác những gì bạn đã làm. Bạn có nhớ không? – Rotem

+0

@Rotem Yeah, tôi biết! Về cơ bản, ban đầu tôi đã gọi 'AcceptAsync' từ bên trong sự kiện gọi lại' Completed' và trong trường hợp nó hoàn thành đồng bộ, tôi chỉ gọi cùng một sự kiện gọi lại. Điều này sẽ gây ra một tràn ngăn xếp nếu nhiều cuộc gọi được hoàn thành đồng bộ, như là phổ biến dưới tải thường xuyên. Thay vì làm theo cách này, tôi chỉ thực hiện một vòng lặp như: 'while (! Acceptor.AcceptAsync (state)) {/ * làm công cụ * /}' –

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

Vấn đề với đoạn mã trên là điều này sẽ chặn hoạt động chấp nhận tiếp theo của bạn.

Cách tốt hơn là như thế này:

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

Đây là một bình luận cho downvote cũ 2 năm của tôi. Tôi đã bỏ phiếu này vì nó không có ý nghĩa gì đối với ngữ cảnh của câu hỏi. Tôi đã không đăng một ví dụ mã, do đó, nó có vẻ như bạn đang trả lời một câu trả lời cho câu hỏi này và không phải là câu hỏi chính nó. Điều này cần phải có một bình luận hai câu trên câu trả lời được chấp nhận. –

Các vấn đề liên quan