11. Python'un ağ işlevleri
Şimdi, TCP / IP (Transfer Control Protocol / Internet Protocol) programlama yapmamızı sağlayan Python ağ işlevlerini ele alacağız.
![]() |
11.1. İnternetteki bir makinenin adını veya IP adresini alma
import sys, socket
#------------------------------------------------
def getIPandName(nomMachine):
#nomMachine : adresini öğrenmek istediğimiz makinenin adı IP
# nomMachine-->IP adresi
try:
ip=socket.gethostbyname(nomMachine)
print "ip[%s]=%s" % (nomMachine,ip)
except socket.error, erreur:
print "ip[%s]=%s" % (nomMachine,erreur)
return
# adres IP --> nomMachine
try:
name=socket.gethostbyaddr(ip)
print "name[%s]=%s" % (ip,name[0])
except socket.error, erreur:
print "name[%s]=%s" % (ip,erreur)
return
# ---------------------------------------- ana
# sabitler
HOTES=["istia.univ-angers.fr","www.univ-angers.fr","www.ibm.com","localhost","","xx"]
# HOTES makinelerinin IP adresleri
for i in range(len(HOTES)):
getIPandName(HOTES[i])
# son
sys.exit()
Notlar:
-
- satır: Python ağ işlevleri, socket modülünde kapsüllenmiştir.
11.2. Bir web istemcisi
Bir web sitesinin "index" paragrafının içeriğini alan bir komut dosyası.
import sys,socket
#-----------------------------------------------------------------------
def getIndex(site):
# URL sitesini okur ve site.html dosyasına kaydeder
# başlangıçta hata yok
erreur=""
# site.html dosyasının oluşturulması
try:
html=open("%s.html" % (site),"w")
except IOError, erreur:
pass
# hata mı?
if erreur:
return "Erreur (%s) lors de la création du fichier %s.html" % (erreur,site)
# sitenin 80 numaralı bağlantı noktasında bağlantı açılıyor
try:
connexion=socket.create_connection((site,80))
except socket.error, erreur:
pass
# hata durumunda geri dönüş
if erreur:
return "Echec de la connexion au site (%s,80) : %s" % (site,erreur)
# bağlantı, iki yönlü bir iletişim akışını temsil eder
# istemci (bu program) ile erişilen web sunucusu arasında
# bu kanal, komut ve bilgi alışverişi için kullanılır
# iletişim protokolü HTTP'tir
# istemci, URL / dosyasını istemek için get komutunu gönderir
# "get" komutunun sözdizimi: URL HTTP/1.0
# HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
connexion.send("GET / HTTP/1.0\n\n")
# sunucu şimdi bağlantı kanalı üzerinden yanıt verecektir. Tüm
# verilerini gönderecek ve ardından kanalı kapatacaktır. İstemci, bağlantı kanalından gelen her şeyi okur
# kanal kapanana kadar gelen tüm verileri okur
ligne=connexion.recv(1000)
while(ligne):
html.write(ligne)
ligne=connexion.recv(1000)
# İstemci de sırayla bağlantıyı kapatır
connexion.close()
# html dosyasının kapatılması
html.close()
# geri dönüş
return "Transfert reussi du paragraphe index du site %s" % (site)
# --------------------------------------------- ana program
# URL'ten HTML metnini al
# web siteleri listesi
SITES=("istia.univ-angers.fr","www.univ-angers.fr","www.ibm.com","xx")
# SITES tablosundaki sitelerin ana sayfalarını okuma
for i in range(len(SITES)):
# SITES[i] sitesinin ana sayfasını okuma
resultat=getIndex(SITES[i])
# sonuç görüntüleme
print resultat
# son
sys.exit()
Notlar:
-
- satır: Ana sayfasını almak istediğimiz web sitelerinin URL listesi. Bu liste, [nomsite.html] adlı metin dosyasında saklanır;
-
- satır: getIndex işlevi bu işi yapar;
-
- satır: getIndex işlevi;
- satır 20: create_connection((site,port)) yöntemi, site makinesinin port numaralı bağlantı noktasında çalışan bir TCP / IP hizmeti ile bağlantı kurulmasını sağlar;
-
- satır: send yöntemi, bir TCP / IP bağlantısı üzerinden veri gönderilmesini sağlar. Burada gönderilen metin verisidir. Bu metin, HTTP protokolüne (HyperText Aktarım Protokolü) uygundur;
-
- satır: recv yöntemi, TCP / IP bağlantısı üzerinden veri alınmasını sağlar. Burada, web sunucusunun yanıtı 1000 karakterlik bloklar halinde okunur ve [nomsite.html] metin dosyasına kaydedilir.
Transfert reussi du paragraphe index du site istia.univ-angers.fr
Transfert reussi du paragraphe index du site www.univ-angers.fr
Transfert reussi du paragraphe index du site www.ibm.com
Echec de la connexion au site (xx,80) : [Errno 11001] getaddrinfo failed
[www.ibm.com] sitesi için alınan dosya:
- 1-11. satırlar, sunucunun yanıtındaki HTTP başlıklarıdır;
-
- satır: sunucu, istemciden 8. satırda belirtilen URL adresine yönlendirilmesini ister;
-
- satır: yanıtın tarihi ve saati;
-
- satır: web sunucusunun kimliği;
-
- satır: sunucu tarafından gönderilen içerik. Burada, 13. satırda başlayan bir HTML sayfası;
-
- satır: HTTP başlıklarını sonlandıran boş satır;
- 13-19. satırlar: web sunucusu tarafından gönderilen HTML sayfası.
11.3. Bir SMTP istemcisi
TCP / IP protokolleri arasında, SMTP (SendMail Aktarım Protokolü), mesaj gönderme hizmetinin iletişim protokolüdür.
# -*- coding=utf-8 -*-
import sys,socket
#-----------------------------------------------------------------------
def getInfos(fichier):
# [fichier] metin dosyasından alınan bilgileri (smtp, gönderen, alıcı, mesaj) döndürür
# 1. satır: smtp, gönderen, alıcı
# sonraki satırlar: mesaj metni
# [fichier] dosyasının açılması
erreur=""
try:
infos=open(fichier,"r")
except IOError, erreur:
return ("Le fichier %s n'a pu etre ouvert en lecture : %s" % (fichier, erreur))
# 1. satırın okunması
ligne=infos.readline()
# satır sonu işaretinin silinmesi
ligne=cutNewLineChar(ligne)
# smtp, gönderen, alıcı alanlarının alınması
champs=ligne.split(",")
# alan sayısı doğru mu?
if len(champs)!=3 :
return ("La ligne 1 du fichier %s (serveur smtp, expediteur, destinataire) a un nombre de champs incorrect" % (fichier))
# Toplanan bilgilerin "işlenmesi"
# 3 alanın her birinden, yararlı bilginin önündeki veya arkasındaki "boşluklar" kaldırılır
for i in range(3):
champs[i]=champs[i].strip()
# alanların alınması
(smtpServer,expediteur,destinataire)=champs
message=""
# mesajın geri kalan kısmının okunması
ligne=infos.readline()
while ligne!='':
message+=ligne
ligne=infos.readline()
infos.close()
# geri dönüş
return ("",smtpServer,expediteur,destinataire,message)
#-----------------------------------------------------------------------
def sendmail(smtpServer,expediteur,destinataire,message,verbose):
# mesajı, gönderen adına smtpserver SMTP sunucusuna gönderir
# alıcıya. verbose=True ise, istemci-sunucu iletişimini izler
# istemcinin adı alınır
try:
client=socket.gethostbyaddr(socket.gethostbyname("localhost"))[0]
except socket.error, erreur:
return "Erreur IP / Nom du client : %s" % (erreur)
# smtpServer'in 25 numaralı bağlantı noktasında bir bağlantı açılır
try:
connexion=socket.create_connection((smtpServer,25))
except socket.error, erreur:
return "Echec de la connexion au site (%s,25) : %s" % (smtpServer,erreur)
# bağlantı, iki yönlü bir iletişim akışını temsil eder
# istemci (bu program) ile bağlanılan SMTP sunucusu arasında
# bu kanal, komut ve bilgi alışverişi için kullanılır
# bağlantı kurulduktan sonra sunucu, okunan bir karşılama mesajı gönderir
erreur=sendCommand(connexion,"",verbose,1)
if(erreur) :
connexion.close()
return erreur
# ehlo komutu:
erreur=sendCommand(connexion,"EHLO %s" % (client),verbose,1)
if erreur :
connexion.close()
return erreur
# "mail from" komutu:
erreur=sendCommand(connexion,"MAIL FROM: <%s>" % (expediteur),verbose,1)
if erreur :
connexion.close()
return erreur
# rcpt to komutu:
erreur=sendCommand(connexion,"RCPT TO: <%s>" % (destinataire),verbose,1)
if erreur :
connexion.close()
return erreur
# data komutu
erreur=sendCommand(connexion,"DATA",verbose,1)
if erreur :
connexion.close()
return erreur
# gönderilecek mesajın hazırlanması
# şu satırları içermelidir
# From: gönderen
# Kime: alıcı
# boş satır
# Mesaj
# .
data="From: %s\r\nTo: %s\r\n%s\r\n.\r\n" % (expediteur,destinataire,message)
# mesaj gönderiliyor
erreur=sendCommand(connexion,data,verbose,0)
if erreur :
connexion.close()
return erreur
# çıkış komutu
erreur=sendCommand(connexion,"QUIT",verbose,1)
if erreur :
connexion.close()
return erreur
# son
connexion.close()
return "Message envoye"
# --------------------------------------------------------------------------
def sendCommand(connexion,commande,verbose,withRCLF):
# bağlantı kanalına komut gönder
# verbose=1 ise ayrıntılı mod
# withRCLF=1 ise, komuta RCLF dizisini ekler
# veriler
RCLF="\r\n" if withRCLF else ""
# komut boş değilse komut gönder
if commande:
connexion.send("%s%s" % (commande,RCLF))
# olası yanıt
if verbose:
affiche(commande,1)
# 1000 karakterden kısa yanıtı oku
reponse=connexion.recv(1000)
# olası yankı
if verbose:
affiche(reponse,2)
# hata kodunu al
codeErreur=reponse[0:3]
# sunucu tarafından gönderilen hata mı?
if int(codeErreur) >=500:
return reponse[4:]
# hatasız dönüş
return ""
# --------------------------------------------------------------------------
def affiche(echange,sens):
# değiş tokuşu ekrana mı gösterir?
# eğer sens=1 ise -->değişim'i göster
# eğer sens=2 ise <-- son 2 karakter hariç değiş tokuşu göster RCLF
if sens==1:
print "--> [%s]" % (echange)
return
elif sens==2:
l=len(echange)
print "<-- [%s]" % echange[0:l-2]
return
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# varsa, [ligne]'teki satır sonu işaretini siler
l=len(ligne)
while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
l-=1
return(ligne[0:l])
# ana sayfa ----------------------------------------------------------------
# mesaj göndermeye olanak tanıyan SMTP istemcisi (SendMail Aktarım Protokolü)
# bilgiler, aşağıdaki satırları içeren bir dosyadan alınır: INFOS
# 1. satır: smtp, gönderen, alıcı
# sonraki satırlar: mesaj metni
# gönderen: gönderenin e-posta adresi
# alıcı: alıcının e-posta adresi
# smtp: kullanılacak smtp sunucusunun adı
# iletişim protokolü SMTP istemci-sunucu
# -> istemci, SMTP sunucusunun 25 numaralı bağlantı noktasına bağlanır
# <- sunucu, istemciye bir hoş geldin mesajı gönderir
# -> istemci EHLO komutunu gönderir: makinesinin adı
# <- sunucu OK yanıtını gönderir veya göndermez
# -> istemci "mail from: <gönderen>" komutunu gönderir
# <- sunucu OK yanıtını verir veya vermez
# -> istemci rcpt to: <alıcı> komutunu gönderir
# <- sunucu OK yanıtını verir veya vermez
# -> istemci data komutunu gönderir
# <- sunucu OK ile yanıt verir veya vermez
# -> istemci, mesajının tüm satırlarını gönderir ve tek bir karakter içeren bir satırla bitirir.
# <- sunucu OK yanıtını verir veya vermez
# -> istemci quit komutunu gönderir
# <- sunucu OK yanıtını verir veya vermez
# sunucunun yanıtları xxx metin biçimindedir; burada xxx, 3 basamaklı bir sayıdır. Herhangi bir xxx sayısı >=500
# bir hatayı bildirir. Yanıt, son satır hariç hepsi xxx- ile başlayan birden fazla satır içerebilir
# şu biçimdedir: xxx(boşluk)
# değiş tokuş edilen metin satırları, RC(#13) ve LF(#10) karakterleriyle bitmelidir
# # e-posta gönderim parametreleri
MAIL="mail2.txt"
# e-posta parametreleri alınır
res=getInfos(MAIL)
# hata mı?
if res[0]:
print "%s" % (erreur)
sys.exit()
# ayrıntılı modda e-posta gönderimi
(smtpServer,expediteur,destinataire,message)=res[1:]
print "Envoi du message [%s,%s,%s]" % (smtpServer,expediteur,destinataire)
resultat=sendmail(smtpServer,expediteur,destinataire,message,True)
print "Resultat de l'envoi : %s" % (resultat)
# son
sys.exit()
Notlar:
- Antivirüs programı yüklü bir Windows bilgisayarında, antivirüs programı Python betiğinin bir SMTP sunucusunun 25 numaralı bağlantı noktasına bağlanmasını engelleyebilir. Bu durumda antivirüs programını devre dışı bırakmak gerekir. Örneğin McAfee için şu şekilde işlem yapılabilir:
![]() |
- [1]'te, VirusScan konsolunu etkinleştirin
- [2]'te, [Protection lors de l'accès] hizmetini durdurun
- [3]'te, hizmet durdurulur
infos.txt dosyası:
smtp.univ-angers.fr, serge.tahe@univ-angers.fr , serge.tahe@univ-angers.fr
Subject: test
ligne1
ligne2
ligne3
Ekran sonuçları:
Thunderbird e-posta okuyucusu tarafından okunan mesaj:
![]() |
11.4. İkinci bir SMTP istemcisi
Bu ikinci komut dosyası, önceki komut dosyasıyla aynı işlevi görür, ancak [smtplib] modülünün özelliklerini kullanır.
# -*- coding=utf-8 -*-
import sys,socket, smtplib
#-----------------------------------------------------------------------
def getInfos(fichier):
# metin dosyasından alınan bilgileri (smtp, gönderen, alıcı, mesaj) döndürür [fichier]
# 1. satır: smtp, gönderen, alıcı
# sonraki satırlar: mesaj metni
# [fichier] dosyasının açılması
erreur=""
try:
infos=open(fichier,"r")
except IOError, erreur:
return ("Le fichier %s n'a pu etre ouvert en lecture : %s" % (fichier, erreur))
# 1. satırın okunması
ligne=infos.readline()
# satır sonu işaretinin silinmesi
ligne=cutNewLineChar(ligne)
# smtp, gönderen, alıcı alanlarının alınması
champs=ligne.split(",")
# alan sayısı doğru mu?
if len(champs)!=3 :
return ("La ligne 1 du fichier %s (serveur smtp, expediteur, destinataire) a un nombre de champs incorrect" % (fichier))
# Alınan bilgilerin "işlenmesi" - bilgilerin başındaki veya sonundaki "boşluklar" kaldırılır
for i in range(3):
champs[i]=champs[i].strip()
# alanların alınması
(smtpServer,expediteur,destinataire)=champs
# gönderilecek mesajın okunması
message=""
ligne=infos.readline()
while ligne!='':
message+=ligne
ligne=infos.readline()
infos.close()
# geri dönüş
return ("",smtpServer,expediteur,destinataire,message)
#-----------------------------------------------------------------------
def sendmail(smtpServer,expediteur,destinataire,message,verbose):
# mesajı, gönderen adına smtpserver SMTP sunucusuna gönderir
# alıcıya. verbose=True ise, istemci-sunucu iletişimini izler
# smtplib kütüphanesi kullanılır
try:
server = smtplib.SMTP(smtpServer)
if verbose:
server.set_debuglevel(1)
server.sendmail(expediteur, destinataire, message)
server.quit()
except Exception, erreur:
return "Erreur envoi du message : %s" % (erreur)
# son
return "Message envoye"
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# varsa, [ligne] satır sonu işaretini siler
l=len(ligne)
while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
l-=1
return(ligne[0:l])
# ana ----------------------------------------------------------------
# mesaj göndermeye olanak tanıyan SMTP istemcisi (SendMail Aktarım Protokolü)
# bilgiler, aşağıdaki satırları içeren bir dosyadan alınır: INFOS
# 1. satır: smtp, gönderen, alıcı
# sonraki satırlar: mesaj metni
# gönderen: gönderenin e-posta adresi
# alıcı: alıcının e-posta adresi
# smtp: kullanılacak smtp sunucusunun adı
# iletişim protokolü SMTP istemci-sunucu
# -> istemci, SMTP sunucusunun 25 numaralı bağlantı noktasına bağlanır
# <- sunucu, istemciye bir hoş geldin mesajı gönderir
# -> istemci EHLO komutunu gönderir: makinesinin adı
# <- sunucu OK yanıtını gönderir veya göndermez
# -> istemci "mail from: <gönderen>" komutunu gönderir
# <- sunucu OK yanıtını verir veya vermez
# -> istemci rcpt to: <alıcı> komutunu gönderir
# <- sunucu OK yanıtını verir veya vermez
# -> istemci data komutunu gönderir
# <- sunucu OK ile yanıt verir veya vermez
# -> istemci, mesajının tüm satırlarını gönderir ve tek bir karakter içeren bir satırla bitirir.
# <- sunucu OK yanıtını verir veya vermez
# -> istemci quit komutunu gönderir
# <- sunucu OK yanıtını verir veya vermez
# sunucunun yanıtları xxx metin biçimindedir; burada xxx, 3 basamaklı bir sayıdır. Herhangi bir xxx sayısı >=500
# bir hatayı bildirir. Yanıt, son satır hariç hepsi xxx- ile başlayan birden fazla satır içerebilir
# şu biçimdedir: xxx(boşluk)
# değiş tokuş edilen metin satırları, RC(#13) ve LF(#10) karakterleriyle bitmelidir
# # e-posta gönderim parametreleri
MAIL="mail2.txt"
# e-posta parametreleri alınır
res=getInfos(MAIL)
# hata mı?
if res[0]:
print "%s" % (erreur)
sys.exit()
# ayrıntılı modda e-posta gönderimi
(smtpServer,expediteur,destinataire,message)=res[1:]
print "Envoi du message [%s,%s,%s]" % (smtpServer,expediteur,destinataire)
resultat=sendmail(smtpServer,expediteur,destinataire,message,True)
print "Resultat de l'envoi : %s" % (resultat)
# son
sys.exit()
Notlar:
- Bu komut dosyası, sendmail işlevi hariç, öncekiyle aynıdır. Bu işlev artık [smtplib] modülünün özelliklerini kullanmaktadır (3. satır).
mail2.txt dosyası:
smtp.univ-angers.fr, serge.tahe@univ-angers.fr , serge.tahe@univ-angers.fr
From: serge.tahe@univ-angers.fr
To: serge.tahe@univ-angers.fr
Subject: test
ligne1
ligne2
ligne3
Ekran sonuçları:
11.5. Echo istemcisi / sunucusu
Bir yankı hizmeti oluşturulur. Sunucu, istemciden aldığı tüm metin satırlarını büyük harflerle geri gönderir. Hizmet, iş parçacıkları sayesinde aynı anda birden fazla istemciye hizmet verebilir.
# -*- coding=utf-8 -*-
# Windows'ta genel TCP sunucusu
# başlık dosyalarının yüklenmesi
import re,sys,SocketServer,threading
# çok iş parçacıklı TCP sunucusu
class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):
def handle(self):
# çalışan iş parçacığı
cur_thread = threading.currentThread()
# istemci verileri
self.data="on"
# boş dizede durma
while self.data:
# istemciden gelen metin satırları readline yöntemi ile okunur
self.data =self.rfile.readline().strip()
# konsol izleme
print "client %s : %s (%s)" % (self.client_address[0],self.data,cur_thread.getName())
# müşteriye yanıt gönderme
response = "%s: %s" % (cur_thread.getName(), self.data.upper())
# self.wfile, istemciye yazma akışıdır
self.wfile.write(response)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
# ------------------------------------------------------ ana işlev
# çağırma sözdizimi: argv[0] bağlantı noktası
# sunucu, belirtilen bağlantı noktasında başlatılır
# Veriler
syntaxe="syntaxe : %s port" % (sys.argv[0])
#------------------------------------- çağrı kontrol ediliyor
# tek bir argüman bulunmalıdır
nbArguments=len(sys.argv)
if nbArguments!=2:
print syntaxe
sys.exit(1)
# bağlantı noktası sayısal olmalıdır
port=sys.argv[1]
modele=r"^\s*\d+\s*$"
if not re.match(modele,port):
print "Le port %s n'est pas un nombre entier positif\n" % (port)
sys.exit(2)
# sunucunun başlatılması - her istemciye bir iş parçacığı üzerinden hizmet verir
host="localhost"
server = ThreadedTCPServer((host,int(port)), ThreadedTCPRequestHandler)
# sunucu bir iş parçacığında başlatılıyor
# her istemciye ayrı bir iş parçacığıyla hizmet verilir
server_thread = threading.Thread(target=server.serve_forever)
# sunucuyu başlatır - istemcileri bekleyen sonsuz döngü
server_thread.start()
# izleme
print "Serveur d'echo a l'ecoute sur le port % s" % (port)
Notlar:
-
- satır: sys.argv, komut dosyasının parametrelerini temsil eder. Bu parametreler burada şu biçimde olmalıdır: nom_du_script port. Dolayısıyla iki tane olmalıdır. sys.argv[0], nom_du_script olacak ve sys.argv[1], port olacaktır ;
-
- satır: yankı sunucusu, ThreadedTcpServer sınıfının bir örneğidir. Bu sınıfın oluşturucusu iki parametre bekler:
-
- parametre: sunucunun çalışacağı makineyi ve dinleme bağlantı noktasını belirleyen iki elemanlı bir tuple (host, port);
- parametre 2: bir istemcinin isteklerini işlemekle görevli sınıfın adı.
-
- satır: bir iş parçacığı oluşturulur (ancak henüz başlatılmaz). Bu iş parçacığı, TCP sunucusunun [serve_forever] yöntemini yürütür. Bu yöntem, istemci bağlantılarını dinleyen bir döngüdür. Bir istemci bağlantısı algılandığında, ThreadedTCPRequestHandler sınıfının bir örneği oluşturulur. Bu sınıfın handle yöntemi, istemciyle iletişimi sağlar;
-
- satır: yankı hizmetinin iş parçacığı başlatılır. Bu andan itibaren istemciler hizmete bağlanabilir;
-
- satır: yankı sunucusu sınıfı. Bu sınıf, SocketServer.ThreadingMixIn ve SocketServer.TCPServer olmak üzere iki sınıftan türetilmiştir. Bu da onu çok iş parçacıklı bir TCP sunucusu yapar: sunucu bir iş parçacığında çalışır ve her müşteriye ayrı bir iş parçacığında hizmet verilir;
-
- satır: istemcilerin isteklerini işleyen sınıf. SocketServer.StreamRequestHandler sınıfından türetilmiştir. Böylece iki özniteliği miras alır:
- rfile: bu, istemci tarafından gönderilen verilerin okuma akışıdır - bir metin dosyası gibi işlenebilir;
- wfile: müşteriye veri gönderilmesini sağlayan yazma akışıdır; bir metin dosyası gibi işlenebilir.
-
- satır: handle yöntemi, istemcilerin isteklerini işler;
-
- satır: Bu yöntemi çalıştıran iş parçacığı;
-
- satır: müşteri isteklerini işleyen döngü. Döngü, müşteri boş bir satır gönderdiğinde sona erer;
-
- satır: Müşterinin isteğinin okunması;
-
- satır: self.client_address[0], müşterinin IP adresini temsil eder. cur_thread.getName(), handle yöntemini çalıştıran iş parçacığının adıdır;
-
- satır: Müşteriye verilen yanıt iki bileşenden oluşur: müşteriye hizmet veren iş parçacığının adı ve müşterinin gönderdiği komut (büyük harflerle yazılmış).
# -*- coding=utf-8 -*-
import re,sys,socket
# ------------------------------------------------------ main
# genel tcp istemcisi
# çağırma sözdizimi: argv[0] ana bilgisayar bağlantı noktası
# istemci yankı hizmetine bağlanır (ana bilgisayar, bağlantı noktası)
# sunucu, istemci tarafından yazılan satırları geri gönderir
# sözdizimi
syntaxe="syntaxe : %s hote port" % (sys.argv[0])
#------------------------------------- çağrı kontrol ediliyor
# iki argüman olması gerekir
nbArguments=len(sys.argv)
if nbArguments!=3:
print syntaxe
sys.exit(1)
# argümanlar alınır
hote=sys.argv[1]
# bağlantı noktası sayısal olmalıdır
port=sys.argv[2]
modele=r"^\s*\d+\s*$"
if not re.match(modele,port):
print "Le port %s foit être un nombre entier positif" % (port)
sys.exit(2)
try:
# müşterinin sunucuya bağlanması
connexion=socket.create_connection((hote,int(port)))
except socket.error, erreur:
print "Echec de la connexion au site (%s,%s) : %s" % (hote,port,erreur)
sys.exit(3)
try:
# giriş döngüsü
ligne=raw_input("Commande (rien pour arreter): ").strip()
while ligne!="":
# satır sunucuya gönderiliyor
connexion.send("%s\n" % (ligne))
# yanıt bekleniyor
reponse=connexion.recv(1000)
print "<-- %s" % (reponse)
ligne=raw_input("Commande (rien pour arreter): ")
except socket.error, erreur:
print "Echec de la connexion au site (%s,%s) : %s" % (hote,port,erreur)
sys.exit(3)
finally:
# bağlantı sonlandırılıyor
connexion.close()
Sunucu, ilk komut penceresinde başlatılır:
İkinci bir pencerede ilk istemci başlatılır:
İstemci, sunucuya gönderdiği komuta yanıt olarak, aynı komutu büyük harflerle yazılmış haliyle almaktadır. Üçüncü bir pencerede ikinci bir istemci başlatılır:
Sunucu konsolu şu şekilde görünür:
İstemciler farklı iş parçacıklarında hizmet almaktadır. Bir istemciyi durdurmak için boş bir komut girmek yeterlidir.
11.6. Genel TCP Sunucusu
Şu özelliklere sahip bir Python betiği yazmayı planlıyoruz:
- aynı anda tek bir istemciye hizmet verebilen,
- istemci tarafından gönderilen metin satırlarını kabul eden,
- klavyeden gelen ve müşteriye yanıt olarak gönderilen metin satırlarını kabul eden bir TCP sunucusu olacak.
Böylece, klavyeyi kullanan kullanıcı sunucu görevi görür:
- konsolunda istemci tarafından gönderilen metin satırlarını görür;
- klavyeye yanıtı yazarak istemciye cevap verir.
Böylelikle her tür istemciye uyum sağlayabilir. Bu nedenle buna "genel TCP sunucusu" denir. Bu, TCP iletişim protokollerini keşfetmek için kullanışlı bir araçtır. Aşağıdaki örnekte, TCP istemcisi bir web tarayıcısı olacaktır; bu da web istemcileri tarafından kullanılan HTTP protokolünü keşfetmemizi sağlayacaktır.
![]() |
# -*- coding=utf-8 -*-
# genel TCP sunucusu
# başlık dosyaları yükleniyor
import re,sys,SocketServer,threading
# genel TCP sunucusu
class MyTCPHandler(SocketServer.StreamRequestHandler):
def handle(self):
# istemci görüntüleniyor
print "client %s" % (self.client_address[0])
# istemciden gelen komutları okumak için bir iş parçacığı oluşturuluyor
thread_lecture = threading.Thread(target=self.lecture)
thread_lecture.start()
# 'bye' komutuyla sonlandırma
# klavyeden girilen komut okunur
cmde=raw_input("--> ")
while cmde!="bye":
# komutun müşteriye gönderilmesi. self.wfile, müşteriye yönelik yazma akışıdır
self.wfile.write("%s\n" % (cmde))
# sonraki komut okunuyor
cmde=raw_input("--> ")
def lecture(self):
# "bye" komutu alınana kadar istemci tarafından gönderilen tüm satırlar görüntülenir
ligne=""
while ligne!="bye":
# Müşteriden gelen metin satırları readline yöntemi ile okunur
ligne = self.rfile.readline().strip()
# konsol izleme
print "<--- %s : %s" % (self.client_address[0], ligne)
# ------------------------------------------------------ main
# çağırma sözdizimi: argv[0] bağlantı noktası
# sunucu, belirtilen bağlantı noktasında başlatılır
# Veriler
syntaxe="syntaxe : %s port" % (sys.argv[0])
#------------------------------------- çağrı kontrol ediliyor
# tek bir argüman bulunmalıdır
nbArguments=len(sys.argv)
if nbArguments!=2:
print syntaxe
sys.exit(1)
# bağlantı noktası sayısal olmalıdır
port=sys.argv[1]
modele=r"^\s*\d+\s*$"
if not re.match(modele,port):
print "Le port %s n'est pas un nombre entier positif\n" % (port)
sys.exit(2)
# sunucunun başlatılması
host="localhost"
server = SocketServer.TCPServer((host, int(port)), MyTCPHandler)
print "Service tcp generique lance sur le port %s. Arret par Ctrl-C" % (port)
server.serve_forever()
Notlar:
-
- satır: TCP sunucusu, SocketServer.TCPServer sınıfının bir örneği olacaktır. Bu sınıfın oluşturucusu iki parametre kabul eder:
- İlk parametre, iki elemanlı bir tuple’dır (host, port); burada host, hizmetin çalıştığı makinedir (genellikle localhost) ve port, hizmetin istemcilerin isteklerini beklediği (dinlediği) bağlantı noktasıdır;
- ikinci parametre, bir müşterinin hizmet sınıfını belirtir. Bir müşteri bağlandığında, hizmet sınıfının bir örneği oluşturulur ve müşteri ile olan bağlantıyı yönetmesi gereken yöntem handle'tir.
SocketServer.TCPServer TCP sunucusu çok iş parçacıklı değildir. Aynı anda yalnızca bir müşteriye hizmet verir;
-
- satır: Tcp sunucusunun serve_forever yöntemi yürütülür. Bu, istemcileri bekleyen sonsuz bir döngüdür;
-
- satır: Tcp sunucusu burada SocketServer.StreamRequestHandler sınıfından türetilmiş bir sınıftır. Bu, istemciyle alışverişi yapılan veri akışlarını metin dosyaları olarak ele almamızı sağlar. Bu sınıfa daha önce rastlamıştık. Elimizde şu yöntemler bulunmaktadır:
- readline, müşteriden gelen bir metin satırını okumak için;
- write, müşteriye metin satırları göndermek için kullanılır.
-
- satır: self.client_address[0], müşterinin IP adresidir;
-
- satır: TCP sunucusu, iki iş parçacığı kullanarak istemciyle iletişim kuracaktır
- müşteriden gelen satırları okumak için bir iş parçacığı;
- müşteriye satır yazmak için bir iş parçacığı.
-
- satır: Müşteriden gelen satırları okumak için iş parçacığı oluşturulur. target parametresi, iş parçacığı tarafından yürütülecek yöntemi belirler. Bu, 25. satırda tanımlanan yöntemdir;
-
- satır: okuma iş parçacığı başlatılır;
- 25-32. satırlar: okuma iş parçacığı tarafından yürütülen yöntem;
-
- satır: okuma iş parçacığı, "bye" satırı alınana kadar istemci tarafından gönderilen tüm metin satırlarını okur;
-
- satır: Burada, müşteriye yazma iş parçacığı içindeyiz. Buradaki mantık, kullanıcının klavyeden girdiği tüm metin satırlarını müşteriye göndermektir.
Sunucu
İstemci tarayıcısı
Sunucu tarafından alınan istek
Kullanıcının klavyeden girdiği sunucu yanıtı (--> işareti hariç)
- 1-5. satırlar: İstemciye gönderilen HTTP yanıtı;
-
- satır: İstemciye gönderilen HTML sayfası;
-
- satır: istemciyle olan iletişimin sonu. İstemciye sunulan hizmet sona erecek ve bağlantı kapatılacak; bu da istemci tarafından gönderilen metin satırlarını okuyan iş parçacığını aniden kesintiye uğratacaktır;
- Müşteriye gönderilen HTTP yanıtının 1-5. satırlarının anlamları şöyledir:
-
- satır: Müşteri tarafından istenen kaynak bulundu;
-
- satır: sunucunun kimliği;
-
- satır: sunucu, kaynağı gönderdikten sonra bağlantıyı kapatacaktır;
-
- satır: Sunucu tarafından gönderilen kaynağın türü: bir HTML belgesi;
-
- satır: Boş bir satır.
[1] tarayıcısı tarafından görüntülenen sayfa:
![]() |
[2] tarayıcısı tarafından alınan kaynak kodunu görüntülediğimizde, web tarayıcısı tarafından alınan HTML kodunun, kendisine gönderilen kodla aynı olduğunu görüyoruz.




