2010-02-15 7 views
7

Có cách nào để truy cập XmlReader không đồng bộ không? Các xml là đến tắt mạng từ nhiều khách hàng khác nhau như trong XMPP; nó là một dòng liên tục của <action>...</action> thẻ.XmlReader không đồng bộ trong .NET?

Tôi đang theo dõi để có thể sử dụng giao diện BeginRead/EndRead. Giải pháp tốt nhất mà tôi đã tạo ra là thực hiện đọc không đồng bộ cho 0 byte trên luồng mạng cơ bản, sau đó khi một số dữ liệu đến, hãy đọc Đọc trên XmlReader- tuy nhiên điều này sẽ chặn cho đến khi tất cả dữ liệu từ nút có sẵn. Giải pháp đó trông giống như thế này

private Stream syncstream; 
private NetworkStream ns; 
private XmlReader reader; 

//this code runs first 
public void Init() 
{ 
    syncstream = Stream.Synchronized(ns); 
    reader = XmlReader.Create(syncstream); 
    byte[] x = new byte[1]; 
    syncstream.BeginRead(x, 0, 0, new AsynchronousCallback(ReadCallback), null); 
} 

private void ReadCallback(IAsyncResult ar) 
{ 
    syncstream.EndRead(ar); 
    reader.Read(); //this will block for a while, until the entire node is available 
    //do soemthing to the xml node 
    byte[] x = new byte[1]; 
    syncstream.BeginRead(x, 0, 0, new AsynchronousCallback(ReadCallback), null); 
} 

EDIT: Đây là một thuật toán có thể làm việc nếu chuỗi chứa nút xml hoàn chỉnh?

Func<string, bool> nodeChecker = currentBuffer => 
       { 
        //if there is nothing, definetly no tag 
        if (currentBuffer == "") return false; 
        //if we have <![CDATA[ and not ]]>, hold on, else pass it on 
        if (currentBuffer.Contains("<![CDATA[") && !currentBuffer.Contains("]]>")) return false; 
        if (currentBuffer.Contains("<![CDATA[") && currentBuffer.Contains("]]>")) return true; 
        //these tag-related things will also catch <? ?> processing instructions 
        //if there is a < but no >, we still have an open tag 
        if (currentBuffer.Contains("<") && !currentBuffer.Contains(">")) return false; 
       //if there is a <...>, we have a complete element. 
       //>...< will never happen because we will pass it on to the parser when we get to > 
       if (currentBuffer.Contains("<") && currentBuffer.Contains(">")) return true; 
       //if there is no < >, we have a complete text node 
       if (!currentBuffer.Contains("<") && !currentBuffer.Contains(">")) return true; 
       //> and no < will never happen, we will pass it on to the parser when we get to > 
       //by default, don't block 
       return false; 
      }; 
+1

bộ đếm của bạn không thành công trong trường hợp này, là * hoàn toàn * hợp pháp XML: , trong đó ranh giới đọc là trước khi baz. –

Trả lời

2

XmlReader đệm trong khối 4kB, nếu tôi nhớ từ khi tôi xem xét điều này một vài năm trước đây. Bạn có thể đệm dữ liệu đến 4kB (ick!) Hoặc sử dụng trình phân tích cú pháp tốt hơn. Tôi cố định điều này bằng cách porting James Clark XP (Java) với C# như một phần của Jabber-Net, ở đây:

http://code.google.com/p/jabber-net/source/browse/#svn/trunk/xpnet

Đó là LGPL, chỉ xử lý UTF8, không được đóng gói để sử dụng, và hầu như không có tài liệu, vì vậy tôi sẽ không khuyên bạn nên sử dụng nó. :)

+0

Bạn có thể cho tôi tóm tắt nhanh về cách sử dụng trình phân tích cú pháp này không? Nhiều trường hợp có phân tích cú pháp các ổ cắm khác nhau một cách không đồng bộ mà không yêu cầu chủ đề của riêng chúng không? (như trong xmpp?) –

+1

Xem: http://code.google.com/p/jabber-net/source/browse/trunk/jabber/protocol/AsynchElementStream.cs ví dụ. Tạo một UTF8Encoding, ném byte vào nó với tokenizeContent hoặc tokenizeCdataSection, nhìn vào các thẻ xuất hiện. Trường hợp các byte đến từ, và đồng bộ hóa để đảm bảo rằng bạn không sửa đổi trạng thái của một trình phân tích cú pháp trên các luồng khác nhau tùy thuộc vào bạn. Nếu bạn muốn làm XMPP, bạn chỉ có thể sử dụng tất cả các Jabber-Net, và tiết kiệm cho mình một số rắc rối. –

+0

Vì vậy, có vẻ như giải pháp * chung * là tìm một trình phân tích cú pháp xml với giao diện cho phép tôi tự đặt byte vào thư viện thay vì cung cấp luồng. Trình phân tích cú pháp sẽ phân tích cú pháp nội dung khi tôi cung cấp nội dung đó, giữ các byte mà nó chưa phân tích cú pháp do nó không phải là nút xml hoàn chỉnh. Âm thanh về quyền? –

1

Điều này thực sự khó khăn, bởi vì XmlReader không cung cấp bất kỳ giao diện không đồng bộ.

Tôi không thực sự chắc chắn bao nhiêu không đồng bộ BeginRead hoạt động khi bạn yêu cầu đọc 0 byte - nó cũng có thể gọi gọi lại ngay lập tức và sau đó chặn khi bạn gọi Read. Điều này có thể giống như gọi trực tiếp Read và sau đó lên lịch Read tiếp theo trong nhóm chủ đề, ví dụ: sử dụng QueueWorkItem.

Có thể tốt hơn khi sử dụng BeginRead trên luồng mạng để đọc dữ liệu ví dụ trong khối 10kB (trong khi hệ thống chờ dữ liệu, bạn sẽ không chặn bất kỳ chuỗi nào). Khi bạn nhận được một đoạn, bạn sẽ sao chép nó vào một số địa phương MemoryStreamXmlReader của bạn sẽ đọc dữ liệu từ MemoryStream này.

Điều này vẫn gặp sự cố - sau khi sao chép 10kB dữ liệu và gọi Read nhiều lần, cuộc gọi cuối cùng sẽ chặn. Sau đó, bạn có thể cần phải sao chép các khối dữ liệu nhỏ hơn để bỏ chặn cuộc gọi đang chờ xử lý đến Read. Khi đã xong, bạn có thể bắt đầu lại một cuộc gọi BeginRead mới để đọc phần dữ liệu lớn hơn một cách không đồng bộ.

Thành thật mà nói, điều này nghe có vẻ khá phức tạp, vì vậy tôi khá hứng thú nếu có ai đưa ra câu trả lời tốt hơn. Tuy nhiên, nó cung cấp cho bạn ít nhất một số hoạt động không đồng bộ được đảm bảo mất một thời gian và không chặn bất kỳ luồng nào trong thời gian chờ đợi (đó là mục tiêu thiết yếu của lập trình không đồng bộ).

(Side lưu ý: Bạn có thể thử sử dụng F# asynchronous workflows viết này, bởi vì họ làm cho mã không đồng bộ đơn giản hơn rất nhiều Kỹ thuật tôi đã mô tả sẽ được khôn lanh ngay cả trong F # mặc dù.)

+0

Tôi đã ném cùng một bài kiểm tra nhanh và BeginRead'ing 0 byte là hoàn toàn tốt, gọi lại không được gọi cho đến khi một số dữ liệu sẵn sàng. Tôi sẽ có một shot tại thuật toán của bạn bây giờ –

+0

Ngoài ra, nếu tôi biết chiều dài tin nhắn, vấn đề bạn mô tả sẽ không tồn tại, phải không? –

+0

Nếu BeginRead làm cho nó chờ ít nhất một số dữ liệu thì có lẽ không sao (nếu bạn đang tải xuống các khối nhỏ). Nếu bạn biết thời lượng tin nhắn (một mục), bạn có thể đọc chính xác số lượng byte cần thiết để thực hiện lệnh gọi 'Đọc' tiếp theo. Nhưng điều này có thể vẫn còn có vấn đề (ví dụ với các mã hóa văn bản khác nhau, v.v.) –

2

Điều dễ nhất để làm là chỉ cần đặt nó trên một chủ đề khác, có lẽ một ThreadPool tùy thuộc vào bao lâu nó vẫn hoạt động. (Không sử dụng các chủ đề của luồng cho các tác vụ thực sự dài).

+0

Tôi nghĩ rằng một thread-per-client không quy mô rất tốt? –

+0

Không. Tôi không nhất thiết phải nói một chủ đề cho mỗi khách hàng :) – kyoryu

+0

Vì vậy, nếu mỗi khách hàng có dòng xml riêng của nó cho cuộc sống của kết nối, làm thế nào bạn sẽ tránh có mỗi XmlReader trong chủ đề riêng của nó? –

0

Bạn đang tìm kiếm một cái gì đó như phương pháp XamlReader.LoadAsync?

Thao tác tải XAML không đồng bộ ban đầu sẽ trả về đối tượng hoàn toàn là đối tượng gốc. Không đồng bộ, XAML phân tích cú pháp sau đó tiếp tục và mọi đối tượng con là được điền trong thư mục gốc.

+0

Tôi không nghĩ XamlReader sẽ kích hoạt các sự kiện khi các nút mới có sẵn, chỉ khi nó đã hoàn tất việc đánh dấu đánh dấu, trong trường hợp của tôi, sẽ là khi kết nối được đóng lại. Sẽ là một sử dụng thú vị của xaml mặc dù: P –

+0

Suy nghĩ càng nhiều. Để lại câu trả lời của tôi mặc dù trong trường hợp nó giúp người khác sau này ... –

1

Dường như DOT NET 4.5 có thuộc tính bool Async trên XmlReader, không có trong 3.5. Có lẽ điều đó sẽ làm việc cho bạn?

2

XmlReader trong .NET 4.5 có phiên bản async của hầu hết các phương pháp có liên quan đến IO.

Kiểm tra mã mẫu here.