Server-Client Uygulamaları

Ağ programlama, uygulamaların ağ ortamı üzerinden iletişimde bulunarak veri alış-verişi yapılmasına olanak verir; bu nedenle ağ programlama uygulamalarda önemli bir yer tutar. Bu konu kapsamında da temel ağ uygulamalarından olan;

– Echo istemci ve sunucusu,

– Chat istemci ve sunucusu,

– Web sunucusu son olarak ise

– Whatsapp benzeri metinsel bilgi iletişimi sağlayan bir uygulamanın

Python dili ile gerçekleştirilmesi istenmiştir. Hazırlanılan uygulamaların anlaşılmasını kolaylaştırmak için öncelikle temel bazı kavramların tanımları bilinmelidir.

Yerel Bölge Ağı (LAN);

Yerel bölge ağları, bilgisayarları ve diğer bilgi işlem araçlarını kısıtlı bir fiziksel çevre dahilinde birbirine bağlar. Bu alanlara örnek olarak bir ofis, bir bina, bir işletme tesisi gösterilebilir.

IP Adres (Internet Protocol Address);

Temel olarak IP Adresi, internet ağı (Wide Area Network, WAN), Yerel Ağ (Local Area Network, LAN), Kablosuz Yerel Ağ (Wireless Local Arean Network, WLAN) vb. ağlar üzerindeki cihazlara verilen adres, isim, numaradır. Ağ üzerinde veri alıp vermek isteyen her cihazın mutlaka bir IP adresine sahip olması gerekir. İnternette iki cihaz aynı IP adresine sahip olamaz.

IPv4;

Halen kullanılmakta olan standart İnternet protokolüdür ve 32 bitten, başka bir ifadeyle sekiz bitlik 4 rakamdan oluşur. Bu rakamlar, 0 ila 255 arasında değişir. IPv4 protokolündeki bir adres 1.0.0.0 ila 255.255.255.255 arasında herhangi bir numara olabilir. 4,294,967,296 adet IP Adres olabilir. Ama IP adreslerinin sınıflara ayrılmış olması, bazı adreslerin çoklu gönderim (Multicasting) için, bazılarının da test amaçlı kullanılmasından dolayı bu sayı 3,2 milyara kadara düşmektedir.

Port;

Her IP Adresi portlara yani sanal veri yollarına bölünmüştür. Bu sayede aynı anda, aynı IP Adresinden (bilgisayar vb.) farklı programlarla veri alışverişi yapılabilmektedir. Örneğin aynı anda, aynı bilgisayardan 110 nolu portu kullanarak Outlook Expressle e-maillerimizi indirirken, 80 nolu portu kullanarak Internet Tarayıcısı ile web sitelerine bakabiliyoruz. Port noları 0-65535 arasında değerler alabiliyor.

Sunucu (Server);

Bilgisayar ağlarında, istemcilerin (kullanıcıların) erişebileceği, kullanımına ve paylaşımına açık kaynakları veya bazı servisleri (FTP, E-Posta, Web Sitesi) çalıştıran bilgisayar birimlerine verilen genel bir addır. Sunucu bilgisayar sunduğu servise göre de Web sitesi yayınlıyorsa Web Sunucusu, Eğer posta servislerini yayınlıyorsa E-Posta Sunucusu veya Veritabanı sunucusu olarak isimlendirilirler. Sunucu aynı anda yüzlerce farklı kullanıcıdan gelen isteklere yanıt vereceği için performans olarak masa üstü bilgisayarlarımızdan daha hızlı ve daha geniş depolama alanları içerir.

İstemci (Client);

Bir ağ üzerinde, sunucu bilgisayarlardan hizmet alan kullanıcı bilgisayarlarıdır. Bilgiye erişim yetkileri sunucu tarafından belirlenir. Eğer bir bilgisayardan Internete bağlanılarak web siteleri ziyaret ediliyorsa o bilgisayar İstemci(Client) bilgisayardır. Yani İstemci bilgisayarlar Sunucu bilgisayarlara bağlanarak onlardan bilgi alırlar.

Soket;

Soketler, süreçler arası iletişim için kullanılır. Interprocess iletişimi istemci-sunucu modeline dayanır. Bu durumda, istemci-sunucu birbiriyle etkileşime giren uygulamalardır. İstemci ve sunucu arasındaki etkileşim için bir bağlantı gereklidir. Soket programlaması, etkileşime girecek uygulamalar arasındaki bağlantıyı kurmaktan sorumludur. Kısacası ağ iletişimini temin eden bir uyarlamadır.

Echo İstemci ve Sunucu Uygulaması

Echo istemci ve sunucu uygulamasında gerçekleştirilen sunucunun istemci bağlantısını kabul ettikten sonra istemciden gelen tüm mesajları aynı şekilde istemciye geri göndermesi gerekmektedir. Bunun için öncelikle sunucu ve istemci yazılımları yapılmıştır. Sunucu yazılımı aşağıdaki şekildedir ;

import socket

# Bir tane socket nesnesi oluşturulur.
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Yerel makinenin ismi alınır.
host = socket.gethostname()
# Servis için bir tane port numarası belirlenir.
port = 14531
# Yerel makine ismi ile port numarası bağlanır.
s.bind((host, port)) #s soketimiz
# İstemci bağlantısı için port dinlemesi başlatılır.
s.listen(10)  #10 istemci bağlanabilir.
# istemci ve adresi kabul edilir.
c, addr = s.accept() #c client adresi
# Bağlanan istemciye mesaj gönderilir.
c.sendall(bytes("Sunucu baglantisi kuruldu.".encode("utf-8")))
# Bağlantı adresi string halinde sunucu ekranına bastırılır.
print('{} bağlandı.'.format(addr))
# Sunucunun sürekli açık kalması için sonsuz döngüye ihtiyacımız var.
# Veya mesaj adediyle veya başka parametrelerle döngü sonlu da olabilir...
while True:
    # İstemciden gelen, ara bellek boyutu 1024 olan,
    # byte tipindeki mesaj stringe dönüştürülür.
    # Bu string'in ilk elemanı hariç diğer elemanları data isminde
    # bir değişkene atanır.
    data = str(c.recv(1024))[1:]
    # Eğer istemciden mesaj gelmişse
    if data:
        # İstemcinin mesajını bastır.
        print("İstemci: {}".format(data))
        # İstemciye gelen mesajı yanıt olarak geri gönderiyoruz.
        respond = data.encode("utf-8")
        # İstemciye mesajı gönder
        c.sendall(bytes(respond))

Burada “socket” kütüphanesi kullanılmış ve işlemlerin çoğu bu kütüphanenin hazır fonksiyonları ile sağlanmıştır. Bağlantının sağlanabilmesi için bir soket oluşturulmuş ve port numarası belirlenmiştir. “listen()” fonksiyonu ile gelen bağlantılar dinlenir ve “accept()” fonksiyonu ile kabul edilir. Bu uygulama şuan için sadece yerel ağ üzerinde çalışmaktadır. Sunucunun sürekli çalışması için sonsuz döngü kullanılmıştır. İstemci bağlantısı sağlandıktan sonra gelen mesajlar data nesnesi içerisinde tutulur ve “sendall()” fonksiyonu ile istemciye geri gönderilmiştir. Uygulamanın istemci tarafı yazılımı aşağıdaki şekildedir;

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
port = 14531 #aynı port numarasını veriyoruz.

# Sunucuya bağlanalım.
s.connect((host, port))

# İstemci sunucuya bağlandıktan sonra 
# açık kalması için sonsuz döngü oluşturuyoruz.
while True:
    data = str(s.recv(1024))[1:] #data nesnesi oluşturuyoruz.
    if data:
        print("Sunucu: {}".format(data)) #ekrana yazdır ve sunucuya da gönder 
        respond = input("Istemci: ").encode("utf-8")
        # q girişi olursa kapat.
        if respond == b"q":
            exit()
        else: #q değilse mesajı gönder 
            s.sendall(bytes(respond))

İstemci kısmında da socket kütüphanesi kullanılmıştır ve oluşturulan port numarası sabit olarak buraya da eklenmiştir. “connect()” fonksiyonu ile sunucuyla bağlantı sağlanır. Bağlantının açık kalması için döngü oluşturulur ve sunucudan gelen mesajlar “input()” fonksiyonu ile alınıp sonrasında ekrana yazdırılmıştır. Uygulama çalıştırılırken önce sunucu uygulaması çalıştırılmalı sonrasında istemci uygulaması çalıştırılmalıdır. Çalışması aşağıdaki şekildedir ;

 

Yukarıda da görüldüğü gibi sunucu istemciyi ip adresi ile tanır ve işlevsel olarak gelen mesajları geri göndermektedir.

Chat İstemci ve Sunucu Uygulaması

Chat istemci ve sunucu uygulamasında sunucunun birden fazla istemciye hizmet vermesi gerekmektedir. Bağlı olan iki veya daha çok istemci arasında mesaj iletimi işlemini gerçekleştirir. Bu uygulama da sunucu ve istemci tarafı olmak üzere iki parçadan oluşur. Sunucu tarafı aşağıdaki şekildedir;

#Çok thereadli mesajlaşma uygulaması server 

from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread #gerekli kütüphaneler

#gelen bağlantıları kabul edecek fonksiyon
def myHandler():
    while True:
        client, client_address = SERVER.accept()
        print("%s:%s Baglandi." % client_address)
        client.send(bytes("Kullanici adinizi yazip enter'a tiklayiniz.", "utf8"))
        addresses[client] = client_address
        Thread(target=handle_client, args=(client,)).start() #birçok kullanıcı için işlem yapabilmemizi sağlar.


def handle_client(client):  # İstemci bilgilerini al
    name = client.recv(BUFSIZ).decode("utf8")
    welcome = 'Hos geldiniz %s ! Eger cıkmak isterseniz, {ayril} yaziniz.' % name
    client.send(bytes(welcome, "utf8"))
    msg = "%s Sohbete katildi." % name
    broadcast(bytes(msg, "utf8"))
    clients[client] = name

    while True:
        msg = client.recv(BUFSIZ)
        if msg != bytes("{ayril}", "utf8"):
            broadcast(msg, name+": ")
        else:
            client.send(bytes("{ayril}", "utf8"))
            client.close()
            del clients[client]
            broadcast(bytes("%s Sohbetten ayrildi." % name, "utf8"))
            break


def broadcast(msg, userName=""): #userName bağlantıda ilk yazılan kullanıcı adı.
    for sock in clients:
        sock.send(bytes(userName, "utf8")+msg) # Bağlı olan bütün istemcilere mesajı gönder

        
clients = {} #istemcilerin listesi
addresses = {} #bağlantı adreslerinin listesi

HOST = ''
PORT = 33000 #Bağlantı için belirlediğimiz port numarası
BUFSIZ = 1024
ADDR = (HOST, PORT)

SERVER = socket(AF_INET, SOCK_STREAM)
SERVER.bind(ADDR)

if __name__ == "__main__":
    SERVER.listen(5) #en fazla 5 istemciye hizmet verir
    print("Baglanti icin bekleniyor...")
    ACCEPT_THREAD = Thread(target=myHandler)
    ACCEPT_THREAD.start()
    ACCEPT_THREAD.join()
    SERVER.close()

Uygulamada serverın birden çok istemciye hizmet verirken daha hızlı çalışmasını sağlayabilmek için Thread yapısı kullanılmıştır. Gelen bağlantıları yakalamak ve mesaj iletimini sağlamak için fonksiyonlar yazıldı ve son kısımda yukarıdaki uygulamada da olduğu gibi port numarası belirlenip soket nesnesi oluşturulmuştur. Birden çok istemci bulunacağı için gerekli adres ve isim bilgileri listeler ile saklanır. İstemci kod kısmı ise aşağıdaki şekildedir;

#Çok threadli mesajlaşma uygulaması istemci
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
import tkinter #mesajlaşma penceresi oluşturabilmek için kullandığımız kütüphane

#gelen mesajları almak için fonksiyon
def receive():
    while True:
        try:
            msg = client_socket.recv(BUFSIZ).decode("utf8")
            msg_list.insert(tkinter.END, msg)#mesaj ekranından ekle
        except OSError:  # istemci mesaj ekranından ayrılırsa çık.
            break


def send(event=None):  # Mesaj göndermek için fonksiyon
    msg = my_msg.get()
    my_msg.set("")  #mesaj gönderildikten sonra yazma kısmını temizle
    client_socket.send(bytes(msg, "utf8"))
    if msg == "{ayril}": #quit mesajı gelirse kapat
        client_socket.close()
        top.quit()


def on_closing(event=None): #pencere kapatılırsa çalışacak fonksiyon
    my_msg.set("{ayril}")
    send()

top = tkinter.Tk()
top.title("Sohbet Penceresi")

messages_frame = tkinter.Frame(top)
my_msg = tkinter.StringVar()  #mesaj göndermek için
my_msg.set("Mesajınızı buraya yaziniz.")
scrollbar = tkinter.Scrollbar(messages_frame)  #Gönderilen mesajları göstermek için
msg_list = tkinter.Listbox(messages_frame, height=20, width=60, yscrollcommand=scrollbar.set)
scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
msg_list.pack()
messages_frame.pack()

entry_field = tkinter.Entry(top, textvariable=my_msg)
entry_field.bind("<Return>", send)
entry_field.pack()
send_button = tkinter.Button(top, text="Gonder", command=send)
send_button.pack()

top.protocol("WM_DELETE_WINDOW", on_closing)

#istemciden gerekli bilgileri al
HOST = input('Baglanti Adresini Giriniz: ')
PORT = input('Port Numarasini Giriniz: ')
if not PORT:
    PORT = 33000
else:
    PORT = int(PORT)

BUFSIZ = 1024
ADDR = (HOST, PORT)

client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(ADDR)

receive_thread = Thread(target=receive)
receive_thread.start()
tkinter.mainloop()  

Bu kısımda farklı olarak ”tkinter” kütüphanesi yardımı ile görsel bir sohbet penceresi oluşturulmuştur.  Uygulamanın çalışması aşağıdaki şekildedir;

Uygulama sunucuya bağlanmak için istemciden adres ve port bilgilerini ister.

 

Web Sunucu Uygulaması

Bu uygulamada metin tabanlı bir web sayfasının yayınını gerçekleştiren bir web sunucusu yapılmıştır. Bunun için “http.server” kütüphanesi kullanılarak bir html sayfası sunucusu gerçekleştirilmiştir.

from http.server import BaseHTTPRequestHandler,HTTPServer #Http fonksiyonlarını kullanabilmek için kütüp. ekledik.

port = 8080 #port numarası

#sunucudan gelecek istekleri yakalamak için ve verileri tutmak için bir class oluşturuyoruz.

class myHandler(BaseHTTPRequestHandler):
	
	#Get isteklerini yakalamak için fonksiyon
	def do_GET(self):
		self.send_response(200)
		self.send_header('Content-type','text/html')
		self.end_headers()
		# Html mesajı gönder 
		#bu kısım html mesajı olduğu için düzenlemeler için html özelliklerini kullanmak gerekiyor.
		self.wfile.write("Gemlige dogru <br> Denizi goreceksin <br> Sakin sasirma. <br> At." .encode("utf-8"))
		return

try:
	#Web serverımızı oluşturuyoruz.
	server = HTTPServer(('0.0.0.0', port), myHandler)
	print ('HttpServer bu port numarasi üzerinde calisiyor : ' , port)	
	#sonsuza kadar gelecek istekler için bekle
	server.serve_forever()

except KeyboardInterrupt:
	print ('^C alindi, web sunucusu kapatildi.')
	server.socket.close()

Diğer uygulamalarda da olduğu gibi bu server için de bağlantı yolunu belirlemek amacıyla bir port numarası atanmıştır. “Do_Get()” fonksiyonu ile gelen istekler yakalanır. Kütüphanenin hazır fonksiyonları ile sayfa yayını sağlanır ve içeriği “write()” fonksiyonu ile sağlanmıştır. Bu uygulamada istemci bağlantı kurmak isteyen cihazda kullanılan tarayıcı uygulamasıdır. Tarayıcı üzerinden sunucu cihazın ip adresi  ve port numarası ile bağlantı sağlanır.

Sunucu terminalinde bağlanan cihazların ip adresleri ve 200 başarılı mesajı görülmektedir.

Mesajlaşma Uygulaması

Bu uygulama yapı olarak chat uygulaması ile benzerlik göstermektedir. Sucunun bağlı olan istemciler arasında mesaj iletimini sağlaması gerekmektedir. Farklı olarak bir web uygulaması şeklinde tasarlanmıştır. Sunucu uygulaması bulunmakta, istemciler tarayıcıları üzerinden sunucu adresi ve port numarası ile bağlantı sağlamaktadır. Uygulamanın tasarım kısmı bir html sayfası yardımıyla sağlanmıştır. Sunucu oluşturulan bu sayfayı kullanmaktadır. Tasarım kısmı aşağıdaki şekildedir;

<!DOCTYPE html>
<html lang="tr">

  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <title>Python Mesajlaşma Uygulaması</title>

    <!-- hazı Bootstrap temasını kullanıyoruz -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" 
    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

    <style>
      div.msg_bbl {
        background-color: rgb(253, 250, 42);
        padding: 5px 10px;
        border-radius: 10px;
        color: rgb(19, 20, 6);
        margin-bottom: 5px;
        margin-top: 5px;
      }
    </style>
  </head>


  <body>

    <div class="text-center well" style="background-color: #9433b742;"><b>Mesajlaşma Uygulaması</b></div>

    <div class="container">
      <div class="col-sm-4">
        <form action="" method="POST">
          <b> Mesajınızı Giriniz <span class="glyphicon glyphicon-arrow-down"></span></b>
          <div class="clearfix" style="margin-top: 5px;"></div>
          <input type="text" class="username form-control" placeholder="Kullanıcı Adı">
          <div style="padding-top: 5px;"></div>
          <input type="text" class="message form-control" placeholder="Mesaj">
          <div style="padding-top: 5px;"></div>
          <button type="submit" class="btn btn-success btn-block"> Gönder </button>
        </form>
      </div>
      <div class="col-sm-8 jumbotron" style="background-color: lightgreen;">
        <div class="no_message">
          <h2 style='color: darkslategray'>Henüz Mesaj Alınmadı...</h2>
          <div class="message_holder"></div>
        </div>
      </div>
      
    </div>


    <!-- jQuery  -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
    <script>
      var socket = io.connect( 'http://' + document.domain + ':' + location.port )
      // broadcast mesaj
      socket.on( 'connect', function() {
        socket.emit( 'my event', {
          data: 'User Connected'
        } )
        var form = $( 'form' ).on( 'submit', function( e ) {
          e.preventDefault()
          let user_name = $( 'input.username' ).val()
          let user_input = $( 'input.message' ).val()
          socket.emit( 'my event', {
            user_name : user_name,
            message : user_input
          } )
          // giriş alanı boş ise
          $( 'input.message' ).val( '' ).focus()
        } )
      } )

      // mesaj yakala
      socket.on( 'my response', function( msg ) {
        console.log( msg )
        if( typeof msg.user_name !== 'undefined' ) {
          $( 'h2' ).remove()
          $( 'div.message_holder' ).append( '<div class="msg_bbl"><b style="color: #000">'+msg.user_name+'</b> '+msg.message+'</div>' )
        }
      } )
    </script>
  </body>
</html>

Tasarım oluşturulurken Boostrap teması ve JQuery fonksiyonları kullanılmıştır. Server kısmı ise şu şekildedir;

from flask import Flask, render_template
from flask_socketio import SocketIO, emit #Flask ve SocketIO kütüphanelerini ekledik


app = Flask(__name__)

app.config[ 'SECRET_KEY' ] = 'jsbcfsbfjefebw237u3gdbdc'
socketio = SocketIO( app ) 
#socketio = SocketIO(app, cors_allowed_origins='http://localhost:8080') #bu çalışmıyor

@app.route( '/' )
def hello():
  return render_template( './Style.html' ) #stil dosyamızı ekledik

def messageRecived():
  print( 'Yeni Mesaj Alindi!!!' )

@socketio.on( 'my event' ) #json türünde 
def handle_my_custom_event( json ):
  print( 'Recived my event: ' + str( json ) )
  socketio.emit( 'my response', json, callback=messageRecived )

if __name__ == '__main__':
  #socketio.run( app, debug = True) 
  socketio.run(app, host="0.0.0.0") #cihazın yanıt vermesi çok uzun sürdü diyor. portu değiştirme!
  #socketio.run(app, debug=True, host='0.0.0.0', port=5000) #bu siteye ulaşılamıyor diyor.

   

Web tabanlı bir uygulama oluşturabilmek için flask ve socketIO frameworkleri kullanılmıştır. Uygulamanın çalışması aşağıdaki şekildedir;

Uygulama çalıştığı sırada sunucu terminalinde kurulan bağlantılar görülmektedir.

Konu kapsamında yapılan uygulamalar bu şekildedir. Uygulamaların hepsi var olan örnek uygulamalar üzerinden gerçekleştirilmiştir.

Kaynakça:


Yayımlandı

kategorisi

yazarı:

Yorumlar

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir