Öncelikle herkese iyi forumlar.
Socket kütüphanesi ile server-client arası iletişim kurabiliyor ve mesaj yollayabiliyorum ama
client-client arası mesaj yollayabilirmiyim ?
Şimdiden teşekkürler.
Zaten sunucunun asıl görevi istemciler arasındaki mesaj trafiğini düzenlemektir. Sunucu, istemcilerden gelen mesajları alır ve onları gönderilmesi istenen istemcilere gönderir.
Örnek olarak web sitesi veya kod paylaşırmısınız ?
tam olarak neyin web sitesi ?
Socket server-client iletişimi örnekleri bulunan bir web sitesi.
bu kanaldaki dersler arasında mesajlaşma ve oyun programlanmış eğitimler var. socket client ile alakalı. Göz atabilirsiniz. Oynatma listelerinde mevcut
video değil kod metinleri okumak istiyorum diyorsanız. Eğitimlerini yazılı olarak paylaştığı web sitesi de mevcut;
Sunucu soketlerin (yani belli bir adrese bind edilmiş soketlerin) tek görevi gelen bağlantıları kabul etmek (socket.accept
) ve bu bağlantıyı temsil eden bir soket döndürmek. Daha sonra bu soket üzerinden bağlantı kurulan istemci ile haberleşebilirsiniz.
Sizin buradaki server ve client’ten kastınız soketlerin kullanım şeklinden çok cihazlar ile alakalı sanırım. Sunucu cihaz kendisine bağlanan cihazların arasındaki veri trafiğini yönetebilir, mesela A istemcisinden gelen bir veriyi B istemcisine gönderebilir. Bu bir yöntem.
Diğer bir yöntem de sunucu cihazı diğer istemcileri bulmak için kullanmak, mesela sunucu kendisine bağlanan iki istemcinin IP adreslerini karşılıklı olarak onlara geri yollarsa bu iki istemci kendi aralarında bağlantı kurabilir.
Eskiden yazdığım, iyileştirilmesi gereken basit bir uygulama var, onu paylaşabilirim.
server.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
import time
import socket
import threading
DATA = ""
NICKNAME = ""
ROOMS = []
NICKNAMES = []
ROOMS_NICKNAMES = []
while True:
try:
PORT = int(input("PORT: "))
break
except ValueError:
print("Port value is invalid.")
def exit_program():
import os
if os.name == "posix":
import signal
os.kill(os.getpid(), signal.SIGKILL)
elif os.name == "nt":
os.system("TASKKILL /F /PID {}".format(os.getpid()))
class Server(socket.socket):
clients = []
def __init__(self):
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
def connect_server(self):
try:
self.bind(("0.0.0.0", PORT))
self.listen(10)
print("Waiting for clients...")
except PermissionError:
print("Permission denied.")
exit_program()
except OverflowError:
print("Port must be 0-65535.")
exit_program()
def accept_connections(self):
try:
conn, addr = self.accept()
self.clients.append(conn)
print('{} connected.'.format(addr))
except OSError:
pass
def receive_data(self):
for i in self.clients:
def receive():
global DATA, NICKNAME
try:
data = i.recv(1024).decode("utf-8")
DATA = data
if len(data) == 0:
pass
else:
if ":nickname:" in DATA:
nick = DATA[len(":nickname: "):]
if nick not in NICKNAMES:
NICKNAMES.append(nick)
else:
NICKNAMES.append(nick)
message = "This nickname is used."
self.clients[
NICKNAMES.index(nick, 1)
].send(bytes(message.encode("utf-8")))
NICKNAMES.remove(
NICKNAMES[NICKNAMES.index(nick, 1)]
)
DATA = ""
elif ":room:" in DATA:
room, NICKNAME = DATA[7:].split(" ")
ROOMS_NICKNAMES.append(room)
ROOMS_NICKNAMES.append(NICKNAME)
if room not in ROOMS:
ROOMS.append(room)
DATA = ""
NICKNAME = ""
elif ":list:" in DATA:
self.clients[
NICKNAMES.index(DATA[len(":list: "):])
].send(
f"Room: {', '.join(ROOMS)}"
.encode("utf-8")
)
DATA = ""
except ConnectionResetError:
pass
receive_thread = threading.Thread(target=receive)
receive_thread.start()
def send_data(self):
global DATA, NICKNAME
if ".:!:." in DATA:
regex = "".join(re.findall("\.:!:\..+\.:!:\.", DATA))
NICKNAME = regex.replace(".:!:.", "")
DATA = DATA.replace(regex, "")
try:
chat_groups = []
for x, y in enumerate(ROOMS):
group = []
for i, j in enumerate(ROOMS_NICKNAMES):
if j == ROOMS[x]:
group.append(ROOMS_NICKNAMES[i + 1])
if group not in chat_groups:
chat_groups.append(group)
count = 0
for i in chat_groups:
if NICKNAME in i:
count += 1
if count > 1:
for j in i:
if j != NICKNAME:
try:
self.clients[
NICKNAMES.index(j)
].send(DATA.encode("utf-8"))
except BrokenPipeError:
pass
else:
for j in i:
try:
self.clients[
NICKNAMES.index(j)
].send(DATA.encode("utf-8"))
except BrokenPipeError:
pass
except ValueError:
pass
DATA = ""
def manage_variables(self):
for i in self.clients:
try:
i.send(bytes(".:!:.test.:!:.".encode("utf-8")))
except (BrokenPipeError, ConnectionResetError):
try:
nick = NICKNAMES[self.clients.index(i)]
try:
room = ROOMS_NICKNAMES[
ROOMS_NICKNAMES.index(nick) - 1
]
print("{} is disconnected.".format(nick))
if ROOMS_NICKNAMES.count(room) == 1:
ROOMS.remove(room)
while ROOMS_NICKNAMES.count(nick) > 0:
ROOMS_NICKNAMES.pop(
ROOMS_NICKNAMES.index(nick) - 1
)
ROOMS_NICKNAMES.remove(nick)
NICKNAMES.remove(nick)
self.clients.remove(i)
except ValueError:
print("{} is disconnected.".format(nick))
NICKNAMES.remove(nick)
except IndexError:
self.clients.remove(i)
time.sleep(10)
if __name__ == "__main__":
server = Server()
thread_connect = threading.Thread(target=server.connect_server)
thread_connect.start()
while True:
thread_accept = threading.Thread(target=server.accept_connections)
thread_accept.daemon = True
thread_accept.start()
thread_accept.join(1)
thread_receive = threading.Thread(target=server.receive_data)
thread_receive.start()
thread_send = threading.Thread(target=server.send_data)
thread_send.daemon = True
thread_send.start()
thread_send.join(1)
thread_manage = threading.Thread(target=server.manage_variables)
thread_manage.daemon = True
thread_manage.start()
thread_manage.join(1)
client.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import time
import socket
import threading
from datetime import datetime as dt
DATA = ""
NICKNAME = ""
NICKNAME_CONTROLLER = 0
ROOM = ""
HOST = input("HOST: ")
now = lambda: dt.now().strftime("%Y-%m-%d %H:%M:%S")
if HOST == "":
HOST = "127.0.0.1"
while True:
try:
PORT = int(input("PORT: "))
break
except ValueError:
print("Port value is invalid.")
def exit_program():
if os.name == "posix":
import signal
os.kill(os.getpid(), signal.SIGKILL)
elif os.name == "nt":
os.system("TASKKILL /F /PID {}".format(os.getpid()))
class Client(socket.socket):
def __init__(self):
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
def connect_server(self):
try:
self.connect((HOST, PORT))
print(f"| {now()} | Welcome to the chat program.\n")
print(
"Commands:\n\t"
"/nick <new_nickname>\n\t"
"/join <room_name>\n\t"
"/list\n\t"
"/clear\n\t"
"/quit\n".expandtabs(4)
)
except socket.gaierror:
print("Name or service not known.")
exit_program()
except ConnectionRefusedError:
print("Connection refused.")
exit_program()
except OverflowError:
print("Port must be 0-65535.")
exit_program()
except ConnectionAbortedError:
print("Software caused connection abort.")
exit_program()
def receive_data(self):
global DATA
try:
data = self.recv(1024).decode("utf-8")
if len(data) == 0:
pass
else:
if "Room:" in data:
for i in data[6:].split(","):
print("\t{}".expandtabs(4).format(i))
elif "This nickname is used." in data:
print(f"| {now()} | {data}")
DATA = data
elif ".:!:.test.:!:." in data:
pass
else:
print(data)
except OSError:
pass
def send_data(self):
global NICKNAME, ROOM, DATA, NICKNAME_CONTROLLER
response = input()
if "/nick" in response:
if NICKNAME_CONTROLLER < 1:
if response == "/nick":
print(f"| {now()} | You must create a nickname.")
else:
NICKNAME = response[6:]
if " " in NICKNAME or NICKNAME == "":
print(
f"| {now()} | "
"Do not use space in your nickname."
)
else:
self.sendall(
f":nickname: {NICKNAME}".encode("utf-8")
)
time.sleep(0.1)
if "This nickname is used." not in DATA:
print(
f"| {now()} | "
"Your nickname has changed to "
f"{NICKNAME}."
)
NICKNAME_CONTROLLER += 1
else:
NICKNAME = ""
else:
print(f"| {now()} | You can not change your nickname.")
DATA = ""
elif "/join" in response:
if response == "/join":
print(f"| {now()} | You must write a room.")
else:
ROOM = response[6:]
if " " in ROOM or ROOM == "":
print(f"| {now()} | Do not use space in room name.")
else:
if NICKNAME != "":
self.sendall(
f":room: {ROOM} {NICKNAME}".encode("utf-8")
)
print(f"| {now()} | Joined {ROOM}.")
else:
print(f"| {now()} | You must create a nickname.")
elif response == "/list":
if NICKNAME != "":
self.sendall(f":list: {NICKNAME}".encode("utf-8"))
print(f"| {now()} | Available Rooms:")
else:
print(f"| {now()} | You must create a nickname.")
elif response == "/quit":
exit_program()
elif response == "/clear":
os.system("clear" if os.name == "posix" else "cls")
else:
if NICKNAME != "":
if ROOM != "":
self.sendall(
f".:!:.{NICKNAME}.:!:.{NICKNAME}: {response}"
.encode("utf-8")
)
else:
print(f"| {now()} | Join a room.")
else:
print(f"| {now()} | Create a nickname.")
if __name__ == "__main__":
client = Client()
thread_connect = threading.Thread(target=client.connect_server)
thread_connect.start()
while True:
thread_receive = threading.Thread(target=client.receive_data)
thread_receive.start()
thread_send = threading.Thread(target=client.send_data)
thread_send.daemon = True
thread_send.start()
thread_send.join(1)
Şimdi ben bağlı olduğu bütün clientlere mesaj atabilen bir kod yazmaya çalışıyorum ama bir client’in attığı mesajı diğeri atmıyor neden olabilir ?
Server.py :
import socket
host = "localhost"
port = 8131
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
s.bind((host,port))
s.listen(5)
while True:
client,adress = s.accept()
print(f"client {adress} connecting")
msg = client.recv(1024)
client.send(msg)
print(msg.decode("utf-8"))
Client:
import socket
host = "localhost"
port = 8131
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
s.connect((host,port))
while True:
a = input("mesaj gönderilsinmi ?")
if a == "e" or a == "E":
s.sendall(bytes("hello world","utf-8"))
msg = s.recv(1024).decode("utf-8")
print(msg)
else:
pass
Socket uygulamalarında accept
fonksiyonu çağrıldığı zaman, sunucu istemcinin kendisine bağlanmasını beklemeye başlar. Bağlandıktan sonra bu fonksiyon işlemini tamamlar. Dolayısıyla sizin öyle bir kod yazmanız gerekiyor ki, sunucu kullanıcının kendisine bağlanmasını beklememeli ama aynı zamanda kullanıcının bağlanmasına olanak tanımalıdır. Bunun nasıl yaparsınız? Mesela threading
kullanabilirsiniz. Aşağıdaki kodlar, sizin kodlarınızın biraz değiştirilmiş hali. Bir bakın isterseniz.
server.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import socket
from threading import Thread
host = "localhost"
port = 8131
clients = []
def accept(s):
conn, addr = s.accept()
if (conn, addr) not in clients:
clients.append((conn, addr))
print(f"client {addr} connected.")
def send(c):
msg = c.recv(1024)
c.send(msg)
print(msg.decode("utf-8"))
def receive():
for c in clients:
t = Thread(target=lambda: send(c[0]), daemon=True)
t.start()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((host,port))
s.listen(5)
while True:
t1 = Thread(target=lambda: accept(s), daemon=True)
t1.start()
t1.join(1)
t2 = Thread(target=receive, daemon=True)
t2.start()
t2.join(1)
client.py’de herhangi bir değişiklik yapmadım.
Burdaki method ne işe yarıyor açıklayabilirmisiniz @dildeolupbiten.
join
fonksiyonunun timeout
isimli bir argümanı var. Bu, thread’in zaman aşımına uğrayacağı süreyi belirtir.
Örnek:
from threading import Thread
t = Thread(target=lambda: input(">>> "), daemon=True)
t.start()
t.join(3)
Normalde, input
fonksiyonu sizden bir şeyler yazıp enter
tuşuna basmanızı bekler. Ancak yukarıdaki kod çalıştırıldığında, 3 saniye içinde bir yazı yazmazsanız program sonlanır.
Burasını anlayamadım
Bir iş parçacığını yukarıdaki örnekte olduğu gibi belli bir süre sonra sonlandırmak için onun daemon
özelliğinin aktif edilmiş olması gerekir.
Ayrıca ana programınız bir main_thread
'tir (ana iş parçacığıdır). Ve eğer arka planda çalışan başka iş parçacıklarınız var ise ve bu iş parçacıkları daemonic
değilse, programınız sonlanmayacaktır ve bu iş parçacıklarının sonlanmasını bekleyecektir. Program ancak geriye sadece daemon
threadler kaldığında sonlanır.
Basit bir örnek:
from threading import Thread
def f():
while True:
continue
t1 = Thread(target=f, daemon=True)
t2 = Thread(target=lambda: print("hello"))
t1.start()
t2.start()
Bir önceki mesajımda şöyle bir şey demiştim.
Şimdi yukarıdaki örneğe göre while
döngüsünü içeren f
fonksiyonunu çağıran iş parçacığının sürekli çalışması gerekiyor gibi geliyor. Ancak onu daemonic
olarak ayarladığımız için, print
fonksiyonu sonlandığında geriye sadece daemonic
iş parçacığı kaldığı için while
döngüsünü çalıştıran iş parçacığı da sonlanır.
Hatta bu kodu şöyle değiştirin:
from threading import Thread
def f():
while True:
continue
t1 = Thread(target=f, daemon=True)
# t2 = Thread(target=lambda: print("hello"))
t1.start()
# t2.start()
Görecekseniz ki, program çalışır çalışmaz sonlanıyor, çünkü geriye sadece daemonic
iş parçacığı kalmış oluyor. daemon
özelliğini kaldırırsanız, while
döngüsünün bulunduğu fonksiyon da çalışmaya devam eder.
Bir hatırlatmada bulunmak istiyorum. Hem server
, hem de client
’in mesaj gönderme ve mesaj alma işlemlerinin güvenli bir şekilde yapılabilmesi için bazı düzenlemeler yapmanız gerekecektir. Örneğin, recv
fonksiyonuna yazılan 1024
veya 2048
gibi arabellek boyutları bir verinin alınabilecek maksimum boyutunu ifade eder. Verinin bu boyuttan daha düşük bir boyutta olan yığınını (chunk
) da alabilirsiniz.
@aib arkadaşımız bizi bu konuda daha önce uyarmıştı. Sabit bir arabellek boyutu kullanmak yerine gönderilecek verinin boyutuna göre bu arabellek boyutunun belirlenmesinin gerektiğini söylemişti. Veri aktarımının başarıyla sonuçlandığını anlamak için de while alinan < gonderilen
gibi bir koşul altında çalışan bir döngünün kullanılabileceğini belirtmişti.
Daha sonradan @EkremDincel arkadaşımız, bu konuyla alakalı bir modül tasarladı. Yani socket
modülünü kullanmak yerine bu modülü kullanabilir ve veri aktarımının daha güvenli olmasını sağlayabilirsiniz. Tabi modülü kullanırken, veri gönderme ve veri alma işlemlerinin birbirlerini bloklamayacak şekilde gerçekleşmesi için iş parçacıkları kullanmanız gerekecektir. @EkremDincel arkadaşımızın yaptığı, veri alış-verişini daha kontrollü bir şekilde yapmak için alternatif send
ve recv
fonksiyonlarını içeren bir yama oluşturmaktır. (Yama doğru bir kelime mi emin olamadım.)
Yapmanız gereken şey sadece A little TCP socket interface for Python. · GitHub adresine giderek, bu dosyayı indirmek ve @EkremDincel’in yaptığı gibi socket programını bu microstream
modülünü kullanarak yazmaktır.
İlgili mesajı incelemek için aşağıdaki bağlantıyı kullanabilirsiniz.
Veya isreadable
metodu kullanılabilir, soketin okunabilecek mesajı olup olmadığını döndürüyor.