2014-11-01 15 views
5

Tôi có một câu hỏi liên quan đến câu hỏi this ("Ổ cắm máy chủ không đồng bộ nhiều máy khách").Microsofts Ổ đĩa máy chủ không đồng bộ Ví dụ

Dù Microsoft đã thay đổi kể từ khi the example Groos câu trả lời hay tôi thực sự không nhận được nó - trong ví dụ này nó nói:

 while (true) { 
      // Set the event to nonsignaled state. 
      allDone.Reset(); 

      // Start an asynchronous socket to listen for connections. 
      Console.WriteLine("Waiting for a connection..."); 
      listener.BeginAccept( 
       new AsyncCallback(AcceptCallback), 
       listener); 

      // Wait until a connection is made before continuing. 
      allDone.WaitOne(); 
     } 

Như tôi đã hiểu được BeginAccept() hàm được gọi lấy lại trong while (true) loop, chỉ bị dừng bởi và cho đến khi hàm AcceptCallback() được gọi, bởi vì điều đầu tiên xảy ra có allDone.Set().

Nhưng Groo nói

Vấn đề với MSDN dụ là nó cho phép kết nối chỉ có một khách hàng duy nhất (listener.BeginAccept được gọi là một lần duy nhất).

Và thực sự tôi không nhận được lý do tại sao ManualResetEvent allDone được sử dụng ở tất cả các ... Và tôi nghĩ phương pháp listener.EndAccept (ar) chặn anyways.

Trình nghe.BeginAccept() có ném ngoại lệ nếu được gọi là lần thứ hai trong khi vẫn chạy không? Nhưng nếu vậy tại sao allDone.Set() là trước khi listener.EndAccept (ar)?

Và một câu hỏi:

Tôi có thể chỉ cần gọi handler.BeginReceive (...), trong (ar IAsyncResult) ReadCallback chức năng sau khi tôi nhận được một EOF, chờ đợi để biết thêm dữ liệu incomming so với cùng khách hàng?

Bất kỳ ai có nhiều kinh nghiệm hơn có thể giải thích cho tôi không?

Cảm ơn!

+1

Đã lâu rồi tôi trả lời, nhưng IIRC ví dụ ban đầu không có vòng lặp. Chúng tôi có thể kiểm tra [trang lưu trữ internet trong khoảng thời gian đó] (http://web.archive.org/web/20110201000000*/http://msdn.microsoft.com/en-us/library/fx6588te.aspx), nhưng máy chủ hiện có vẻ bị hỏng. – Groo

+0

sau đó có vấn đề với câu hỏi cũ vì nó có liên kết đến mã nguồn được cập nhật ... – fosb

Trả lời

7

Có thể ví dụ này đã được cập nhật thực tế vì câu trả lời SO khác đã được đăng. Hoặc có thể người trả lời Groo đơn giản là không hoàn toàn hiểu rõ ví dụ của mình. Trong cả hai trường hợp, bạn đúng để quan sát rằng tuyên bố của mình rằng chỉ có một khách hàng có thể được chấp nhận là không chính xác.

Tôi đồng ý với một số những gì usr đã viết, nhưng có một chút khác biệt về toàn bộ điều. Ngoài ra, tôi nghĩ rằng các câu hỏi xứng đáng được điều trị toàn diện và cụ thể hơn.

Trước tiên, trong khi tôi đồng ý rằng thiết kế cao cấp thường phát hành các cuộc gọi tiếp theo tới BeginAccept() trong phương thức gọi lại chấp nhận thay vì sử dụng vòng lặp, không có gì sai với việc triển khai trong ví dụ. Không có cuộc gọi mới đến BeginAccept() được thực hiện cho đến khi hoàn thành sau cuộc gọi trước đó; trình xử lý sự kiện được sử dụng để đồng bộ hóa luồng mà tại đó BeginAccept() được gọi với bất kỳ luồng nào xử lý việc hoàn thành. Luồng đầu tiên được phát hành chỉ khi chấp nhận được chấp thuận trước đó hoàn thành, và sau đó chỉ có một cuộc gọi mới đến BeginAccept() được thực hiện trước khi chặn chuỗi đó một lần nữa.

Hơi khó xử, nhưng hoàn toàn kosher. Có thể là tác giả của mẫu đó đã tìm ra rằng vì trong chương trình giao diện điều khiển của mình, anh ta sẽ có một sợi chỉ đang ngồi ở đó nhàn rỗi, anh ấy cũng có thể cho nó một cái gì đó để làm. :)

Dù sao, để trả lời câu hỏi # 1: bạn chính xác, ví dụ hiện tại tại liên kết đó cho phép nhiều khách hàng kết nối.

Câu hỏi # 2, tại sao xử lý sự kiện được sử dụng, tôi hy vọng giải thích ở trên đã trả lời điều đó. Đó là những gì ví dụ sử dụng để giải phóng thread đang gọi BeginAccept(), để nó có thể gọi nó một lần khác khi cuộc gọi trước đó đã hoàn thành.

Câu hỏi # 3, EndAccept() đang chặn? Sắp xếp. Nếu bạn gọi EndAccept() trước khi hoạt động chấp nhận đã thực sự hoàn tất, thì có. Nó sẽ chặn. Nhưng trong trường hợp này, nó chỉ được gọi khi cuộc gọi hoàn thành đã được gọi. Tại thời điểm này, chúng tôi có thể chắc chắn rằng lệnh gọi đến số EndAccept() sẽ không phải là số. Tất cả nó sẽ làm là lấy kết quả của các hoạt động hoàn thành tại thời điểm đó (giả sử không có trường hợp ngoại lệ, tất nhiên).

Câu hỏi # 4, liệu pháp luật có gọi số BeginAccept() lần thứ hai trước khi EndAccept() được gọi không? Có, ngay cả khi nó không hợp pháp để có nhiều hoạt động chấp nhận xếp hàng (nó là). Tại đây, cuộc gọi đến BeginAccept() xảy ra trong cuộc gọi lại hoàn tất cho số BeginAccept() đầu tiên. Tức là, trong khi mã chưa gọi là EndAccept(), hoạt động chấp nhận chính nó đã hoàn tất và do đó, thậm chí không phải là trường hợp có nhiều hoạt động chấp nhận xuất sắc. Các hoạt động nhận và gửi tương tự tự do; bạn có thể gọi một cách hợp pháp tất cả các phương thức đó nhiều lần trước khi hoàn thành bất kỳ điều gì đã xảy ra.

Câu hỏi số 5, tôi có thể gọi số BeginReceive() mặc dù tôi đã nhận được <EOF> không? Vâng. Trên thực tế, đây là khu vực mà ví dụ MSDN bị thiếu, trong đó không tiếp tục nhận được sau khi nhận được dữ liệu mong đợi cuối cùng. Trong thực tế, cho đến khi nhận được hoàn thành với 0 byte, nó sẽ luôn gọi BeginReceive() một lần nữa, có hay không nhiều dữ liệu được mong đợi, và sau đó xử lý một nhận hoàn thành trong đó số byte là 0 bằng cách gọi Shutdown(SocketShutdown.Both) tại thời điểm đó để báo hiệu xác nhận của duyên dáng đóng kết nối (giả sử nó được thực hiện gửi vào thời điểm đó, lúc đó nó sẽ được gọi là Shutdown(SocketShutdown.Send) ... nếu không, nó chỉ nên sử dụng SocketShutdown.Receive và/hoặc không gọi tắt cho đến khi nó được gửi xong và nó có thể sử dụng SocketShutdown.Both ... SocketShutdown.Receive không thực sự làm bất kỳ điều gì quan trọng đối với kết nối, vì vậy, chỉ cần đợi đến SocketShutdown.Both là thích hợp).

Nói cách khác, ngay cả khi máy chủ biết chắc chắn khách hàng sẽ không gửi bất kỳ dữ liệu bổ sung nào, việc sử dụng đúng API socket vẫn phải thực hiện thao tác nhận khác, tìm kiếm trả về 0 byte đó giá trị chỉ ra rằng máy khách đã thực sự bắt đầu tắt kết nối. Chỉ tại thời điểm đó máy chủ nên bắt đầu quá trình tắt máy của riêng mình và đóng socket.

Cuối cùng, bạn không hỏi nhưng vì usr đã đưa nó lên: Tôi không đồng ý rằng ví dụ MSDN này không có liên quan ngày hôm nay. Thật không may, Microsoft đã không cung cấp một phiên bản dựa trên Task của một API async cho lớp Socket. Có các API mạng khác hỗ trợ async/await (ví dụ: TcpClient/NetworkStream), nhưng nếu bạn muốn sử dụng trực tiếp lớp Socket, bạn bị mắc kẹt với các mô hình không đồng bộ cũ (Socket có hai, cả gọi lại).

Bạn có thể quấn các phương thức Socket đồng bộ trong Tasks làm phương án thay thế cho API cũ hơn, nhưng sau đó bạn sẽ mất lợi thế của API không đồng bộ dựa trên cổng I/O trong lớp Socket. Tốt hơn nhiều là một loại trình bao bọc tương thích với Task mà vẫn sử dụng API không đồng bộ bên dưới, nhưng điều đó phức tạp hơn để triển khai và tôi không nhận thức được một điều như vậy vào lúc này (nhưng nó chắc chắn có thể tồn tại, để có thể đáng một chút tìm kiếm trên web nếu bạn muốn sử dụng async/await).

Hy vọng điều đó sẽ hữu ích! Tôi xin lỗi vì câu trả lời dài, nhưng đó là một câu hỏi khá rộng (gần như quá rộng, nhưng với tôi nó dường như vẫn nằm trong giới hạn SO hợp lý :)).

+0

wow, cảm ơn thời gian của bạn đã trả lời chi tiết này @Peter Duniho! Tôi thực sự cảm kích! Cảm ơn bạn đã giúp đỡ và thời gian của bạn guys. – fosb

0

Mẫu bị nhầm lẫn. Sự kiện là không cần thiết. Thay vào đó, việc gọi lại chấp nhận được cho là phát hành hoạt động chấp nhận tiếp theo để luôn có một chấp nhận nổi bật.

Gọi bắt đầuChấp nhận trong vòng lặp chưa được kiểm tra sẽ không chính xác vì điều đó sẽ bắt đầu một số lượng không giới hạn các hoạt động chấp nhận chưa xử lý.

Bạn có biết rằng kể từ khi giới thiệu await APM cũ đã lỗi thời? Tất cả các mã này không có liên quan ngày hôm nay.

Ngoài ra, hãy lưu ý rằng hầu hết mã ổ cắm trên web đều thiếu sót nghiêm trọng theo các cách khác nhau.

+0

Cảm ơn câu trả lời, nó dẫn tôi đến các câu hỏi sau: > Bạn có biết rằng kể từ khi giới thiệu đang chờ APM cũ đã lỗi thời? Tất cả các mã này không có liên quan ngày hôm nay. – fosb

+0

xin lỗi, tạo ra câu trả lời trước đây do tai nạn ... @usr nhưng cảm ơn câu trả lời, nó dẫn tôi đến các câu hỏi sau: * Cuộc gọi chấp nhận tiếp theo là gọi listener.BeginAccept()? Tôi đã nhận được quyền đó sau đó listener.EndAccept() đang chờ đợi kết nối incomming tiếp theo? * Bạn có nghĩ rằng việc làm với TCPListener và TCPClient là một cách tốt hơn? "Bạn có biết rằng kể từ khi phần giới thiệu chờ đợi APM cũ đã lỗi thời? Tất cả mã này không có liên quan ngày hôm nay." Bạn có ý nói tốt hơn là nên đi với EAP? Cảm ơn bạn lần nữa ^^ – fosb

+0

Bắt đầu và Kết thúc được ghép nối. Bất kỳ cuộc gọi Kết thúc cụ thể nào khớp với cuộc gọi Bắt đầu và tìm nạp kết quả của nó. Kết thúc chỉ đơn giản là cung cấp cho bạn kết nối đã được chấp nhận. Tiếp theo, hãy gọi lại Bắt đầu lại để bắt đầu chấp nhận lại .; TcpListener/Client là trình bao bọc mỏng. Chúng được ưa thích .; EAP cũng đã lỗi thời. Sử dụng đang chờ với NetworkStream. Sử dụng các phương thức IO không đồng bộ như ReadAsync. – usr

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