2010-05-06 35 views
12

Tôi có đối tượng Luồng đôi khi nhận được một số dữ liệu trên đó, nhưng không thể đoán trước được. Các thông báo xuất hiện trên luồng được xác định rõ và khai báo kích thước của tải trọng trước (kích thước là số nguyên 16 bit chứa trong hai byte đầu tiên của mỗi thư).Đọc liên tục từ luồng?

Tôi muốn có lớp StreamWatcher phát hiện thời điểm Luồng có một số dữ liệu trên đó. Một khi nó xảy ra, tôi muốn một sự kiện được nâng lên để một cá thể StreamProcessor đã đăng ký có thể xử lý thông báo mới.

Điều này có thể thực hiện với các sự kiện C# mà không cần sử dụng trực tiếp Chủ đề không? Nó có vẻ như nó nên được đơn giản, nhưng tôi không thể có được khá đầu của tôi xung quanh đúng cách để thiết kế này.

Trả lời

0

Có, điều này có thể được thực hiện. Sử dụng phương pháp không chặn Stream.BeginRead với số AsyncCallback. Gọi lại được gọi là không đồng bộ khi dữ liệu có sẵn. Trong cuộc gọi lại, hãy gọi Stream.EndRead để nhận dữ liệu và gọi lại Stream.BeginRead để nhận dữ liệu tiếp theo. Đệm dữ liệu đến trong một mảng byte đủ lớn để chứa thông báo. Khi mảng byte đầy (có thể cần nhiều cuộc gọi lại), hãy tăng sự kiện. Sau đó đọc kích thước tin nhắn tiếp theo, tạo một bộ đệm mới, lặp lại, thực hiện.

+0

Anh ấy nói mà không cần sử dụng chủ đề !!! =) – Nayan

+0

@Nayan: Asynchrony * là * đa luồng. Vấn đề này vốn là một trong đa luồng. Tôi nghi ngờ ý nghĩa của OP là anh ta không muốn tự tạo ra các chủ đề một cách rõ ràng. –

+0

@Nayan: Tôi cho rằng OP đang tìm kiếm một giải pháp không tạo một Chủ đề mới rõ ràng và sử dụng phương pháp Đọc chặn, nhưng đối với một giải pháp không đồng bộ, không chặn. Bạn không thể có asynchronicity không có chủ đề. – dtb

1

Cách tiếp cận thông thường là sử dụng .NET asynchronous programming pattern được hiển thị bởi Stream. Về cơ bản, bạn bắt đầu đọc không đồng bộ bằng cách gọi Stream.BeginRead, chuyển qua bộ đệm byte[] và phương thức gọi lại sẽ được gọi khi dữ liệu đã được đọc từ luồng. Trong phương thức gọi lại, bạn gọi số Stream.EndRead, chuyển cho đối số IAsncResult đã được trao cho cuộc gọi lại của bạn. Giá trị trả lại của EndRead cho bạn biết số byte đã được đọc vào bộ đệm.

Khi bạn đã nhận được vài byte đầu tiên theo cách này, bạn có thể đợi phần còn lại của thư (nếu bạn không nhận đủ dữ liệu trong vòng đầu tiên) bằng cách gọi lại BeginRead. Khi bạn đã có toàn bộ thư, bạn có thể nâng cao sự kiện.

11

Khi bạn nói không sử dụng đề trực tiếp, tôi giả sử bạn vẫn muốn sử dụng chúng gián tiếp qua các cuộc gọi async, nếu không điều này sẽ không thể rất hữu ích.

Tất cả những gì bạn cần làm là bọc các phương pháp không đồng bộ của Stream và lưu kết quả vào bộ đệm. Trước tiên, hãy xác định một phần sự kiện của spec:

public delegate void MessageAvailableEventHandler(object sender, 
    MessageAvailableEventArgs e); 

public class MessageAvailableEventArgs : EventArgs 
{ 
    public MessageAvailableEventArgs(int messageSize) : base() 
    { 
     this.MessageSize = messageSize; 
    } 

    public int MessageSize { get; private set; } 
} 

Bây giờ, đọc một 16-bit số nguyên từ con suối không đồng bộ và báo cáo lại khi nó đã sẵn sàng:

public class StreamWatcher 
{ 
    private readonly Stream stream; 

    private byte[] sizeBuffer = new byte[2]; 

    public StreamWatcher(Stream stream) 
    { 
     if (stream == null) 
      throw new ArgumentNullException("stream"); 
     this.stream = stream; 
     WatchNext(); 
    } 

    protected void OnMessageAvailable(MessageAvailableEventArgs e) 
    { 
     var handler = MessageAvailable; 
     if (handler != null) 
      handler(this, e); 
    } 

    protected void WatchNext() 
    { 
     stream.BeginRead(sizeBuffer, 0, 2, new AsyncCallback(ReadCallback), 
      null); 
    } 

    private void ReadCallback(IAsyncResult ar) 
    { 
     int bytesRead = stream.EndRead(ar); 
     if (bytesRead != 2) 
      throw new InvalidOperationException("Invalid message header."); 
     int messageSize = sizeBuffer[1] << 8 + sizeBuffer[0]; 
     OnMessageAvailable(new MessageAvailableEventArgs(messageSize)); 
     WatchNext(); 
    } 

    public event MessageAvailableEventHandler MessageAvailable; 
} 

Tôi nghĩ đó là về nó. Điều này giả định rằng bất kỳ lớp nào đang xử lý thư cũng có quyền truy cập vào Stream và được chuẩn bị để đọc nó, đồng bộ hoặc không đồng bộ, dựa trên kích thước thư trong sự kiện. Nếu bạn muốn lớp người xem thực sự đọc toàn bộ thư thì bạn sẽ phải thêm một số mã để làm điều đó.

+0

+1 để thực hiện chứng minh lý thuyết của tôi về BeginRead không phải là giải pháp đúng. Việc triển khai của bạn đã xóa bỏ sự nghi ngờ đó. – Nayan

+0

Vinh quang! Điều này có vẻ như nó sẽ làm các trick, và tôi sẽ thử nó vào ngày mai. –

+0

+1. Nitpicking: AFAIK một Stream.Read được phép trả về ít hơn sau đó đếm byte, miễn là nó trả về ít nhất một byte (nếu không kết thúc). Vì vậy, nếu '(bytesRead! = 2)' bạn không nên ném một ngoại lệ nhưng BeginRead một lần nữa cho đến khi hai byte đã được đọc. – dtb

1

Không sử dụng Stream.BeginRead() giống như sử dụng phương thức Stream.Read() đồng bộ trong một chuỗi riêng biệt?

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