Forum Sevdam
Nickiniz
Şifreniz
Sponsor Linkler




Hediyeler    Anasayfa    Gruplar    Etiketler    Blog    Yardım    İletişim
Forumdan En İyi Şekilde Faydalanmak İçin KAYIT Olmanız Gerekmektedir.    

Geri git   Forum Sevdam > Bilgisayar ve İnternet > Programlama ve Web Tasarım > Silverlight, Expression, LinQ, Ajax, JavaScript

Bu Bölümlerimizi İncelediniz mi ?:
Şimdi ÜCRETSİZ Üye Ol!
Forumumuza kaydınız bulunmamaktadır. Hızlı ve Ücretsiz olarak forumumuza aşağıdaki formu doldurarak üye olabilirsiniz.

Üye adı: Şifre: Şifre Onay: E-posta: E-posta Onay:
Doğum Gününüz:     Cinsiyet:    
Grafik Doğrulaması
  Forum kuralları kabul ediyorum. 


Silverlight 2.0 ve Socket Programlama Mucizesi

Silverlight, Expression, LinQ, Ajax, JavaScript


Cevapla
 
LinkBack Seçenekler Stil
Alt 26-05-2008, 01:32   #1
Hayat İşte...

11 Oca 2008
Dünyadan
:
Yaş: 24
Ruh hali:
Ettiği Teşekkür: 1,244
417 Mesajına 625 Kere Teşekkür Edlidi
Tepki Sayısı: 3
23 Mesajına 26 Kere Tepki Gösterildi
 
Tecrübe Puanı: 10000
XiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond reputeXiLoNeN has a reputation beyond repute
XiLoNeN - MSN üzeri Mesaj gönder
 XiLoNeN isimli Üye şimdilik offline konumundadırOffline  



Standart Silverlight 2.0 ve Socket Programlama Mucizesi


Socket programlama Silverlight çıktığından beri biz yazılım geliştiricilerin en büyük hayali ve bu hayal gerçek oluyor. Silverlight 2.0 Beta 1 ile beraber Socket Programlama karşımızda. Yani artık istemci ile sunucu arasında TCP/IP ile haberleşmek mümkün. Tabi belirli kurallar var; bu kurallardan ilki sunucudaki uygulamanın istemci uygulamanın yüklendiği web sitesi ile aynı konumda olması. Yani sunucu uygulamanızın web siteniz ahmet.com ise ahmet.com'un reverse DNS Look-Up ile bakıldığında çıkan IP adresine sahip sunucuda bulunması gerekiyor. Bu durumun Silverlight'ın Beta 1 sonrası sürümlerinde policyfile gibi sistemlerde daha esnek hale getirileceği söylentiler arasında fakat baktığımızda şu anki hali ile bile süper bir potansiyel söz konusu.
Peki nedir bunun avantajı?
Diyorum ya, hayalimizdi diye, peken neden? Web sitelerinde güncel bilgi göstermek her zamanki en büyük derttir. Bunu yapabilmek için çok eskilere döndüğümüzde bazı meta tagları ile belirli aralıkla sayfanın refresh atmasını sağladığımız günler bile olurdu. IFRAME vs nin gelmesi ile en azından bunu sayfada kısmi bölümlerde uygulayabilir hale geldik. Sonrasında AJAX geldi ve çok daha sinsi bir şekilde kullanıcı farkında olmadan belirli aralıklarla sunucudan yeni veri talebinde bulunarak sayfa değişmeden yeni içeriği gösterebildik. Oysa hep bizi rahatsız eden bir nokta vardı, o da şu; sürekli istemciden sunucuya bağlanarak bir veri değişikliğinin olup olmadığını kontrol etmek durumunda kalıyorduk. Sunucuya "Yeni birşey var mı?" diye dakikada bir soruyor ve çoğunda da hüsran ile geri dönüyordu. Keşke sunucu bize bir "Alo" diyebilse ve değişiklik olduğunda istemciyi haberdar edebilse? Teknik olarak bu güvenlik sebepleri nedeniyle zaten mümkün değil çünkü bir istemci bilgisayara dışarıdan içeriye bağlantı kuramazsınız (kuramamanız gerekir). Peki nasıl oluyor da Socket Programming bunu aşıyor? Aslında aşmıyor, yine istemci sunucuya bağlanıyor fakat söz konusu bağlantı TCP bazında olduğu herhangi bir trafiğe neden olmadan sürekli açık tutulabiliyor. Durum böyle olunca sunucu kendisine bağlı istemciye istediğinde söz konusu bağlantı üzerinden rahatlıkla ulaşabiliyor.
Sunucu tarafından işe başlayalım.
İlk olarak sunucudaki programımızı hazırlayalım. Söz konusu program kendisine gelen tüm istekleri karşılayarak gerektiğinde istemcilere veri gönderecek. Bizim programımız içerisinde bir TextBox bulunacak ve kutu içerisine metin yazıldıkça kendisine bağlı tüm istemcilere bu metin sürekli gönderilecek.
HTML-Kodu:
  Dim Baglilar As New System.Collections.Generic.List(Of System.IO.StreamWriter)
     Dim yeniTR As System.Threading.Thread
     Dim TCPBaglantilari As New System.Threading.ManualResetEvent(True)
     Dim Dinleyici As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 4530)
İlk olarak global değişkenlerimizi tanımlıyoruz. Bunlardan ilki olan Baglilar değişkeni sunucuya bağlı olan istemcilere veri gönderecek olan StreamWriter nesnelerini bir listesini taşıyacak. Böylece istediğimizde bu liste içerisinde gezerek tüm bağlı olan istemcilere veri gönderebileceğiz. yeniTR adındaki değişkenimizi yeni bir Threat yaratmak ve her yerden kendisine ulaşabilmek için kullanacağız. TCPBaglantilari değişkenimiz var olan Threat'ın blocklanması ve tüm event-larının sıfırlanması için kullanılacak. Dinleyici adındaki değişkenimizi ise tüm istemcileri dinleyecek olan ve gelen bağlantıları algılayacak olan TCPListener nesnemizin ta kendisi. Gördüğünüz gibi bu nesneyi tanımlarken iki parametre aktarmışız. Bunlardan ilki herhangi bir IP adresi üzerinden bu uygulamaya bağlanılabileceği anlamına gelirken diğer ise sadece 4530 portu üzerinden bağlantı yapılabileceği anlamına geliyor. Silverlight 2.0 Beta 1 şu anda 4502-4532 aralığındaki portları kullanabiliyor.
HTML-Kodu:
Private Sub btn_Basla_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Basla.Click
         yeniTR = New System.Threading.Thread(AddressOf Bekle)
         yeniTR.Start()
     End Sub
Uygulamamızdaki düğmeye basıldığında dinleme işlemini başlatmak üzere yeni bir Thread yaratıyoruz. Söz konusu Thread Bekle Sub'ına bağlı. Yarattığımız thread'i hemen başlatıp yolumuza devam edelim.
HTML-Kodu:
   Sub Bekle()
         Dinleyici.Start()
         While True
             TCPBaglantilari.Reset()
             Dinleyici.BeginAcceptTcpClient(New System.AsyncCallback(AddressOf BaglantiGeliyor), Nothing)
             TCPBaglantilari.WaitOne()
         End While
     End Sub
Yeni Thread içerisinde hemen Dinleyici nesnemizi başlatıyoruz ve kısır bir döngüye giriyoruz. Sürekli olarak elimizdeki Threadi sıfırlayarak Dinleyici'nin BeginAcceptTcpClient metodu ile istemciden bir bağlantı geleceğini belirterek WaitOne metodu ile de bekliyoruz. Eğer burada bir bağlantı gelir ve başarılı veya başarısız şekilde sonuçlanırsa bu döngü başa gelerek tekrar yeni bir bağlantı bekleyecek. BeginAcceptTcpClient içerisinde parametre olarak verdiğimiz BaglantiGeliyor event-handları herhangi bir bağlantı geldiğinde çalıştırılacak.
HTML-Kodu:
 Private Sub BaglantiGeliyor(ByVal ar As System.IAsyncResult)
         TCPBaglantilari.Set()
         Dim Musteri As System.Net.Sockets.TcpClient = Dinleyici.EndAcceptTcpClient(ar)
         If Musteri.Connected Then
             Dim yazici As New System.IO.StreamWriter(Musteri.GetStream)
             yazici.AutoFlush = True
             Baglilar.Add(yazici)
             yazici.Write("Bağlandınız.")
         End If
     End Sub
Baglanti geldiği anda bekleyen Threat'leri devam ettirmek adına TCPBaglantilari.Set() metodunu çağırıyoruz. Unutmayalım ki programımız aynı anda sadece tek istemcinin bağlantısını authenticate edebilir, yani diğerleri bir önceki istemci bağlanıp bağlantısını oluşturana kadar bekleyecektir. Bu noktada artık istemci bağlantı kurma işlemini tamamladığı için diğerlerine yol veriyoruz. Musteri adında bir TCPClient yarattıktan sonra Dinleyici'nin EndAcceptTcpClient metodu ile args parametresi üzerinden gelen Request'i alıyoruz. Eğer Musteri bağlı ise, yani Connected ise artık sıra geldi ona veri göndermeye. Musteri'nin yani TCPClient'ın Stream'ini alarak bundan bir StreamWriter oluşturuyoruz. Bu Stream üzerinden artık istemciye istediğimiz veriyi gönderebiliriz.
Unutmayın ki uygulamamızda bir TextBox vardı ve içerisine birşey yazıldığında tüm bağlı kullanıcılara gönderecektik. Bunun için sonra kullanabilmek adına elimizdeki canlı Stream'leri saklamak sorundayız. Global olarak tanımladığımız Baglilar adında Generic.List'e elimizdeki Stream'i aktarıyoruz. Bu arada kullanıcıya "Bağlandınız" diye de bir metin gönderiyoruz.
HTML-Kodu:
 Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
         For Each x As System.IO.StreamWriter In Baglilar
             x.Write(TextBox1.Text)
         Next
     End Sub
Artık TextBox içerisinde değişiklik olunca bunu istemcilere göndermek çok kolay. Basit bir şekilde Generic.List içerisinde gezin ve her Stream'e elinizdeki veriyi gönderin.
İstemci tarafında neler olacak?
Silverlight tarafında çok basit görsellikte bir uygulamamız olacak. Sadece bir TextBlock! Uygulama tarayıcı içerisinde ilk açıldığında sunucuya bağlanacak ve gelen veriyi sürekli olarak söz konusu TextBlock içerisinde gösterecek.
HTML-Kodu:
<UserControl x:Class="SocketsClient.Page"
     xmlns="http://schemas.microsoft.com/client/2007"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Width="400" Height="300">
   <Grid x:Name="LayoutRoot" Background="White">
     <TextBlock Margin="42,46,37,125" Text="TextBlock" TextWrapping="Wrap" x:Name="Metin"/>
   </Grid>
 </UserControl>
XAML kodumuzu yukarıdaki şekilde düzenledikten sonra hemen code-behind dosyasına geçerek bağlantı kodlarımızı yazmaya başlayalım.
HTML-Kodu:
   Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
         Dim Hat As New System.Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Stream, Net.Sockets.ProtocolType.Tcp)
         Dim Args As New System.Net.Sockets.SocketAsyncEventArgs
  
         Args.UserToken = Hat
         Args.RemoteEndPoint = New System.Net.DnsEndPoint("localhost", 4530)
         AddHandler Args.Completed, AddressOf Baglandi
         Hat.ConnectAsync(Args)
     End Sub
Silverlight uygulaması ilk yüklendiğinde hemen bir Socket bağlantısı yaratmak için System.Net.Sockets.Socket üzerinden ilerliyoruz. Hat adındaki değişkenimizi yaratırken verdiğimiz parametrelerden ilki olan InterNetwork bizim bağlantı için IPv4 kullanacağımızı ve ikinci parametre de TCP kullanacağımızı belirtiyor. Asenkron bir çalışma yapısı için bir de SocketAsyncEventArgs nesnesi yaratarak söz konusu nesneyi Hat adındaki Socket'imize bağlıyoruz. Args'ın RemoteEndPoint özelliği istemcinin bağlanacağı sunucunun adresini ve port bilgisini içeriyor. Bağlanti oluşturulduğunda elimizdeki SocketAsyncEventArgs nesnesi olan Args'ın Completed event-handları çalışacağı için ona da dinamik bir event-handler olarak Baglandi metodunu atıyoruz. Son olarak ConnectAsync diyerek Socket değişkenimizin eldeki SocketAsyncEventArgs ile sunucuya bağlanmasını sağlıyoruz.
HTML-Kodu:
  Private Sub Baglandi(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs)
         Dim Gelen(1024) As Byte
         e.SetBuffer(Gelen, 0, Gelen.Length)
         RemoveHandler e.Completed, AddressOf Baglandi
         AddHandler e.Completed, AddressOf Geldi
         Dim Baglanti As System.Net.Sockets.Socket = CType(e.UserToken, System.Net.Sockets.Socket)
         Baglanti.ReceiveAsync(e)
     End Sub
Artık istemci sunucuya bağlandığında göre sıra geldi karşı taraftan yeri geldiğinde veriyi almaya. Hatta ilk bağlantı esnasında hatırlarsanız bizim sunucumuzun "Bağlandınız" diye bir metin gönderiyordu. Gelen veriyi alabilmek için ve sürekli gelen veriyi dinlemek için eldeki Socket'i alarak sürekli dinleme durumunda olmamız şart. Kodumuzdaki event-handler içerisinde e parametresi aslında bizim bir önceki adımda tanımladığımız Args adaınki SocketAsyncEventArgs'ın ta kendisi. SetBuffer ile veriyi önbelleklemek için kullanacağımız ayarları da bir Byte değişkeni üzerinden aktardıktan sonra ilginç bir şekilde elimizdeki event-handlerları değiştiyoruz. Bundan sonra Args'ın Compeleted durumu yeni bir bağlantı oluştuğunu değil yeni veri geldiğini bildireceği için farklı bir event-handlerı bağlamamız gerekiyor. Baglandi adındaki metodumuzla Args'ın ilişkisi keserek Geldi adında farklı bir metoda bağlıyoruz. Son olarak Page.Load'a atadığımız ve e.UserToken üzerinden alabileceğimiz ana Socket değişkenimizi de yakalayarak ReceiveAsync metodu ile veri alımını başlatıyoruz.
Geldi ve Baglandi metodları aslında Silverlight içerisinde ayrı bir Thread içerisinde çalışıyor. Bu nedenle tüm bu işlemler yapılırken kullanıcının uygulama ile olan interaktivitesi kesinlikle kesilmiyor. Tabi ayrı bir Thread gibi davranıyor olmasını dezavantajı ise birazdan karşımıza çıkacak. Kısır döngü içerisinde süreki ReceiveAsync ile sunucuyu dinlerken istemci tarafında görsel arayüzde değişiklik yapamayacağız. Bu da bizim sunucudan veri alabilmemizi fakat ekranda göstermememize neden olacak. Tabi demokrasilerde çare tükenmez...
HTML-Kodu:
  Delegate Sub MyDelegate(ByVal myArg2 As String)
  
     Sub GelGel(ByVal x As String)
         Metin.Text = x
     End Sub
İlk olarak bir Delegate tanımlayacağız, söz konusu delegemiz sadece bir parametre alacak. Ayrıca bir de Sub yaratıyoruz. Aynı şekilde Sub'da bir metin parametresi alıyor ve bizim uygulamamızda adı Metin olan TextBlock içerisine yerleştiriyoruz. İşte bu yapı ile biraz önce bahsettiğimiz sorundan kurtuluyor olacağız.
HTML-Kodu:
  Private Sub Geldi(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs)
         Dim Gelen As String = System.Text.Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred)
         Me.Dispatcher.BeginInvoke(New MyDelegate(AddressOf GelGel), New String() {Gelen})
         Dim Baglanti As System.Net.Sockets.Socket = CType(e.UserToken, System.Net.Sockets.Socket)
         Baglanti.ReceiveAsync(e)
     End Sub
Aslında sunucudan gelen veriyi almak çok kolay. Kodumuz içerisindeki ilk satır bu işi hallediyoruz. Esas mesele veriyi aldıktan sonra sahnede göstermek. Dispatcher nesnesi belki de Silverlight içerisinde en ilginç yapılardan biri; Dispatcher ile mevcut Thread'i yakalayarak BeginInvoke ile başka bir metod çalıştırıyoruz. Çalıştıracağımız metodu sunucudan gelen veriyi parametre olarak vereceğiz ve söz konusu metod (GelGel) bu veriyi alarak sahnedeki Metin adındaki TextBlock içerisine yerleştirecek. BeginInvoke ile ilgili işimizi de tamamladıktan sonra artık tekrar sunucunun dinlenmeye başlanması için elimizdeki Socket'i yakalayarak ReciveAsync metodunu çalıştırıyoruz. Bu sistem böyle sonsuza tek dönecek ve sunucudan gelen veri sürekli olarak tüm istemcilerde anında gösterilecek.
Son olarak hem istemci hem de sunucu uygulamanın tam kodunu sizlerle paylaşmak istiyorum.
[İstemci: Silverlight uygulaması]
HTML-Kodu:
Partial Public Class Page
     Inherits UserControl
  
     Public Sub New()
         InitializeComponent()
     End Sub
  
     Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
         Dim Hat As New System.Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Stream, Net.Sockets.ProtocolType.Tcp)
         Dim Args As New System.Net.Sockets.SocketAsyncEventArgs
  
         Args.UserToken = Hat
         Args.RemoteEndPoint = New System.Net.DnsEndPoint("localhost", 4530)
         AddHandler Args.Completed, AddressOf Baglandi
         Hat.ConnectAsync(Args)
     End Sub
  
     Private Sub Baglandi(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs)
         Dim Gelen(1024) As Byte
         e.SetBuffer(Gelen, 0, Gelen.Length)
         RemoveHandler e.Completed, AddressOf Baglandi
         AddHandler e.Completed, AddressOf Geldi
         Dim Baglanti As System.Net.Sockets.Socket = CType(e.UserToken, System.Net.Sockets.Socket)
         Baglanti.ReceiveAsync(e)
     End Sub
  
     Private Sub Geldi(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs)
         Dim Gelen As String = System.Text.Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred)
         Me.Dispatcher.BeginInvoke(New MyDelegate(AddressOf GelGel), New String() {Gelen})
         Dim Baglanti As System.Net.Sockets.Socket = CType(e.UserToken, System.Net.Sockets.Socket)
         Baglanti.ReceiveAsync(e)
     End Sub
  
     Delegate Sub MyDelegate(ByVal myArg2 As String)
  
     Sub GelGel(ByVal x As String)
         Metin.Text = x
     End Sub
  
 End Class
[Sunucu: Winforms Uygulaması]
HTML-Kodu:
Public Class Form1
  
     Dim Baglilar As New System.Collections.Generic.List(Of System.IO.StreamWriter)
     Dim yeniTR As System.Threading.Thread
     Dim TCPBaglantilari As New System.Threading.ManualResetEvent(True)
     Dim Dinleyici As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 4530)
  
     Private Sub btn_Basla_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Basla.Click
         'İzin Verilen Port Aralığı 4502-4532
         yeniTR = New System.Threading.Thread(AddressOf Bekle)
         yeniTR.Start()
     End Sub
  
     Sub Bekle()
         Dinleyici.Start()
         While True
             TCPBaglantilari.Reset()
             Dinleyici.BeginAcceptTcpClient(New System.AsyncCallback(AddressOf BaglantiGeliyor), Nothing)
             TCPBaglantilari.WaitOne()
         End While
     End Sub
  
     Private Sub BaglantiGeliyor(ByVal ar As System.IAsyncResult)
         TCPBaglantilari.Set()
         Dim Musteri As System.Net.Sockets.TcpClient = Dinleyici.EndAcceptTcpClient(ar)
         If Musteri.Connected Then
             Dim yazici As New System.IO.StreamWriter(Musteri.GetStream)
             yazici.AutoFlush = True
             Baglilar.Add(yazici)
             yazici.Write("Bağlandınız.")
         End If
  
     End Sub
  
     Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
         For Each x As System.IO.StreamWriter In Baglilar
             x.Write(TextBox1.Text)
         Next
     End Sub
 End Class
Hepinize kolay gelsin.



Kaynak: [Üye Olmadan Linkleri Göremezsiniz. Üye Olmak için TIKLAYIN...]
İmzamDisplay Signature
Digg this Post!Add Post to del.icio.usFurl this Post!Facebookta paylaş!!Google'da PaylaşYahoo'da PaylaşLive'da Paylaş!
Alıntı ile Cevapla

Cevapla

Bookmarks

Etiketler
mucize, silverlight, silverlight 2.0, socket programlama


Konuyu Toplam 1 Üye okuyor. (0 Kayıtlı üye ve 1 Misafir)
 
Seçenekler
Stil

Yetkileriniz
Konu Acma Yetkiniz Yok
Cevap Yazma Yetkiniz Yok
Eklenti Yükleme Yetkiniz Yok
Mesajınızı Değiştirme Yetkiniz Yok

BB code is Açık
Smileler Açık
[IMG] Kodları Açık
HTML-Kodu Kapalı
Trackbacks are Açık
Pingbacks are Açık
Refbacks are Açık
Hizli Erisim

Benzer Konular
Konu Konuyu Başlatan Forum Cevaplar Son Mesaj
Sayfa içi Silverlight uygulamaları XiLoNeN Silverlight, Expression, LinQ, Ajax, JavaScript 0 26-05-2008 15:27
ASP.NET ile Silverlight XAML Yaratalım XiLoNeN Silverlight, Expression, LinQ, Ajax, JavaScript 0 26-05-2008 15:24
Silverlight 1.0 ve HTML Kontrolleri XiLoNeN Silverlight, Expression, LinQ, Ajax, JavaScript 0 26-05-2008 15:22
Peygamberimiz (sav)'in En Büyük Mucizesi: Kuran-i Kerim akasya Dini Paylaşım 0 16-05-2008 11:53


WEZ Format +3. Şuan Saat: 21:17.

Powered by vBulletin® Version 3.7.3
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.

Yahoo bot last visit powered by MyPagerank.Net Powered by  MyPagerank.Net Join 4Shared Now! Msn bot last visit powered by MyPagerank.Net cvZip.com - Bilişim Sektörünün Yeni Kariyer Platformu

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193