Network Programlama

Giriş:

Yeni bir yazı yazmanın zamanı geçiyordu bile. Bu yazımızda istemci tarafını yazalım. İstemci için daha az bilgiye ihtiyacımız var. Bir önceki yazımızda (Soket Programlama – 2) sunucuya bağlanmak için telnet veya netcat programını kullanmıştık. Bu yazımızda kendi istemcimizle sunucumuza bağlanacağız. Sunucumuzun tek mesajlık gücü vardı hatırlarsanız.

İstemci Yapalım:

Bu işlem için sunucuya bağlanmak için bir sokete ihtiyaç duymaktayız. Soket elde etme işlemi sunucu soketteki gibidir:
import socket
s =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((”, 9000))
data =s.recv(1024)
print data

Bu kod tahmin edilebileceği gibi bir adet TCP protokolünü kullanacak soket nesnesi oluşturur. “connect” metoduna geçilen 9000 değeri sunucunun hangi porttan bağlantı beklediğidir. Bağlantı sağlandıktan sonra “recv” metodu 1024’lük bir buffer’a veriyi saklayacaktır. Veri elde edildikten sonra ekrana yazdırılacak ve program sonlanacaktır.

Herşey bu kadar basit mi?

Malesef aslında soket programlama göründüğünden biraz daha karışık olabilir. “send” ve “recv” metodları buffered bir okuma ve yazma yapmaktadırlar. Yani “send” ile gönderilen verinin tamamı gönderilmeyebilir. Hani bir TCP stack var demiştik. İşte TCP stack’teki bufferlar dolmadan send işlemi (verinin gerçekten kabloya elektriksel olarak iletilmesi) gerçekleşmemektedir. Benzer şekilde “recv” metodu da buffer dolmadan okumayı sonlandırmamaktadır. Yani siz 1024’lük bir veri okuyacaksınız ancak 100’lük bir veri okuyabilirsiniz.

Biraz daha detaylandıralım. İlk olarak, buffered I/O meselesi biraz uzun hikaye. Kısaca özetleyecek olursak, C’deki gets() fonksiyonu da bu şekilde çalışmaktadır diyebiliriz. Buffer’ın tazelenmesi veya boşaltılmasını bazı özel karakterler (örneğin ‘\n’) tetikleyebilmektedir. Bizim ilk eğitimlerimizdeki örneklerimizde ‘n’ olmamasına rağmen recv nasıl bizim sunucumuzun gönderdiği yazıyı aldı peki? Burada da özel bir durum var. Herhangi bir soketin bağlantısı kapatıldığında (close()) TCP stack’taki bufferlardaki veriler buffer dolmamış olsa bile iletilir. Bizim sunucunun mesajını görebilmemizin sebebi buydu. Bunu gerçekten görmek için ve buffered I/O’nun etkisini görmek için sunucu kodumuzu biraz değiştirelim:
import socket
import time

s =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((”, 9000))
s.listen(5)
conn, addr =s.accept()

conn.send(‘Merhaba\n’)
for i in range(100):
# dikkat ediniz bu send metodunda \n yok
conn.send(‘Merhaba ‘)

time.sleep(100)

conn.close()
Kodumuzda ilk göndermeyi (send()) ‘\n’ ile yaptıktan sonra, 100 kere daha aynı mesajı sokete yazmaktayız. Bu işlemin ardından 100 saniye daha bekliyoruz ki sokete yazma kendimizce (!) tamamlansın (buffered I/O’nun etkisini göstermek için sleep bulunuyor).

Benzer şekilde istemciyi de düzenleyelim.
import socket

s =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((”, 9000))

fori in range(100):
data =s.recv(1024)
print data
Sunucu yazılımımızı çalıştırdıktan sonra istemci yazılımımızı çalıştıralım. Evet recv fonksiyonunun farklı büyüklüklerde okuduğunu (Merhaba yazısının aynı satırda farklı sayılarda olduğuna dikkat ediniz) göreceğiz.

Sorular?

1. Bu örneği sunucu yazılımında conn.close() satırını kaldırarak tekrar çalıştırınız. Nasıl bir fark gördünüz, ne bekliyordunuz?

2. Aynı örneği istemcide 100 kere okuma yapmadan sadece tek okuma yaparak tekrarlayınız. Nasıl bir fark gördünüz, ne bekliyordunuz, niçin bu sonucu aldınız?

2. sorunun cevabı biraz birincinin cevabını verdiğinden bu soruyu cevaplayalım. Bir tek “Merhaba” satırı göreceksiniz, çünkü send(“Merhaba\n”) ‘\n’ barındırdığından istemcideki recv tarafından okunur ve recv fonksiyonu istemcide döner (bloklama yapmaz, sonlanır) . Malesef send tarafından diğer Merhaba’lar yazılmış olmasına rağmen istemci sonlandığından bu veriler istemci tarafından hiç işlenmez. Bu daha ilginç bir problemi göz önüne koymaktadır. Eğer istemci sonlanmış ve sunucu send yapmışsa, ki bu durumda kapanmış bir sokete yazma yapmaktadır, BROKEN_PIPE sinyalini sunucu neden almamıştır? Hatta, istemci kodunu soketi kapatacak şekilde aşağıdaki gibi düzeltiniz ve denemeyi tekrar yapınız, bu durumda “Connection reset” uyarısı göreceksiniz. Farkı yaratan sebebi bulmaya çalışınız.

import socket

s =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((”, 9000))
data =s.recv(1024)
print data
s.close()

Sağlıcakla kalınız

Leave a Reply

Your email address will not be published. Required fields are marked *