C# Streams

Net Framework çalıştığı sistem üzerinde girdi,çıktı (input,output) I/O işlemleri için içerisinde streams-based hazır yapılar sunmaktadır. Bu öz (core) sınıflar System.IO namespace’i içerisinde yer almaktadır. Stream olarak ifade eden tüm sınıflar Stream ana sınıfından türetilmiştir.

Stream’ler verinin soyut hali olan birer byte dizisi olduğundan, bu byte dizileri manipüle etmek için byte diziler üzerinde okuma, yazma ve atlama (read,write and seek) gibi temel basit operasyonlar yapabiliyor olmamız gerekir.

Yazıya devam etmeden önce Stream kelimesinin türkçedeki karşılığı olan akış kelimesi ile anlamını kavramanıza yardımcı olmak adına açıklayayım. Aşağıdaki resimde görüldüğü üzere akış yardımcı bellek(Stream backing store) bilgisayar ve network üzerindeki herhangi bir kaynak olabilir ve burada verilerimizi tutuyor olabiliriz. Bu veriyi başka bir kaynağa göndermek için akış (stream) haline getiriyoruz ki okuma ve yazma operasyonlarını kolay ve yönetilebilir şekilde yapmamıza olanak tanımış olalım.

Stream Reader Writer

Stream sınıfı sayesinde stream üzerinde binary I/O operasyonları, TextReader ve TextWriter sınıfları sayesinde ise karekter I/O operasyonları, BinaryReader ve BinaryWriter sınıfları sayesindede daha ilkel (primitive) tipler için I/O işlemlerini gerçekleştirebiliriz.

SınıfAçıklama
StreamByte okuyup yazacak, Stream sınıflarının kullanacağı ana soyut sınıf (abstract base class).
FileStreamBasit Stream sınıfının normal davranışlarına ek olarak Seek metodu ile dosyalara rasgele erişim (random access) sağlar. Yapacağı işlemleri senkron ve asenkron olarak gerçekleştirebilir.
MemoryStreamTamponu olmayan (nonbuffered) veriyi direk bellekte (memory) tutan Stream türüdür. Bu stream veriyi saklamak için ayrı bir kaynak kullanmamaktadır, bu yüzden geçici tampon (temporary buffer) olarak kullanılması duruma göre avantaj sağlayabilmektedir.
BufferedStreamBir Stream'in tampon ihtiyacı olduğunda tampon olarak BufferedStream kullanmaktadır. NetworkStream gibi. (FileStream kendi içerisinde tampon yapısını halletmekte, Memory Stream ise tampona ihtiyaç duymamaktadır.) BufferedStream objesi duruma göre stream'lerin okuma ve yazma hızını arttıkmak için kullanılabilmektedir.
TextReaderStreamReader ve StringReader objeleri için soyut ana sınıftır (abstract base class). Soyut ana sınıf olan Stream sınıfı byte tipinde girdi (input) ve çıktı (output) vermek için tasarlanmıştır. TextReader sınıfı ise Unicode karakter çıktısı verecek şekilde tasarlanmış olup hazır metodları içermektedir.
StreamReaderStream'den karakterleri okur, byte tipindeki verileri Encoding kullarak karaktere çevirir.
StringReaderString tipinden karakterleri okumak için kullanılır. StringReader sınıfı kendi içerisinde String sınıfı ile aynı API'yi kullanır buna bağlı olarak Stream nesnesinden okuduğu byte tipini herhangi bir encoding türünde yada String çıktı (output) olarak verebilir.
TextWriterStreamWriter ve StringWriter objeleri için soyut ana sınıftır. Soyut ana sınıf olan Stream sınıfı byte tipinde girdi (input) ve çıktı (output) vermek için tasarlanmıştır. TextWriter sınıfı ise Unicode karakter girdisi verecek şekilde tasarlanmış olup hazır metodları içermektedir.
StreamWriterKarakterleri byte tipine çevitmek için Encoding kullanır ve Stream'e yazar.
StringWriterStringWriter sınıfı karakterleri String olarak yazar. Kendi içerisinde String sınıfı ile aynı API'yi kullanır buna bağlı olarak Stream nesnesinden okuduğu byte tipini herhangi bir encoding türünde yada String çıktı (output) olarak verebilir.
BinaryReaderStream'den binary veriyi okur.
BinaryWriterBinary veriyi Stream'e yazar.

Stream soyut sınıfından türemiş olan iki sınıf ve namespace’leri yukarıdaki tabloda yer almamaktadır. Bunlardan biri System.Net.Sockets namespace’i içerisinde yer alan NetworkStream‘dir. NetworkStream sınıfı sayesinde bir stream ağ (network connection) üzerinde temsil edilebilmektedir. Diğeride System.Security.Cryptography namespace’i içerisinde yer alan CryptoStream‘dir. CryptoStream sayesinde veri stream’lerini kriptografik bir biçimde birleştirmektedir.

Stream sınıfı ve türevlerinin tasarımı, veri kaynağı ve aktarılacağı yer için genel bir ifade ediş biçiminde yapılmıştır. Buda programcıya uygulamasını tekrar tasarlamadan bu stream sınıflarını birbirinin yerine kullanılabilmesine olanak tanır.

Genel olarak Stream objeleri aşağıdaki işlemlerden birini yada daha fazlasını yerine getirebilmektedir :

  • Stream’den verileri okuyarak belirli bir veri yapısına transfer edebilmek, örneğin byte dizisi olarak.
  • Herhangi bir veri kaynağından verileri stream içerisine yazabilir.
  • Stream üzerinde şuan ki konumundan (current position) başka bir konumuna atlayabilir(seek).

Bu özelliklerin hepsi her stream tarafından desteklenmediğini unutmamak gerekir. Örneğin NetworkStream objesi başka bir konuma atlamayı (seek) desteklememektedir. Kullandığınız Stream’in hangi operasyonları desteklendiğini CanRead, CanWrite ve CanSeek özelliklerine bakarak anlayabilirsiniz.

Stream Mimarisi

Stream Mimarisi

Senkron ve Asenkron I/O

Stream üzerinde gerçekleştireceğimiz operasyonları senkron yada asenkron olmak üzere uygulamanıza bağlı olarak iki şekilde yapabiliriz. Buradan anlaşılacağı üzere Stream sınıfı senkron ve asenkron operasyonları desteklemektedir. Bu operasyonları örneklemeden önce avantajları ve dezavantajları üzerine biraz açıklama yapalım.

Senkron (Synchronous) I/O

Varsayılan olarak stream operasyonlarının tümü senkron olarak yapılmaktadır. Bu şekikde I/O işlemleri en basit şekilde yapılmış olmaktadır. Senkron olarak işlemlerin yapılmasının dezavantajı ise yapılan I/O operasyonu bitene kadar uygulamanız çalışmaya devam etmez ve arayüz kilitlenir. Senkron I/O operasyonları, işlem yapacağınız veri kaynağının (dosya vb.) boyutu küçükse kullanışlı olmaktadır. Büyük boyutlu veri kaynağı (dosya vb.) uygulamanız performans sorunları yaşayacak ve çalışan thread bloklanacaktır. Aynı şekilde ağ (network) üzerinden yapılan işlemler için senkron I/O uygun olmamaktadır, çünkü ağ üzerinde kontrolünüz daha az ve zamanınız kısıtlı olacaktır. Buna bağlı olarak ağ üzerinden çok büyük boyutlu stream transferi yapmak için senkron I/O kullanmak doğru bir seçim olmamaktadır.

Asenkron (Asynchronous) I/O

Asenkron I/O işlemleri yaparken uygulamanız kilitlenmez ve I/O operasyonu çalışırken diğer işlerinizde devam eder. Asenkron I/O operasyonu bittiği zaman sistem I/O işlemini çağıran yapıya geri bildirimde bulunur. Yani asenkron I/O işlemleri için ayrı bir bildirim mekanizması gerekmektedir. Asenkron I/O metodları stream ile büyük boyutlu verileri işlerken yada yavaş donanımlar (hdd, network vb.) uygulamanızın yavaşlamadan işlemlerini bitirmesine olanak sağlar ve her asenkron I/O işlemi için ayrı bir sistemde ayrı bir thread oluşturur.

Kaynak ;

Asenkron IO işlemleri için STUART BLACKLER’ın Asynchronous File IO in .Net makalesini okumanızı özellikle tavsiye ederim.

Stream Class
A replacement for MemoryStream

Bir cevap yazın