Ana içeriğe geç

Python'da Socket Programlamaya Kapsamlı Bir Rehber

Sıfırdan TCP istemcileri ve sunucuları oluşturun, çoklu bağlantıları threading ile yönetin ve ne zaman asyncio'ya başvurmanız gerektiğini öğrenin.
· 15 dk. oku

Yapay Zeka ile Keşfedin

Cihazları bilgi alışverişi için birbirine bağlamak, ağ iletişiminin özüdür. Soketler, yerel veya küresel ağlar üzerinden cihazlar arasında ve aynı makinedeki farklı süreçler arasında mesaj iletmek için kullanılan temel kavram olduklarından, etkili ağ iletişiminin vazgeçilmez bir parçasıdır. Gönderilecek veya alınacak trafiği ince ayarlarla kontrol etmeyi sağlayan düşük seviyeli bir arayüz sunarlar.

Bu düşük seviyeli yapı, soket iletişiminin üzerine inşa edilen geleneksel protokollerde bulunabilecek ek yüklerden kaçınarak, belirli kullanım durumları için çok yüksek performanslı iletişim kanalları (veya özel protokoller) oluşturmayı mümkün kılar.

Bu da soketleri, anlık mesajlaşmaya dayanan veya çok büyük miktarda veriyle çalışan gerçek zamanlı istemci-sunucu uygulamalarında son derece kullanışlı kılar.

Bu yazıda, soket programlamanın temellerini ele alacak ve Python kullanarak soket tabanlı istemci ve sunucu uygulamalarının adım adım nasıl oluşturulacağını göstereceğiz. 

Ağ Temelleri

Ağlar, her türden iletişimi ve bilgi paylaşımını mümkün kılar. Bu şekilde birbirine bağlanan cihazların oluşturduğu bütüne ağ denir.

Fiziksel dünyamızda gözlemleyebileceğimiz pek çok ağ vardır: havayolu veya enerji iletim hatları ağları ya da otoyollarla birbirine bağlanan şehirler iyi örneklerdir.

Benzer şekilde, bilgi teknolojilerinde de sayısız ağ vardır; bunların en belirgini ve en bilineni, sayısız cihazı birbirine bağlayan ve muhtemelen şu an bu makaleyi okumak için kullandığınız ağların küresel ağı olan internettir.

Ağ türleri

İnternet, ölçek veya diğer özelliklere göre birbirinden ayrılan pek çok ağı kendi içinde barındırır: örneğin, genellikle birbirine yakın bilgisayarları bağlayan yerel alan ağları (LAN). Şirketlerde veya diğer kurumlarda (bankalar, üniversiteler vb.) bulunan makineler ya da evinizde yönlendiriciye bağlı cihazlar böyle bir ağı oluşturur.

Bunun yanı sıra, PAN (kişisel alan ağı) gibi daha küçük veya MAN (metropol alan ağı) gibi daha büyük, bir şehrin tamamındaki cihazları birbirine bağlayabilen ve WAN (geniş alan ağı) gibi ülkeleri veya tüm dünyayı kapsayabilen ağ türleri de vardır. Evet, en büyük WAN ağı bizzat internetin kendisidir.

Bilgisayar ağlarının çok karmaşık olabileceği ve birçok unsurdan oluşabileceği aşikârdır. En temel ve kritik yapı taşlarından biri iletişim protokolüdür.

Ağ iletişim protokolü türleri

İletişim protokolleri, bilgilerin nasıl ve hangi formatta gönderilip alınacağını belirler. Bu protokoller, ağ iletişiminde yer alan çeşitli görevleri yönetmek için hiyerarşik olarak düzenlenir.

Yaygın olarak kullanılan ve iyi bilinen bazı ağ iletişim protokolleri şunlardır:

Wi-Fi

Bir bağlantı katmanı protokolü örneğidir; donanıma çok yakın konumlanır ve verilerin kablosuz ortamda bir cihazdan diğerine fiziksel olarak gönderilmesinden sorumludur.

IP (Internet Protocol)

IP, başlıca paket yönlendirmeden ve IP adreslemeden sorumlu bir ağ katmanı protokolüdür.

TCP (Transmission Control Protocol)

Tam çift yönlü iletişim sağlayan, veri bütünlüğünü ve teslimatını güvence altına alan, bağlantı odaklı güvenilir bir protokoldür. Bağlantıları yöneten, hataları tespit eden ve bilgi akışını kontrol eden bir taşıma katmanı protokolüdür.

UDP (User Datagram Protocol)

TCP ile aynı protokol ailesinden bir protokoldür. Temel fark, UDP'nin teslimat kontrolü yapmayan ve “gönder ve unut” yaklaşımını izleyen, daha basit, hızlı ama güvenilir olmayan bağlantısız bir protokol olmasıdır. TCP gibi, UDP de taşıma katmanında yer alır.

HTTP (Hypertext Transfer Protocol)

Uygulama katmanı protokolüdür ve özellikle webde tarayıcı ile sunucu arasındaki iletişim için en yaygın kullanılan protokoldür. Şu an okuduğunuz bu makalenin de HTTP üzerinden sunulduğunu söylemeye gerek yok. HTTP, TCP üzerine inşa edilir ve üstbilgiler (metadata ve çerezleri taşımak için kullanılır), farklı HTTP yöntemleri (GET, POST, DELETE, UPDATE) gibi web uygulamalarına özgü bilgileri yönetir ve aktarır.

MQTT (Message Queue Telemetry Transport)

Sınırlı işlem gücü ve pil ömrüne sahip cihazlar için, güvenilir olmayan ağ koşullarında çalışan (örneğin bir maden sahasındaki gaz sensörleri ya da evinizdeki akıllı bir ampul) bir başka uygulama katmanı protokolü örneğidir. MQTT, IoT (Nesnelerin İnterneti) için standart bir mesajlaşma protokolüdür. Hem hafiftir hem de kullanımı basittir; güvenilirliği artırmak için yerleşik yeniden iletim mekanizmalarıyla tasarlanmıştır. Bu protokolü Python ile kullanmak istiyorsanız, Paho MQTT istemcisini ayrıntılı biçimde anlatan şu Python MQTT rehberini okuyabilirsiniz.

Önemli bir gözlem, yukarıda bahsedilen tüm protokollerin alt tarafta soketleri kullanması, ancak üzerine kendi mantık ve veri işleme katmanlarını eklemesidir. Bunun nedeni, modern cihazlarda soketlerin herhangi bir ağ iletişimi için düşük seviyeli arayüz olmasıdır; bir sonraki bölümde bunu tartışacağız.

Temel Kavramlar ve Terimler

Elbette ağlar bağlamında kullanılan başka pek çok önemli kavram ve terim vardır. İşte bu eğitim boyunca karşınıza çıkabilecek en öne çıkan bazılarına hızlı bir bakış:

  • Paket: bir bilgisayar ağında standart veri iletim birimi (gündelik dilde “mesaj” olarak da düşünülebilir).
  • Uç nokta: paketlerin ulaştığı hedef.
  • IP adresi: ağ üzerindeki bir cihazı benzersiz olarak tanımlayan sayısal tanımlayıcı. Bir IP adresi örneği: 192.168.0.0
  • Portlar: belirli ağ iletişimlerini yöneten ve bir cihazda çalışan bir süreci benzersiz olarak tanımlayan sayısal tanımlayıcılar: örneğin, web sitenizi HTTP üzerinden sunar. Bir IP adresi cihazı tanımlarken, bir port uygulamayı tanımlar (her uygulama bir süreçtir veya süreçlerden oluşur). Bazı bilinen port örnekleri: HTTP trafiğini yönetmek için sunucular tarafından geleneksel olarak kullanılan 80 numaralı port ve HTTPS (güvenli HTTP) için 443 numaralı port.
  • Ağ geçidi (gateway): bir ağdan diğerine erişim noktası görevi gören özel bir ağ düğümü (cihaz). Bu ağlar farklı protokoller de kullanabileceğinden, ağ geçidinin protokol çevirisi yapması gerekebilir. Bir ev yönlendiricisinin yerel ağınızı internete bağlaması buna örnektir.

Soketleri Anlamak

Soket nedir?

Soket, aynı ya da farklı makinelerde bulunan farklı süreçler arasında iletişim için bir arayüzdür (geçit). İkincisinden söz ediyorsak, ağ soketlerinden bahsediyoruz.

Ağ soketleri bağlantı yönetimini soyutlar. Özellikle Unix sistemlerinde soketler, aynı yazma-okuma işlemlerini destekleyen, ancak tüm verileri ağ üzerinden gönderen dosyalardır.

Bir soket dinleme veya bağlanma durumundayken, her zaman ana bilgisayarı (makine/cihaz) ve süreci tanımlayan bir IP adresi ile bir port numarasının birleşimine bağlanır.

Soket bağlantıları nasıl çalışır

Soketler gelen bağlantıları dinleyebilir veya kendileri dışa doğru bağlantı kurabilir. Bir bağlantı kurulduğunda dinleyen soket (sunucu soketi), bağlanan tarafın IP ve portuna da ayrıca bağlanır.

Ya da alternatif olarak, artık dinleyici ile istekte bulunanın IP adresi ve port numaralarından oluşan iki çifte bağlı yeni bir soket oluşturulur. Böylece, farklı makinelerdeki iki bağlı soket birbirini tanımlayabilir ve dinleyen soketi engellemeden (o sırada başka bağlantıları dinlemeye devam eder) veri aktarımı için tek bir bağlantıyı paylaşabilir.

Bağlanan soket (istemci soketi) durumunda ise, bağlantı başlatılırken cihazın IP adresine ve rastgele erişilebilir bir port numarasına örtük olarak bağlanır. Ardından, bağlantı kurulduğunda, diğer iletişim tarafının IP ve portuna bağlanma, dinleyen sokette olduğu gibi gerçekleşir, ancak yeni bir soket oluşturulmadan.

Soket programlama

Soket programlamadan bahsettiğimizde, kodumuzda soket nesneleri oluşturur ve bunlar üzerinde işlemler yaparız (dinleme, bağlanma, alma, gönderme vb.). Bu bağlamda, soketler programımızda oluşturduğumuz ve ağ bağlantıları ile trafiği yönetmek için özel yöntemlere sahip özel nesnelerdir.

Perde arkasında bu yöntemler, işletim sisteminizin çekirdeğini, daha doğrusu ağ işlemlerini yönetmekten sorumlu çekirdeğin özel bir parçası olan ağ yığınını çağırır.

Soketler ve istemci-sunucu iletişimi

Soketler bağlantılarla ilgilidir; onlar bağlantı işleyicileridir. Webde, veri göndermek veya almak istediğinizde bir bağlantı başlatırsınız (soket adı verilen arayüz üzerinden).

Bu noktada, ya siz ya da bağlanmaya çalıştığınız taraf sunucu olarak, diğeri ise istemci olarak davranır. Sunucu verileri istemcilere sunarken, istemciler proaktif biçimde bağlanır ve sunucudan veri talep eder. Bir sunucu, dinleme soketi aracılığıyla yeni bağlantıları dinler, bunları kurar, istemcinin isteklerini alır ve yanıtında istenen verileri istemciye iletir.

Öte yandan, bir istemci bağlanmak istediği sunucunun IP adresi ve portunu kullanarak bir soket oluşturur, bağlantı başlatır, isteğini sunucuya iletir ve yanıt olarak verileri alır. İstemci ve sunucu soketleri arasındaki bu kesintisiz bilgi alışverişi, çeşitli ağ uygulamalarının bel kemiğini oluşturur.

Ağ protokolleri için temel olarak soketler

Soketlerin bel kemiğini oluşturması, onların üzerine inşa edilen ve kullanılan çeşitli protokoller olduğu anlamına da gelir. Daha önce kısaca bahsettiğimiz UDP ve TCP çok yaygın olanlardır. Bu taşıma protokollerinden birini kullanan soketlere UDP veya TCP soketleri denir.

IPC soketleri

Ağ soketlerinin yanında başka türler de vardır. Örneğin, IPC (Süreçler Arası İletişim) soketleri. IPC soketleri, aynı makinedeki süreçler arasında veri aktarmak için tasarlanmıştır; ağ soketleri ise bunu ağ üzerinden yapabilir.

IPC soketlerinin güzel yanı, veriyi göndermek için paket oluşturma ve yön çözümleme gibi ek yüklerin çoğundan kaçınmalarıdır. IPC bağlamında gönderen ve alıcı yerel süreçler olduğundan, IPC soketleri üzerinden iletişim tipik olarak daha düşük gecikmeye sahiptir.

Unix soketleri

IPC soketlerine iyi bir örnek, Unix soketleridir; Unix'teki her şeyde olduğu gibi bunlar da dosya sisteminde yer alan dosyalardır. IP adresi ve portla değil, dosya sistemindeki dosya yolu ile tanımlanırlar.

IPC olarak ağ soketleri

Sunucu ve alıcı aynı makinadaysa (yani IP adresi 127.0.0.1 ise), süreçler arası iletişim için ağ soketlerini de kullanabileceğinizi unutmayın.

Elbette bu, bir yandan verilerinizin ağ yığını tarafından işlenmesiyle ilişkili ek yüklere bağlı olarak ek gecikme ekler; ancak diğer yandan, ağ soketleri tüm sistemlerde mevcut ve çalışır durumda olduğundan, belirli bir işletim sistemine veya aileye özgü olabilen IPC soketlerinin aksine temel işletim sistemi farklılıklarıyla uğraşmamamızı sağlar.

Python Socket Kütüphanesi

Python'da soket programlama için, soketleri oluşturmak, yönetmek ve onlarla çalışmak üzere işlevler, sabitler ve sınıflardan oluşan resmi, yerleşik Python socket kütüphanesini kullanırız. Bu kütüphanenin yaygın kullanılan bazı işlevleri şunlardır:

  • socket(): Yeni bir soket oluşturur.
  • bind(): Soketi belirli bir adrese ve porta bağlar.
  • listen(): Soket üzerinde gelen bağlantıları dinlemeye başlar.
  • accept(): Bir istemciden bağlantı kabul eder ve iletişim için yeni bir soket döndürür.
  • connect(): Uzak bir sunucuya bağlantı kurar.
  • send(): Soket üzerinden veri gönderir.
  • recv(): Soketten veri alır.
  • close(): Soket bağlantısını kapatır.

Python Socket Örneği

Python ile yazılmış pratik bir örnek üzerinden soket programlamaya bakalım. Burada amacımız iki uygulamayı birbirine bağlamak ve aralarında iletişim kurmaktır. Python socket kütüphanesini kullanarak, ağ üzerinden bir istemciyle iletişim kurup bilgi alışverişi yapacak bir sunucu soketi uygulaması oluşturacağız.

Dikkate alınacak noktalar ve sınırlamalar

Eğitsel amaçlarla örneğimizin basitleştirildiğini ve uygulamaların yerel olarak çalışacağını, gerçek ağ üzerinden konuşmayacağını unutmayın - istemciyi sunucuya bağlamak için geri dönüş (loopback) localhost adresini kullanacağız.

Bu, istemci ve sunucunun aynı makinede çalışacağı ve istemcinin, çalıştığı aynı makinede, sunucuyu temsil eden farklı bir sürece bağlantı başlatacağı anlamına gelir.

Farklı makinelerde çalıştırma

Alternatif olarak, uygulamalarınızı iki farklı cihaza kurup her ikisini de aynı Wi-Fi yönlendiricisine bağlayabilirsiniz; böylece bir yerel alan ağı oluşur. Bu durumda bir cihazda çalışan istemci, diğer makinede çalışan sunucuya bağlanabilir.

Ancak bu durumda, yönlendiricinizin cihazlarınıza atadığı IP adreslerini bilmeniz ve localhost (127.0.0.1) geri dönüş IP adresi yerine bunları kullanmanız gerekir (IP adreslerini görmek için Unix benzeri sistemlerde ifconfig, Windows'ta ipconfig komutunu kullanın). Uygulamalarınızın IP adreslerini edindikten sonra, bunları koddaki ilgili yerlere koyarsanız örnek yine çalışacaktır.

Python'da soket sunucusu oluşturma

Önce, istemcilerle mesaj alışverişi yapacak bir soket sunucusu (özellikle TCP soketleriyle çalışacağından bir Python TCP sunucusu, birazdan göreceğiz) oluşturalım. 

Şu adımları izleyin:

Bir miktar şablon kodla Python dosyası oluşturma

  • server.py adlı bir dosya oluşturun

  • Python betiğinize socket modülünü içe aktarın.

import socket
  • run_server adlı bir işlev ekleyin. Kodumuzun çoğunu buraya ekleyeceğiz. Kodu bu işlevin içine eklerken doğru girinti verdiğinizden emin olun:
def run_server():
    # your code will go here

Soket nesnesini oluşturma

Bir sonraki adım olarak, run_server içinde socket.socket() işlevini kullanarak bir soket nesnesi oluşturun.

İlk argüman (socket.AF_INET), IPv4 için IP adres ailesini belirtir (diğer seçenekler: IPv6 ailesi için AF_INET6 ve Unix soketleri için AF_UNIX).

İkinci argüman (socket.SOCK_STREAM), TCP soketi kullandığımızı gösterir.

TCP kullanıldığında, işletim sistemi sıralı veri teslimi, hata tespiti ve yeniden iletim ile akış kontrolüne sahip güvenilir bir bağlantı kurar. Tüm bu ayrıntıları sizin uygulamanız gerekmez.

UDP soketi belirtmek için socket.SOCK_DGRAM seçeneği de vardır. Bu, UDP'nin tüm özelliklerini perde arkasında uygulayan bir soket oluşturur.

Daha da düşük seviyeye inip, soketlerin kullandığı TCP/IP ağ katmanı protokolünün üzerinde kendi taşıma katmanı protokolünüzü kurmak isterseniz, ikinci argüman için socket.SOCK_RAW değerini kullanabilirsiniz. Bu durumda işletim sistemi, üst seviye protokol özelliklerini sizin yerinize ele almaz; başlıklar, bağlantı onayı ve yeniden iletim işlevlerini gerektiğinde kendiniz uygulamalısınız. Diğer değerler için belgelere bakabilirsiniz.

# create a socket object
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Sunucu soketini IP adresine ve porta bağlama

Sunucunun hangi adresten erişilebilir olacağını ve gelen bağlantıları nerede dinleyeceğini belirtmek için ana bilgisayar adını veya sunucu IP'sini ve portu tanımlayın. Bu örnekte sunucu yerel makinede dinliyor - bu, server_ip değişkeninin 127.0.0.1 (localhost olarak da adlandırılır) olarak ayarlanmasıyla belirlenir.

port değişkeni 8000 olarak ayarlanmıştır; bu, sunucu uygulamasının işletim sistemi tarafından tanınacağı port numarasıdır (Sistem süreçleri tarafından kullanılan portlarla çakışmayı önlemek için 1023'ün üzerindeki değerleri kullanmanız önerilir).

server_ip = "127.0.0.1"
    port = 8000

Soketi, daha önce tanımladığımız IP adresi ve porta bağlayarak bağlantıları almaya hazırlayın.

# bind the socket to a specific address and port
    server.bind((server_ip, port))

Gelen bağlantıları dinleme

listen işleviyle sunucu soketini dinleme durumuna geçirin. backlog argümanı, kabul edilmeden önce kaç gelen bağlantının kuyrukta bekleyebileceğini belirler.

Örneğimizde 0 geçiriyoruz; bu, çoğu sistemde sunucuyu aynı anda tek bir istemciyle etkin biçimde sınırlar — meşgulken yapılan ek bağlantı denemeleri reddedilir. Daha yüksek bir değer (örneğin 1 veya daha fazla), o sayıda istemcinin kuyrukta beklemesine izin verir; bir istemci üzerinde accept çağrıldığında, kuyruktan çıkar ve yuva boşalır.

Argümanı atarsanız, sistem varsayılanına geri döner (Unix'te /proc/sys/net/core/somaxconn yolundan görülebilir).

# listen for incoming connections
    server.listen(0)
    print(f"Listening on {server_ip}:{port}")

Gelen bağlantıları kabul etme

Şimdi, gelen istemci bağlantılarını bekleyip kabul edin. accept yöntemi, bir istemci bağlanana kadar yürütme iş parçacığını durdurur. Ardından (conn, address) şeklinde bir ikili döndürür; burada address, istemcinin IP adresi ve portundan oluşan bir ikilidir, conn ise istemciyle bağlantıyı paylaşan ve onunla iletişim kurmak için kullanılabilen yeni bir soket nesnesidir.

accept, iletişim için dinleme soketini (örneğimizde server olarak adlandırılan) istemcinin adresine bağlamak ve onu kullanmak yerine, istemciyle iletişim kurmak için yeni bir soket oluşturur; çünkü dinleme soketinin diğer istemcilerden gelecek bağlantıları dinlemesi gerekir, aksi hâlde bloke olurdu. Elbette bizim örneğimizde aynı anda tek bir istemciyi ele alıyor ve bunu yaparken diğer tüm bağlantıları reddediyoruz; ancak çok iş parçacıklı sunucu örneğine geçtiğimizde bu daha da anlamlı olacak.

# accept incoming connections
    client_socket, client_address = server.accept()
    print(f"Accepted connection from {client_address[0]}:{client_address[1]}")

İletişim döngüsü oluşturma

İstemciyle bağlantı kurar kurmaz (accept çağrısından sonra), iletişim kurmak için sonsuz bir döngü başlatırız. Bu döngüde, client_socket nesnesinin recv yöntemini çağırırız. Bu yöntem, istemciden belirtilen sayıda baytı alır — bizde bu sayı 1024.

1024 bayt, yük boyutu için yaygın bir gelenektir; iki kuvveti olduğu için, rastgele bir değerden optimizasyon açısından daha uygun olabilir. Ancak bu değeri dilediğiniz gibi değiştirebilirsiniz.

request değişkenine istemciden gelen veri ham ikili biçimde olduğundan, decode işlevini kullanarak bayt dizisini bir dizeye dönüştürdük.

Ardından, ”close” iletisi aldığımızda iletişim döngüsünden çıkmayı sağlayan bir if ifademiz var. Yani, sunucumuz istekte ”close” dizesini alır almaz bunu istemciye teyit olarak gönderir ve bağlantıyı sonlandırır. Aksi hâlde, alınan mesajı konsola yazdırırız. Bizim durumumuzda teyit, istemciye ”closed” dizesini göndermekten ibarettir.

If ifadesinde request dizesi üzerinde kullandığımız lower yöntemi, dizeyi küçük harfe çevirir. Böylece close dizesinin büyük ya da küçük harfle yazılmış olmasına aldırmayız.

# receive data from the client
    while True:
        request = client_socket.recv(1024)
        request = request.decode("utf-8") # convert bytes to string
        
        # if we receive "close" from the client, then we break
        # out of the loop and close the connection
        if request.lower() == "close":
            # send response to the client which acknowledges that the
            # connection should be closed and break out of the loop
            client_socket.send("closed".encode("utf-8"))
            break

        print(f"Received: {request}")

İstemciye yanıt gönderme

Şimdi sunucunun istemciye normal yanıtını (yani istemci bağlantıyı kapatmak istemediğinde) ele almalıyız. While döngüsünün içinde, print(f"Received: {request}") satırının hemen ardından, bir yanıt dizesini (bizde ”accepted”) baytlara çevirip istemciye gönderecek aşağıdaki satırları ekleyin. Böylece sunucu, istemciden ”close” olmayan bir mesaj aldığında yanıt olarak ”accepted” dizesini gönderecektir:

response = "accepted".encode("utf-8") # convert string to bytes
# convert and send accept response to the client
client_socket.send(response)

Kaynakları serbest bırakma

Sonsuz while döngüsünden çıkar çıkmaz, istemciyle iletişim tamamlanmıştır; bu nedenle sistem kaynaklarını serbest bırakmak için close yöntemiyle istemci soketini kapatırız. Aynı yöntemle sunucu soketini de kapatırız; bu da sunucumuzu fiilen kapatır. Gerçek dünyada, elbette, sunucunun sadece tek bir istemciyle konuştuktan sonra kapanmasını değil, diğer istemcileri dinlemeye devam etmesini isteriz; merak etmeyin, aşağıda başka bir örneğe geçeceğiz.

Şimdilik, sonsuz while döngüsünden sonra aşağıdaki satırları ekleyin:

# close connection socket with the client
    client_socket.close()
    print("Connection to client closed")
    # close server socket
    server.close()

Not: server.py dosyanızın sonunda run_server işlevini çağırmayı unutmayın. Aşağıdaki kod satırını kullanın:

run_server()

Tam sunucu soketi kod örneği

İşte tam server.py kaynak kodu:

import socket

def run_server():
    # create a socket object
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    server_ip = "127.0.0.1"
    port = 8000

    # bind the socket to a specific address and port
    server.bind((server_ip, port))
    # listen for incoming connections
    server.listen(0)
    print(f"Listening on {server_ip}:{port}")

    # accept incoming connections
    client_socket, client_address = server.accept()
    print(f"Accepted connection from {client_address[0]}:{client_address[1]}")

    # receive data from the client
    while True:
        request = client_socket.recv(1024)
        request = request.decode("utf-8") # convert bytes to string
        
        # if we receive "close" from the client, then we break
        # out of the loop and close the conneciton
        if request.lower() == "close":
            # send response to the client which acknowledges that the
            # connection should be closed and break out of the loop
            client_socket.send("closed".encode("utf-8"))
            break

        print(f"Received: {request}")

        response = "accepted".encode("utf-8") # convert string to bytes
        # convert and send accept response to the client
        client_socket.send(response)

    # close connection socket with the client
    client_socket.close()
    print("Connection to client closed")
    # close server socket
    server.close()

run_server()

Bu temel örneği karmaşıklaştırmamak için hata yönetimini atladığımıza dikkat edin. Elbette try-except blokları eklemek ve finally bölümünde soketleri daima kapattığınızdan emin olmak isteyeceksiniz. Okumaya devam edin; daha gelişmiş bir örnek göreceğiz.

Python'da İstemci Soketi Oluşturma

Sunucunuzu kurduktan sonra, bir sonraki adım sunucunuza bağlanıp istek gönderecek bir istemci ayarlamaktır. Şimdi şu adımlarla başlayalım:

Bir miktar şablon kodla Python dosyası oluşturma

  • client.py adlı yeni bir dosya oluşturun
  • Soket kütüphanesini içe aktarın:
import socket
  • Tüm kodumuzu koyacağımız run_client işlevini tanımlayın:
def run_client():
    # your code will go here

Soket nesnesini oluşturma

Ardından, istemcinin sunucuyla temas noktası olarak hizmet edecek bir TCP soket nesnesi oluşturmak için socket.socket() işlevini kullanın.

# create a socket object
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Sunucu soketine bağlanma

Bağlanabilmek için sunucunun IP adresini ve portunu belirtin. Bunlar, daha önce server.py içinde ayarladığınız IP adresi ve portla eşleşmelidir.

server_ip = "127.0.0.1"  # replace with the server's IP address
server_port = 8000  # replace with the server's port number

İstemci soketi nesnesi üzerinde connect yöntemiyle sunucuyla bir bağlantı kurun. İstemci soketini herhangi bir IP adresine veya porta bağlamadığımıza dikkat edin. Bu, istemci için normaldir; çünkü connect, otomatik olarak boş bir port seçecek ve sistemin ağ arayüzlerinden sunucuya en iyi yolu sağlayan bir IP adresi (127.0.0.1 bizde) alarak istemci soketini bunlara bağlayacaktır.

# establish connection with server
client.connect((server_ip, server_port))

İletişim döngüsü oluşturma

Bağlantı kurulduktan sonra, sunucuya birden fazla mesaj göndermek için sonsuz bir iletişim döngüsü başlatırız. Kullanıcıdan Python'un yerleşik input işleviyle girdi alır, sonra bunu baytlara kodlar ve en fazla 1024 bayta kırparız. Ardından mesajı client.send ile sunucuya göndeririz.

while True:
   # input message and send it to the server
   msg = input("Enter message: ")
   client.send(msg.encode("utf-8")[:1024])

Sunucu yanıtını işleme

Sunucu istemciden bir mesaj aldığında buna yanıt verir. Artık istemci kodumuzda, sunucunun yanıtını almak istiyoruz. Bunun için iletişim döngüsü içinde, en fazla 1024 bayt okumak üzere recv yöntemini kullanırız. Sonra yanıtı decode ile baytlardan dizeye çevirir ve ”closed” değerine eşit olup olmadığını kontrol ederiz. Öyleyse, döngüden çıkarız; birazdan göreceğimiz gibi bu, istemcinin bağlantısını sonlandıracaktır. Aksi hâlde sunucunun yanıtını konsola yazdırırız.

# receive message from the server
response = client.recv(1024)
response = response.decode("utf-8")

# if server sent us "closed" in the payload, we break out of the loop and close our socket
if response.lower() == "closed":
   break

print(f"Received: {response}")

Kaynakları serbest bırakma

Son olarak, while döngüsünden sonra, close yöntemiyle istemci soketi bağlantısını kapatın. Bu, kaynakların düzgünce serbest bırakılmasını ve bağlantının sonlandırılmasını sağlar (ör. “closed” mesajını aldığımızda while döngüsünden çıktığımızda).

# close client socket (connection to the server)
client.close()
print("Connection to server closed")

Not: Yine, yukarıda uyguladığımız run_client işlevini dosyanın sonunda aşağıdaki gibi çağırmayı unutmayın:

run_client()

Tam istemci soketi kod örneği

İşte tam client.py kodu:

import socket

def run_client():
    # create a socket object
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    server_ip = "127.0.0.1"  # replace with the server's IP address
    server_port = 8000  # replace with the server's port number
    # establish connection with server
    client.connect((server_ip, server_port))

    while True:
        # input message and send it to the server
        msg = input("Enter message: ")
        client.send(msg.encode("utf-8")[:1024])

        # receive message from the server
        response = client.recv(1024)
        response = response.decode("utf-8")

        # if server sent us "closed" in the payload, we break out of the loop and close our socket
        if response.lower() == "closed":
            break

        print(f"Received: {response}")

    # close client socket (connection to the server)
    client.close()
    print("Connection to server closed")

run_client()

İstemcinizi ve sunucunuzu test edin

Yukarıda yazdığımız sunucu ve istemci uygulamasını test etmek için şunları yapın:

  • Aynı anda iki terminal penceresi açın.
  • Bir terminal penceresinde, server.py dosyasının bulunduğu dizine gidin ve sunucuyu başlatmak için aşağıdaki komutu çalıştırın:
python server.py

Bu, sunucu soketini localhost adresine (127.0.0.1) 8000 portundan bağlayacak ve gelen bağlantıları dinlemeye başlayacaktır.

  • Diğer terminalde, client.py dosyasının bulunduğu dizine gidin ve istemciyi başlatmak için aşağıdaki komutu çalıştırın:
python client.py

Bu, kullanıcı girdisi isteyecektir. Mesajınızı yazıp Enter'a basabilirsiniz. Bu, girdinizi sunucuya iletir ve sunucunun terminal penceresinde görüntüler. Sunucu yanıtını istemciye gönderir ve istemci sizden tekrar giriş ister. Bu, sunucuya ”close” dizesini gönderene kadar devam edecektir.

Birden Fazla İstemciyle Çalışma – Çok İş Parçacıklılık

Önceki örnekte bir sunucunun tek bir istemciden gelen isteklere nasıl yanıt verdiğini gördük; ancak pratikte çoğu durumda, birçok istemcinin aynı anda tek bir sunucuya bağlanması gerekebilir. İşte burada çok iş parçacıklılık devreye girer. Çok iş parçacıklılık, birden fazla görevi (ör. birden fazla işlevi yürütmek) eşzamanlı olarak ele almanız gerektiğinde kullanılır.

Fikir, işlemci tarafından ele alınabilen bağımsız bir talimatlar dizisi olan bir iş parçacığı (thread) oluşturmaktır. İş parçacıkları, aslında bir sürecin içinde yaşadıkları ve kendileri için çok fazla kaynak ayırmanız gerekmediği için süreçlere kıyasla çok daha hafiftir.

Python'da çok iş parçacıklılığın sınırlamaları

Varsayılan CPython derlemesinde, global yorumlayıcı kilidi (GIL) nedeniyle aynı anda yalnızca bir iş parçacığı Python bayt kodu çalıştırır. Soket programlama için bu sorun değildir — GIL, ağ G/Ç sırasında serbest bırakılır; böylece iş parçacıkları gerçekten bağlantıları paralel olarak bekleyebilir. Ekim 2024 itibarıyla Python 3.13'te (PEP 703) GIL'i kaldıran serbest iş parçacıklı bir derleme de sunulmaktadır ve asyncio yüksek eşzamanlılığa sahip soket kodu için daha yaygın modern tercihtir.

Çok iş parçacıklı sunucu örneği

Aşağıdaki örnekte, çok sayıda istemciyi ele almak için sunucunuza çok iş parçacıklılığı nasıl ekleyebileceğinize bakalım. Bu kez try-except-finally bloklarıyla temel bir hata yönetimi de ekleyeceğimizi unutmayın. Başlamak için şu adımları izleyin:

İş parçacığı oluşturan sunucu işlevi oluşturma

Python dosyanızda, hem soketler hem de iş parçacıklarıyla çalışabilmek için socket ve threading modüllerini içe aktarın:

import socket
import threading

run_server işlevini tanımlayın; tıpkı yukarıdaki örnekte olduğu gibi bir sunucu soketi oluşturacak, bağlayacak ve gelen bağlantıları dinleyecek. Ardından sonsuz bir while döngüsünde accept çağırın. Bu, her zaman yeni bağlantıları dinlemeyi sürdürür. accept bir bağlantı aldığında ve döndüğünde, threading.Thread yapıcısını kullanarak bir iş parçacığı oluşturun. Bu iş parçacığı, birazdan tanımlayacağımız handle_client işlevini yürütecek ve ona client_socket ile addr'i argüman olarak geçecektir (addr ikilisi, bağlanan istemcinin IP adresi ve portunu tutar). İş parçacığını oluşturduktan sonra, yürütmeyi başlatmak için start çağrısı yaparız.

accept çağrısının engelleyici olduğunu unutmayın; bu nedenle while döngüsünün ilk yinelemesinde accept satırına geldiğimizde, başka hiçbir şey yürütmeden bir istemci bağlantısını bekleriz. İstemci bağlanır bağlanmaz, accept yöntemi döner ve yürütmeye devam ederiz: söz konusu istemciyi ele alacak bir iş parçacığı oluşturur ve ardından bir sonraki yinelemeye geçeriz; burada yeniden accept çağrısında başka bir istemcinin bağlanmasını bekleyerek dururuz.

İşlevin sonunda, beklenmedik bir şey olursa sunucu soketinin her zaman kapatılmasını sağlayan bir hata yönetimi bulunur.

def run_server():
    server_ip = "127.0.0.1"  # server hostname or IP address
    port = 8000  # server port number
    # create a socket object
    try:
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # bind the socket to the host and port
        server.bind((server_ip, port))
        # listen for incoming connections
        server.listen()
        print(f"Listening on {server_ip}:{port}")

        while True:
            # accept a client connection
            client_socket, addr = server.accept()
            print(f"Accepted connection from {addr[0]}:{addr[1]}")
            # start a new thread to handle the client
            thread = threading.Thread(target=handle_client, args=(client_socket, addr,))
            thread.start()
    except Exception as e:
        print(f"Error: {e}")
    finally:
        server.close()

Örneğimizdeki sunucu yalnızca beklenmedik bir hata oluşursa duracaktır. Aksi hâlde, süresiz olarak istemcileri dinler; durdurmak isterseniz terminali sonlandırmanız gerekir.

Ayrı iş parçacığında çalışacak istemci işleme işlevi oluşturma

Şimdi, run_server işlevinin üstünde, handle_client adlı başka bir işlev tanımlayın. Bu işlev, her istemci bağlantısı için ayrı bir iş parçacığında çalışacaktır. Argüman olarak, istemcinin soket nesnesini ve addr ikilisini alır.

Bu işlev içinde, tek iş parçacıklı örnekte yaptığımızın aynısını, buna ek olarak hata yönetimiyle yaparız: recv kullanarak istemciden mesaj almak için bir döngü başlatırız.

Ardından kapatma mesajı alıp almadığımızı kontrol ederiz. Aldıysak, ”closed” dizesiyle yanıt verir ve döngüden çıkarak bağlantıyı kapatırız. Aksi hâlde, istemcinin istek dizesini konsola yazdırır ve bir sonraki istemci mesajını almak üzere döngünün bir sonraki yinelemesine geçeriz.

Bu işlevin sonunda, beklenmedik durumlar için bir hata yönetimi (except bölümü) ve client_socket'i close ile serbest bırakmayı garanti eden bir finally bölümü bulunur. Bu finally bölümü, ne olursa olsun her zaman çalışır; bu da istemci soketinin daima düzgün şekilde serbest bırakılmasını sağlar.

def handle_client(client_socket, addr):
    try:
        while True:
            # receive and print client messages
            request = client_socket.recv(1024).decode("utf-8")
            if request.lower() == "close":
                client_socket.send("closed".encode("utf-8"))
                break
            print(f"Received: {request}")
            # convert and send accept response to the client
            response = "accepted"
            client_socket.send(response.encode("utf-8"))
    except Exception as e:
        print(f"Error when handling client: {e}")
    finally:
        client_socket.close()
        print(f"Connection to client ({addr[0]}:{addr[1]}) closed")

handle_client döndüğünde, onu çalıştıran iş parçacığı da otomatik olarak serbest bırakılır.

Not: Dosyanızın sonunda run_server işlevini çağırmayı unutmayın.

Tam çok iş parçacıklı sunucu kodu örneği

Şimdi, çok iş parçacıklı sunucunun tam kodunu bir araya getirelim:

import socket
import threading

def handle_client(client_socket, addr):
    try:
        while True:
            # receive and print client messages
            request = client_socket.recv(1024).decode("utf-8")
            if request.lower() == "close":
                client_socket.send("closed".encode("utf-8"))
                break
            print(f"Received: {request}")
            # convert and send accept response to the client
            response = "accepted"
            client_socket.send(response.encode("utf-8"))
    except Exception as e:
        print(f"Error when hanlding client: {e}")
    finally:
        client_socket.close()
        print(f"Connection to client ({addr[0]}:{addr[1]}) closed")

def run_server():
    server_ip = "127.0.0.1"  # server hostname or IP address
    port = 8000  # server port number
    # create a socket object
    try:
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # bind the socket to the host and port
        server.bind((server_ip, port))
        # listen for incoming connections
        server.listen()
        print(f"Listening on {server_ip}:{port}")

        while True:
            # accept a client connection
            client_socket, addr = server.accept()
            print(f"Accepted connection from {addr[0]}:{addr[1]}")
            # start a new thread to handle the client
            thread = threading.Thread(target=handle_client, args=(client_socket, addr,))
            thread.start()
    except Exception as e:
        print(f"Error: {e}")
    finally:
        server.close()

run_server()

Not: Gerçek dünya kodlarında, çok iş parçacıklı sunucularla çalışırken yarış koşulları veya veri tutarsızlıkları gibi olası sorunları önlemek için iş parçacığı güvenliği ve eşzamanlama tekniklerini dikkate almak hayati önem taşır. Bizim basit örneğimizde bu bir sorun değildir.

Temel hata yönetimli istemci örneği

Artık aynı anda birden fazla istemciyi ele alabilen bir sunucu uygulamamız olduğuna göre, bağlantı başlatmak için yukarıdaki ilk temel örnekteki istemci uygulamasını kullanabilir veya onu biraz güncelleyip hata yönetimi ekleyebiliriz. Aşağıda, önceki istemci örneğiyle aynı olan, ancak try-except bloklarının eklendiği kodu bulabilirsiniz:

import socket

def run_client():
    # create a socket object
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    server_ip = "127.0.0.1"  # replace with the server's IP address
    server_port = 8000  # replace with the server's port number
    # establish connection with server
    client.connect((server_ip, server_port))

    try:
        while True:
            # get input message from user and send it to the server
            msg = input("Enter message: ")
            client.send(msg.encode("utf-8")[:1024])

            # receive message from the server
            response = client.recv(1024)
            response = response.decode("utf-8")

            # if server sent us "closed" in the payload, we break out of
            # the loop and close our socket
            if response.lower() == "closed":
                break

            print(f"Received: {response}")
    except Exception as e:
        print(f"Error: {e}")
    finally:
        # close client socket (connection to the server)
        client.close()
        print("Connection to server closed")

run_client()

Çok iş parçacıklı örneği test etme

Çoklu istemci uygulamasını test etmek isterseniz, istemciler için birkaç terminal penceresi ve sunucu için bir tane açın. Önce sunucuyu python server.py ile başlatın. Ardından birkaç istemciyi python client.py ile başlatın. Sunucu terminal penceresinde yeni istemcilerin sunucuya bağlandığını göreceksiniz. Şimdi ilgili terminallere metin girerek farklı istemcilerden mesaj göndermeye devam edebilirsiniz; hepsi sunucu tarafında ele alınıp konsola yazdırılacaktır.

Veri Biliminde Soket Programlama Uygulamaları

Her ağ uygulaması perde arkasında işletim sistemi tarafından oluşturulan soketleri kullansa da, özel kullanım durumları için veya performansı artırmak amacıyla özellikle soket programlamasına büyük ölçüde dayanan sayısız sistem vardır. Peki soket programlama veri bilimi bağlamında nasıl kullanışlıdır? Büyük miktarda veriyi hızlıca alıp göndermek gerektiğinde anlamlı bir rol oynar. Bu nedenle, soket programlama ağırlıkla veri toplama ve gerçek zamanlı işleme, dağıtık hesaplama ve süreçler arası iletişim için kullanılır. Gelin, veri bilimi alanındaki bazı somut uygulamalara daha yakından bakalım.

Gerçek zamanlı veri toplama

Soketler, farklı kaynaklardan gerçek zamanlı veriyi toplayıp işlemek, bir veritabanına ya da analitik hattına aktarmak vb. için yaygın biçimde kullanılır. Örneğin, bir finans sistemi veya sosyal medya API'sinden anında veri almak ve ardından veri bilimciler tarafından işlenmesini sağlamak için soket kullanılabilir.

Dağıtık hesaplama

Veri bilimciler, çok büyük veri kümelerinin işlenmesini ve hesaplamasını birden fazla makineye dağıtmak için soket bağlantısını kullanabilir. Soket programlama, düğümler arasındaki iletişim için Apache Spark ve diğer dağıtık hesaplama çatıları tarafından yaygın olarak kullanılır.

Model sunumu

Makine öğrenimi modellerini kullanıcılara sunarken soket programlama kullanılabilir; böylece tahmin ve öneriler anında iletilebilir. Gerçek zamanlı karar almayı kolaylaştırmak için, veri bilimciler büyük miktarda veriyi alan, bunu eğitilmiş modellerle işleyip tahminler üreten ve bulguları hızla istemciye döndüren, performanslı soket tabanlı sunucu uygulamalarından yararlanabilir.

Süreçler Arası İletişim (IPC)

Soketler, aynı makinede çalışan farklı süreçlerin birbirleriyle iletişim kurup veri alışverişi yapmasına imkân veren IPC için kullanılabilir. Bu, karmaşık ve kaynak yoğun hesaplamaları birden fazla sürece dağıtmakta veri bilimi açısından faydalıdır. Nitekim Python'un alt süreç (subprocessing) kütüphanesi bu amaçla sıklıkla kullanılır: ağır hesaplamalar yapılırken birden çok işlemci çekirdeğinden yararlanmak ve uygulama performansını artırmak için birkaç süreç oluşturur. Bu tür süreçler arasındaki iletişim IPC soketleri üzerinden uygulanabilir.

İşbirliği ve iletişim

Soket programlama, veri bilimciler arasında gerçek zamanlı iletişim ve işbirliğini mümkün kılar. Etkili işbirliği ve bilgi paylaşımını kolaylaştırmak için soket tabanlı sohbet uygulamaları veya işbirlikçi veri analizi platformları kullanılır.

Yukarıdaki uygulamaların çoğunda, veri bilimcilerin doğrudan soketlerle çalışmayabileceğini belirtmek gerekir. Genellikle soket programlamanın tüm düşük seviyeli ayrıntılarını soyutlayan kütüphane, çatı ve sistemleri kullanırlar. Ancak perde arkasında bu tür çözümlerin tümü soket iletişimine dayanır ve soket programlamadan yararlanır.

Soket Programlamada Zorluklar ve En İyi Uygulamalar

Soketler bağlantı yönetiminin düşük seviyeli bir kavramı olduğundan, onlarla çalışan geliştiricilerin sağlam ve güvenilir uygulamalar oluşturmak için etrafında gerekli altyapının tamamını uygulaması gerekir. Bu elbette pek çok zorluğu beraberinde getirir. Ancak bu sorunların üstesinden gelmek için izlenebilecek bazı en iyi uygulamalar ve genel yönergeler vardır. Aşağıda soket programlamada en sık karşılaşılan sorunlardan bazıları ve genel ipuçları yer almaktadır:

Bağlantı yönetimi

Aynı anda birçok bağlantıyla çalışmak; birden fazla istemciyi yönetmek ve eşzamanlı isteklerin verimli şekilde ele alındığından emin olmak kesinlikle zorlayıcı ve sıradan olmayan bir iştir. Darboğazlardan kaçınmak için dikkatli kaynak yönetimi ve koordinasyon gerekir

En iyi uygulamalar

  • Etkin bağlantıları listeler veya sözlükler gibi veri yapılarıyla takip edin. Ölçeklenebilirliğe de yardımcı olan bağlantı havuzu (connection pooling) gibi gelişmiş teknikleri kullanın.
  • Aynı anda birden çok istemci bağlantısını ele almak için çok iş parçacıklılık veya eşzamansız programlama tekniklerini kullanın.
  • Kaynakları serbest bırakmak ve bellek sızıntılarını önlemek için bağlantıları düzgünce kapatın.

Hata yönetimi

Bağlantı hataları, zaman aşımı ve veri iletim sorunları gibi hatalarla başa çıkmak kritik önemdedir. Bu hataları ele almak ve istemcilere uygun geri bildirim sağlamak, özellikle düşük seviyeli soket programlaması yaparken zor olabilir.

En iyi uygulamalar

  • Belirli hata türlerini yakalayıp ele almak için try-except-finally blokları kullanın.
  • Bilgilendirici hata mesajları sağlayın ve sorun giderimine yardımcı olması için günlük kaydı (logging) kullanmayı düşünün.

Ölçeklenebilirlik ve performans

Yüksek hacimli veri akışları veya gerçek zamanlı uygulamalarla uğraşırken, optimum performansı sağlamak ve gecikmeyi en aza indirmek temel kaygılardır.

En iyi uygulamalar

  • Gereksiz veri işleme ve ağ ek yükünü en aza indirerek kodunuzu performans için optimize edin.
  • Büyük veri aktarımlarını verimli şekilde ele almak için arabellekleme (buffering) tekniklerini uygulayın.
  • İstemci isteklerini birden fazla sunucu örneğine dağıtmak için yük dengeleme tekniklerini değerlendirin.

Güvenlik ve kimlik doğrulama

Soket tabanlı iletişimi güvenceye almak ve uygun kimlik doğrulama mekanizmaları uygulamak zor olabilir. Veri gizliliğini sağlamak, yetkisiz erişimi önlemek ve kötü niyetli etkinliklere karşı koruma sağlamak için güvenli protokollerin dikkatle ele alınması ve uygulanması gerekir.

En iyi uygulamalar

  • Verileri şifreleyerek güvenli iletim sağlamak için SSL/TLS güvenlik protokollerini kullanın.
  • İstemci kimliğini doğrulamak için belirteç (token) tabanlı kimlik doğrulama, açık anahtarlı kriptografi veya kullanıcı adı/parola gibi güvenli yöntemler uygulayın.
  • Parolalar veya API anahtarları gibi gizli verilerin korunduğundan ve şifrelendiğinden emin olun; ideal olarak hiç saklamayın (gerekirse yalnızca özetleri).

Ağ güvenilirliği ve dayanıklılığı

Ağ kesintileri, dalgalanan bant genişliği ve güvenilmez bağlantılarla uğraşmak zorluklar doğurabilir. Kararlı bir bağlantıyı sürdürmek, bağlantı kesilmelerini zarifçe ele almak ve yeniden bağlantı mekanizmalarını uygulamak, sağlam ağ uygulamaları için esastır.

En iyi uygulamalar

  • Etkin olmayan veya düşen bağlantıları tespit etmek için keep-alive mesajları kullanın.
  • Süresiz bloklamayı önlemek ve zamanında yanıt işlemesini sağlamak için zaman aşımı uygulatın.
  • Bağlantı kesilirse yeniden kurmak için üstel geri çekilme (exponential backoff) ile yeniden bağlanma mantığı uygulayın.

Kod sürdürülebilirliği

Son olarak, ama en az onun kadar önemli olan konu kod sürdürülebilirliğidir. Soket programlamanın düşük seviyeli doğası nedeniyle, geliştiriciler kendilerini daha fazla kod yazarken bulur. Bu, hızla sürdürülemez bir spagetti koda dönüşebilir; bu yüzden kodu mümkün olduğunca erken organize edip yapılandırmak ve mimari planlamaya ekstra çaba harcamak gerekir.

En iyi uygulamalar

  • Kodunuzu, ideal olarak çok uzun olmayan sınıflara veya işlevlere bölün.
  • İstemci ve sunucu uygulamalarınızı taklit ederek (mock) birim testlerini erken yazın.
  • Mutlaka soket programlaması kullanmanız gerekmiyorsa, bağlantılarla uğraşmak için daha üst düzey kütüphaneleri kullanmayı değerlendirin.

Özet: Python'da soket programlama

Soketler, tüm ağ uygulamalarının ayrılmaz bir parçasıdır. Bu makalede, Python'da soket programlamayı inceledik. Akılda tutulması gereken başlıca noktalar şunlardır:

  • Soketler, bağlantı yönetimini soyutlayan arayüzlerdir.
  • Soketler, farklı süreçler (genellikle bir istemci ve bir sunucu) arasında yerel olarak veya ağ üzerinden iletişimi mümkün kılar.
  • Python'da soketlerle çalışma, diğerlerinin yanı sıra recv, send, listen, close gibi yöntemlere sahip bir soket nesnesi sunan socket kütüphanesi aracılığıyla yapılır.
  • Soket programlamanın veri toplama, süreçler arası iletişim ve dağıtık hesaplama dâhil veri bilimi için faydalı çeşitli uygulamaları vardır.
  • Soket programlamadaki zorluklar arasında bağlantı yönetimi, veri bütünlüğü, ölçeklenebilirlik, hata yönetimi, güvenlik ve kod sürdürülebilirliği yer alır.

Soket programlama becerileriyle geliştiriciler, verimli ve gerçek zamanlı ağ uygulamaları oluşturabilir. Kavramları ve en iyi uygulamaları özümseyerek, güvenilir ve ölçeklenebilir çözümler geliştirmek için soket programlamanın tüm potansiyelinden yararlanabilirler.

Bununla birlikte, soket programlama çok düşük seviyeli bir tekniktir; uygulama mühendislerinin uygulama iletişiminin en küçük ayrıntılarını bile hesaba katması gerektiğinden kullanımı zordur.

Günümüzde, uygulamadan gerçek anlamda en yüksek performansı almak veya onu ölçeklendirmek gerekmediği sürece, çoğu zaman doğrudan soketlerle çalışmamıza gerek kalmaz; çünkü genellikle üst düzey kütüphane ve çerçeveler tarafından ele alınırlar.

Bununla birlikte, soketleri anlamak ve perde arkasında işlerin nasıl yürüdüğüne dair içgörü sahibi olmak, bir geliştirici veya veri bilimci olarak genel farkındalığı artırır ve her zaman iyi bir fikirdir.

Python'un ağ analizi alanındaki rolü hakkında daha fazla bilgi edinmek için Intermediate Network Analysis in Python kursumuza göz atın. Python programlama becerilerinizi geliştirmek için Python Programming beceri yolumuzu da takip edebilirsiniz.

Serhii Orlivskyi

Yazar

Serhii Orlivskyi

Serhii, çoğunlukla DBMS, veritabanına özgü diller, Python, Nodejs vb. gibi backend teknolojilerinde 3 yıllık deneyime sahip bir yazılım mühendisidir. Ayrıca VueJS ve ReactJS gibi frontend çerçeveleriyle de deneyimi vardır.

SSS

Python'da socket programlama ne için kullanılır?

Python'da socket programlama, uygulamaların bir ağ üzerinden veya yerel süreçler arasında veri alışverişi yapmasına olanak tanır. Yaygın kullanım alanları arasında sohbet uygulamaları, çok oyunculu oyunlar, IoT iletişimi, dağıtık hesaplama ve makine öğrenimi modellerinin sunulması yer alır. Yerleşik socket kütüphanesi hem istemci hem sunucu kodunu yönetir.

Python'da TCP ve UDP soketleri arasındaki fark nedir?

TCP soketleri (socket.SOCK_STREAM), hata denetimiyle birlikte güvenilir ve sıralı teslimat sağlar — sohbet uygulamaları, dosya aktarımı ve web sunucuları için idealdir. UDP soketleri (socket.SOCK_DGRAM) daha hızlıdır ancak güvenilir değildir; "gönder ve unut" bağlantıları video akışı, oyun ve IoT sensörleri için uygundur.

Ağ programlaması için Python soketlerini mi yoksa asyncio'yu mu kullanmalıyım?

Ağ hakkında ayrıntılı denetim gerektiğinde veya ağın nasıl çalıştığını öğrenirken ham soketleri kullanın. Üretim kodunun çoğu için asyncio kullanın — binlerce eşzamanlı bağlantıyı iş parçacıklarından daha verimli yönetir. FastAPI ve aiohttp, asyncio üzerinde inşa edilmiştir.

Python soketleriyle birden fazla istemciyi nasıl ele alırım?

En basit yaklaşım çok iş parçacıklılıktır — her bağlantı için threading.Thread ile yeni bir iş parçacığı başlatın. Daha yüksek eşzamanlılık için, tek bir iş parçacığında çok sayıda istemciyi ele almak üzere asyncio kullanın. Üretimde, standart kitaplıktaki socketserver modülü hazır ThreadingTCPServer ve ForkingTCPServer sınıflarını sağlar.

Python soket bağlantıları güvenli midir?

Ham Python soketleri varsayılan olarak verileri şifrelenmemiş iletir — trafiği ele geçiren herkes bunu okuyabilir. Güvenli iletişim için yerleşik ssl modülünü (ssl.SSLContext.wrap_socket()) kullanarak soketinizi TLS ile sarmalayın. Üretimde, hassas herhangi bir veri için daima TLS kullanın.

Konular

İlgili