Python ile Socket Programlama 3
Python ile One to One Chat(1’e 1 mesajlaşma uygulaması) yapmak isterseniz Socketleri nasıl yöneteceğim ? Bir kullanıcıdan gelen mesajı nasıl diğerine aktaracağım ? gibi sorular sorabilirsiniz. Python veya herhangi bir dil için bunları cevaplar yok denecek kadar az.
Python Socket 1’e 1 Mesajlaşma Örneği(Thread ile)
1’e 1 mesajlaşma yapabilmemiz için kullanıcının ve kullanıcının mesaj attığı kişinin servera bağlı olması gerekir. Bağlı olmasının yanında bu kişilerin socket bağlantılarının bir yerde depolamamamız gerekir.
Biz burada Basit bir şekilde 2 kullanıcının birbiri ile haberleşmesini sağlayacağız. Basit bir şekilde olacağından dolayı gerçek kullanımlar için bir çok kontrol yapısı eklenmesi gerekir.
Not: Bu örnekte string ve seperatorleri kullanarak haberleşmeyi sağladık. Daha iyi bir kullanım ve kontrol için json kullanabilirsiniz.
# server.py import socket import threading HEADER = 64 IP = socket.gethostbyname(socket.gethostname()) PORT = 9999 ADDR = (IP, PORT) FORMAT = 'utf-8' DISCONNECT_MESSAGE = "DISCONNECT_SERVER_CODE" USERNAME_MESSAGE = "EXAMPLE_APP_USERNAME_FIELD" SEP = "///**//" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(ADDR) users = [] def send_message(to_username, msg): for user in users: if user['username'] == to_username: connection = user["connection"] msg = msg.encode(FORMAT) msg_lenth = str(len(msg)).encode(FORMAT) header = msg_lenth + b' ' * (HEADER - len(msg_lenth)) connection.send(header) connection.send(msg) break # we can add if check for if there is no user named like client want def delete_connection(username): for (idx, user) in enumerate(users): if user['username'] == username: del users[idx] def handle_client(conn, addr): print("[New Connection] {addr} connected...") connected = True while connected: max_lenth = conn.recv(HEADER).decode(FORMAT) if max_lenth: max_lenth = int(max_lenth) msg = conn.recv(max_lenth).decode(FORMAT) write_active_connections() messages = msg.split(SEP) username = messages[0] print(f"{users}") if USERNAME_MESSAGE in msg: # first connect write to users list data = { "username": username, "connection": conn } users.append(data) continue if DISCONNECT_MESSAGE in msg: # connection closed delete from user list print("[Disconnect] Disconnecting from server") delete_connection(username) connected = False if len(messages) > 2: # one to one messagging print(f"[{addr}] message will send -> {msg}") to_username = messages[1] send_message(to_username, msg) conn.close() write_active_connections() def write_active_connections(): print(f"Active Connection Count is {threading.activeCount()}") def start(): server.listen() print(f"[Listening] Server is Listening now on {IP}") while True: conn, addr = server.accept() thread = threading.Thread(target=handle_client, args=(conn, addr)) thread.start() write_active_connections() if __name__ == "__main__": print("[Starting] Socket Server is starting... Stand By") start()
Server kodunu incelediğimizde server’a connect olunduğunda username ve connection bilgisi içeren bir dict objesini users listesine atıyoruz bu sayede server’a connect olan herkes birbirine mesaj atabilecek.
Kodumuzda özel bir ayırma işlemini gerçekleştireceğimiz random sayılabilecek karakterler var client tarafı bilgileri gönderirken bu SEP(bölmek için gereken karakterler) ile ayırıyor.
gonderen{SEP}gonderdigi_kisi{SEP}mesaj
Gelen bilgileri ve tasarımlara göre bilgiyi parçalayıp kimden geldiğini kime gideceğini ve mesajı ayırabiliyoruz. Bu tasarımları kendinize göre yapabilirsiniz.
send_message fonksiyonunda users listesi içerisinden gönderilecek user’in socket bağlantısı bulunuyor ve mesaj ilk geldiği gibi user’a gönderiliyor.
# client.py import socket from threading import Thread HEADER = 64 PORT = 9999 FORMAT = 'utf-8' DISCONNECT_MESSAGE = "DISCONNECT_SERVER_CODE" USERNAME_MESSAGE = "EXAMPLE_APP_USERNAME_FIELD" SEP = "///**//" IP = socket.gethostbyname(socket.gethostname()) ADDR = (IP, PORT) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(ADDR) def send(msg): message = msg.encode(FORMAT) msg_lenth = str(len(message)).encode(FORMAT) header = msg_lenth + b' ' * (HEADER - len(msg_lenth)) client.send(header) client.send(message) def main(username, to_username): # send(f"{username}{SEP}{DISCONNECT_MESSAGE}") while True: message = input("") if message == "disconnect": send(f"{username}{SEP}{DISCONNECT_MESSAGE}") send(f"{username}{SEP}{to_username}{SEP}{message}") def listen(username): # should check for main thread if it closed we need to terminate listen thread. connected = True while connected: max_lenth = client.recv(HEADER).decode(FORMAT) if max_lenth: max_lenth = int(max_lenth) msg = client.recv(max_lenth).decode(FORMAT) messages = msg.split(SEP) if len(messages) > 2: if messages[1] == username: print(f"\n {messages[0]} --> {messages[2]} \n") else: print("security issue check server.py ") if __name__ == "__main__": username = input("Please write your user name: ") if username: send(f"{username}{SEP}{USERNAME_MESSAGE}") to_username = input(f"who you will send message: ") print("you can write your message and press enter") main_thread = Thread(target=main, args=(username, to_username,)) listen_thread = Thread(target=listen, args=(username,)) main_thread.daemon = True main_thread.start() listen_thread.start()
Client kodunu incelediğimizde 2 thread ile çalışmakta olduğunu görüyoruz. İlk thread Kullanıcıdan aldığı inputlari server’a gönderirken 2. thread serverdan gelen mesajları dinliyor. Threadler başlamadan önce kullanıcıya username ve kime mesaj atmak istediği soruluyor. Basit bir örnek olduğunu için bunları kendimiz ayarlıyoruz uygulama yapmak isteseydik online kullanıcıların listelenmesi gerekirdi.
Client tarafında da ayni SEP karakterlerini kullanarak gelen mesajı ayırıyoruz ve kimden geldiğini ve mesaj bilgisini konsola yazdırıyoruz.
Mesaj gönderirken ise tekrar kullanıcı, gönderdiği kişi ve mesaj SEP ile birleştirilerek gönderiliyor.
Server ve client kodlarını çalıştırdığımızda 2 kullanıcının birbiri ile haberleşebildiğini göreceğiz.
Kaynak Kod
Kod Örneği GitHub FurkanOzkaya
Bir sonraki blog yazımda bu kodu AsyncIO ile yazmaya çalışacağız.
hocam allah razı olsun çok makbule geçti. Ben bu kodları kendime adapte etmeye çalışayım.
Rica Ederim 🙂 Herhangi bir sorunuz olursa ulaşmaktan çekinmeyin. Kolay Gelsin.