Artık büyük boyutlu dosyalar da gönderilebilir. Kodlar güncellenmiştir.
Merhabalar,
Öncelikle, son derece yakışıklı duruyor yazdığınız örnek fakat bir server’in yazdığınız gibi çalışmaması gerektiğini düşünüyorum.
Server sadece clientler arasındaki iletişimi düzenliyor olmalı. Aşağıda yazdığım basit server örneğini incelerseniz, ne demek istediğimi daha iyi anlarsınız. (Bir client yazmaya gerek duymadım, mantık anlaşılıyor diye düşünüyorum.)
Link: http://dpaste.com/2ZJ2FMY.txt
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
#
#
__author__ = '@r0ark'
import os, sys, struct
import socket, threading, base64
def log(msg):
sys.stdout.write('[%s] - %s\n' % (time.ctime(), msg))
sys.stdout.flush()
def send(conn, data):
if type(data) != bytes:
data = bytes(data, 'utf-8')
data = struct.pack('>I', len(data)) + data
conn.sendall(data)
def _recv(conn, buff):
data = b''
while len(data) < buff:
packet = conn.recv(buff - len(data))
if not packet:
break
data += packet
return data
def recv(conn):
data_len = _recv(conn, 4)
if not data_len:
return None
data_len = struct.unpack('>I', data_len)[0]
return _recv(conn, data_len)
class Connection:
def __init__(self, conn, addr):
self.conn
self.addr
class Server:
clients = []
def __init__(self, host='0.0.0.0', port=8000):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((host, port))
self.socket.listen(10)
def send_all(self, message):
for client in self.clients:
try:
send(client.conn, message)
except Exception as err:
log('Error: ' + err.message)
def handle_client(self, connection):
while True:
message = recv(connection.conn)
if message:
message = ': '.join(connection.addr[0], message)
self.send_all(message)
def serve(self):
while True:
conn, addr = self.socket.accept()
log('New connection -> ' + addr[0])
connection = Connection(conn, addr)
self.clients.append(connection)
threading.Thread(self.handle_client, args=(connection, )).start()
if __name__ = '__main__':
server = Server()
server.serve()
Şu an group chat gibi bir şey ama tabii ki odalar, private kısımlar vs bir çok özellik eklenebilir. Bu arada, bağlanan client için Connection adında bir sınıf tanımlamak işi daha da kolaylaştırabilir. Özellikle açık anahtarlı şifreleme kullanacak olursak.
Merhaba, katılımınız için teşekkür ederim. Dediğiniz gibi bir sunucunun işlevi istemciler arasındaki iletişimi düzenliyor olmalı. Yalnız şu ana kadar sunucuyu bahsettiğiniz gibi ayarlamak için fırsat olmadı. Açıkçası şu an nasıl yapacağımı da bilmiyorum. Biraz araştırmam lazım.
Edit: Az önce bahsettiğiniz konuyla alakalı bir örnek yapıyordum. Sunucu herhangi bir istemciden gelen mesajları, gelir gelmez diğer istemcilere gönderdi. Neyse uygulamayı biraz geliştirmeye çalışayım. Kodlar aşağıdadır:
server.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import threading
DATA = ""
class Server(socket.socket):
clients = []
def __init__(self):
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
self.conn = None
def __connect__(self):
self.bind(("", 12346))
self.listen(10)
def __accept__(self):
try:
conn, addr = self.accept()
self.conn = conn
self.clients.append(conn)
print('{} connected.'.format(addr))
except OSError:
pass
def __receive__(self):
global DATA
if self.conn is not None:
data = str(self.conn.recv(1024))[1:]
DATA = data
if len(data) == 2:
pass
else:
print("\n{}".format(data[1:-1]), flush=True)
def __send__(self):
global DATA
for i in self.clients:
try:
i.send(bytes(DATA.encode("utf-8")))
except BrokenPipeError:
print("\nClient has been disconnected\n")
self.clients.remove(i)
DATA = ""
server = Server()
thread_connect = threading.Thread(target=server.__connect__)
thread_connect.start()
while True:
thread_accept = threading.Thread(target=server.__accept__)
thread_accept.daemon = True
thread_accept.start()
thread_accept.join(1)
thread_receive = threading.Thread(target=server.__receive__)
thread_receive.daemon = True
thread_receive.start()
thread_receive.join(1)
thread_send = threading.Thread(target=server.__send__)
thread_send.daemon = True
thread_send.start()
thread_send.join(1)
client.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import threading
nickname = input("Nickname: ")
class Client(socket.socket):
def __init__(self):
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
def __connect__(self):
self.connect(("", 12346))
def __receive__(self):
try:
data = str(self.recv(1024))[1:]
if len(data) == 2:
pass
else:
print(data[2:-2])
except OSError:
pass
def __send__(self):
respond = input()
self.sendall(bytes("<{}>: {}".format(nickname, respond).encode("utf-8")))
client = Client()
thread_connect = threading.Thread(target=client.__connect__)
thread_connect.start()
while True:
thread_receive = threading.Thread(target=client.__receive__)
thread_receive.start()
thread_send = threading.Thread(target=client.__send__)
thread_send.daemon = True
thread_send.start()
thread_send.join(1)
Vaktim olmadığı için bir client yazamadım ama bahsettiğim olay, şu projede uygulanmış.
Bu çalışmaya da bakacağım. Teşekkür ederim.
Uygulamayı indirip çalıştırdım, mantığını anlamak için programı biraz kurcalıyordum da:
Mesela:
<join> oda_ismi
yazarak bir odaya girdim.
Daha sonra başka bir istemciyi sunucuya bağladım.
<list>
yazdığımda açık olan odalar ekrana yazdırıldı…
Ama önce başka bir oda daha oluşturayım dedim.
<join> yeni_oda
yazdım. Ve bu odadayken de
<join> oda_ismi
yazarak odadan odaya geçiş yaptım.
Kullanıcıların mesajlaşabilmesi için bir odaya girmesi gerekiyor. Bazı komutlar yardımıyla odalar oluşturmayı veya odalardan çıkmayı ve ancak bu sayede istemciler arasında iletişim kurulmasını sağlayan bir program oluşturmayı deneyeceğim. Sizinle yukarıda paylaştığım son program ise, herhangi bir odaya girmeden, istemcilerin birbirleriyle mesajlaşmasını sağlıyor.
Tekrar teşekkür ederim.
Hocam İyi Güzel Yapmışsınız Hepinizin Ellerine Sağlıkta Bu Örnekler Beni Aşar Anlaması 10 Yılımı Alır Zaten
Her gün bir kaç saatinizi ayırabilirseniz, daha kısa sürelerde beni aşar dediğiniz örnekleri rahat anlayabilirsiniz. Sadece biraz ilgi istiyor.
Hocam İyi Güzelde Thread Ve Socket Falan Hakkında Hiç Bilgim Yok Kaynak Bulamadım Okuyamadım O Yüzden Aşar Fakat Kaynak Bulabilirsem Tabiiki İncelerim.
Kaynak mı bulamadınız? Nasıl bulamadınız anlamıyorum. Google’a “threading python türkçe” yazdığınızda karşınıza inceleyebileceğiniz en az 5, 6 sayfa çıkar zaten. Aynı şekilde socket’i de aratsanız yine kaynak bulursunuz. Üstelik bu forumda hatta bu başlıkta en basitinden socket örnekleri mevcut daha da basiti yoktur bence. Yani yapmanız gereken okuduğunuz kodları incelemek, “o ne işe yarıyormuş acaba” diye merak edip sağını solunu değiştirmek ve sonuçları gözlemlemek.
@r0ark
Dediğiniz gibi sunucuyu sadece istemciler arasındaki mesajlaşmayı ayarlayacak şekilde bir program yazdım. İstemciler mesajlaşabilmek için chat odalarına girmek zorundalar, yoksa mesajlaşamıyorlar. Paylaştığınız örnek programda kullanıcı komutları girmek için <komut>
yazıyordu, bu çalışmada /komut
şeklinde değiştirildi. Ama işlev olarak diğer programa benziyor diyebilirim. Program henüz yeni bitti sayılır bu yüzden hatalar olabilir, tespit ettiğiniz hataları paylaşırsanız sevinirim.
Kodlar:
server.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
import socket
import threading
DATA = ""
NICKNAME = ""
ROOMS = []
NICKNAMES = []
ROOMS_NICKNAMES = []
class Server(socket.socket):
clients = []
def __init__(self):
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
def __connect__(self):
self.bind(("", 12346))
self.listen(10)
print("Waiting for clients...")
def __accept__(self):
try:
conn, addr = self.accept()
self.clients.append(conn)
print('{} connected.'.format(addr))
except OSError:
pass
def __receive__(self):
for i in self.clients:
def receive():
global DATA, NICKNAME
data = str(i.recv(1024))[1:]
DATA = data
if len(data) == 2:
pass
else:
if ":nickname:" in DATA:
nickname = DATA[len(" :nickname: "):-1]
if nickname not in NICKNAMES:
NICKNAMES.append(nickname)
else:
NICKNAMES.append(nickname)
message = "This nickname is used."
self.clients[NICKNAMES.index(nickname, 1)].send(bytes(message.encode("utf-8")))
NICKNAMES.remove(NICKNAMES[NICKNAMES.index(nickname, 1)])
DATA = ""
elif ":room:" in DATA:
room, NICKNAME = DATA[8:-1].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: "):-1])].send(
bytes("Room: {}".format(", ".join(ROOMS)).encode("utf-8")))
DATA = ""
receive_thread = threading.Thread(target=receive)
receive_thread.start()
def __send__(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)
for i in chat_groups:
if NICKNAME in i:
for j in i:
self.clients[NICKNAMES.index(j)].send(bytes(DATA.encode("utf-8")))
except ValueError:
pass
DATA = ""
server = Server()
thread_connect = threading.Thread(target=server.__connect__)
thread_connect.start()
while True:
thread_accept = threading.Thread(target=server.__accept__)
thread_accept.daemon = True
thread_accept.start()
thread_accept.join(1)
thread_receive = threading.Thread(target=server.__receive__)
thread_receive.start()
thread_send = threading.Thread(target=server.__send__)
thread_send.daemon = True
thread_send.start()
thread_send.join(1)
client.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
from datetime import datetime
import socket
import threading
DATA = ""
NICKNAME = ""
ROOM = ""
class Client(socket.socket):
def __init__(self):
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
def __connect__(self):
self.connect(("", 12346))
print("| {} | Welcome to the chat program.\n".format(str(datetime.now())[:-7]))
print("Commands:\n\t/nick <new_nickname>\n\t/join <room_name>\n\t/list\n\t/quit\n".expandtabs(4))
def __receive__(self):
global DATA
try:
data = str(self.recv(1024))[1:]
if len(data) == 2:
pass
else:
if "Room:" in data:
for i in data[6:-1].split(","):
print("\t{}".expandtabs(4).format(i[1:]))
elif "This nickname is used." in data:
print("| {} | {}".format(str(datetime.now())[:-7], data[1:-1]))
DATA = data[1:-1]
else:
print(data[2:-2])
except OSError:
pass
def __send__(self):
global NICKNAME, ROOM, DATA
respond = input()
if "/nick" in respond:
if respond == "/nick":
print("| {} | You must create a nickname.".format(str(datetime.now())[:-7]))
else:
NICKNAME = respond[6:]
if " " in NICKNAME or NICKNAME == "":
print("| {} | Do not use space in your nickname.".format(str(datetime.now())[:-7]))
else:
self.sendall(bytes(":nickname: {}".format(NICKNAME).encode("utf-8")))
time.sleep(0.1)
if "This nickname is used." not in DATA:
print("| {} | Your nickname has changed to {}.".format(str(datetime.now())[:-7], NICKNAME))
else:
NICKNAME = ""
DATA = ""
elif "/join" in respond:
if respond == "/join":
print("| {} | You must write a room.".format(str(datetime.now())[:-7]))
else:
ROOM = respond[6:]
if " " in ROOM or ROOM == "":
print("| {} | Do not use space in room name.".format(str(datetime.now())[:-7]))
else:
if NICKNAME != "":
self.sendall(bytes(":room: {} {}".format(ROOM, NICKNAME).encode("utf-8")))
print("| {} | Joined {}.".format(str(datetime.now())[:-7], ROOM))
else:
print("| {} | You must create a nickname.".format(str(datetime.now())[:-7]))
elif "/list" in respond:
if NICKNAME != "":
self.sendall(bytes(":list: {}".format(NICKNAME).encode("utf-8")))
print("| {} | Available Rooms:".format(str(datetime.now())[:-7]))
else:
print("| {} | You must create a nickname.".format(str(datetime.now())[:-7]))
elif "/quit" in respond:
import os
import signal
os.kill(os.getpid(), signal.SIGKILL)
else:
if NICKNAME != "":
if ROOM != "":
self.sendall(bytes(".:!:.{}.:!:.{}: {}".format(NICKNAME, NICKNAME, respond).encode("utf-8")))
else:
print("| {} | Join a room.".format(str(datetime.now())[:-7]))
else:
print("| {} | Create a nickname.".format(str(datetime.now())[:-7]))
client = Client()
thread_connect = threading.Thread(target=client.__connect__)
thread_connect.start()
while True:
thread_receive = threading.Thread(target=client.__receive__)
thread_receive.start()
thread_send = threading.Thread(target=client.__send__)
thread_send.daemon = True
thread_send.start()
thread_send.join(1)
bir şey soracaktım bu ne işe yarıyor
ve client ile serveri nasıl bağlayabilirim ikisinide ayrı ayrı açtıktan sonra nasıl birbirine bağlayacam?
Bu, istemcilerin birbirleriyle mesajlaşmasına yarıyor.
Client ve serveri şöyle bağlayabilirsiniz:
- Önce server.py’yi çalıştırırsınız.
- Sonra başka bir oturumda client.py’yi çalıştırırsınız.
- client.py’yi çalıştırdıktan sonra ekranda gözüken yönergelere bakarsınız. Orada komutlar “Commands:” adı altında listelenmiştir. Bu listelenmiş komutlardan olan /nick komutuyla bir nick belirtirsiniz:
/nick tekbir
- sonra ya bir odaya girersiniz.
/join herhangi_bir_oda
- ya da mevcut bir oda var mı ona bakarsınız:
/list
- Sonra başka bir oturumda bir tane daha client.py açıp aynı sunucuya bağlanırsınız.
- Bu istemciyle daha önce bağlanan istemcinin mesajlaşmasını istiyorsanız, daha önce bağlanan istemcinin girdiği odaya girersiniz.
# tabi önce nick seçmeniz gerekir.
/nick yeni_nick
# sonra odayı yazarsınız
/join herhangi_bir_oda
- Ve artık bu iki istemci birbirleriyle yazışabilirler.
Bu programdaki sunucunun görevi istemciler arasındaki iletişimi düzenlemektir.
Anlattıklarım sizin kendi bilgisayarınızda yapabileceğiniz şeyler. Bu tarz programlarla bir çok kişiyle chat yapılabilir veya karşı tarafa dosyalar gönderebilirsiniz.
Hataları tespit için unittest
modülünü kullanmaya ne dersiniz?
Olur, neden olmasın. Teşekkür ederim.
Bir hatayla karşılaşmadım.
Yalnız bu programın biraz geliştirilmesi gerekiyor. Bir istemci sunucuya bağlandıktan sonra, sunucuda tanımlanmış olan listelere istemcinin girmiş olduğu kullanıcı adı ve oda ismi kaydedilir. İstemci sunucudan çıktığı zaman istemcinin açtığı odanın ismi ve seçmiş olduğu kullanıcı adının bu listelerden silinmesi gerekiyor.
İstemcilerin daha önceden oluşturmuş oldukları kullanıcı adı ve oda isimleri, istemciler sunucuyu terk eder etmez artık siliniyor.
Kodlar:
https://github.com/dildeolupbiten/A-console-chat-program-with-chat-rooms/blob/master/server.py
https://github.com/dildeolupbiten/A-console-chat-program-with-chat-rooms/blob/master/client.py
Edit: Sanırım bir hata var. Bu sefer de mesaj gönderme işlemi engelleniyor.
Edit: Mesaj gönderme işleminin engellenmesi sorunu çözüldü.
Her programda bir bug vardır. Siz işinizi riske atmayın. Testlerinizi otomatikleştirin.
Mesela programda şöyle bir bug tespit ettim. Diyelim istemci birden fazla odaya girdi, o zaman yazdığı mesaj girdiği oda sayısı kadar kendisine gönderiliyor. Şimdi düşünüyorum da yazdığım kodlara göre beklenmesi gereken bir sonuç bu. O zaman sonucun böyle olacağını düşünememiştim. Denemelerimi de hep tek bir oda üzerinden yaptım. Aynı odaya girmiş istemciler mesajlaşabiliyorlarsa sorun yoktur diye düşünüyordum. Ama farklı ihtimallerin de gerçekleşip gerçekleşmeyeceğini görmek için denemeler yapınca, başka bir sorun daha açığa çıktı. Neyse bir ara bu sorunla ilgileneyim.
Edit: Bu sorun da çözülmüştür. Artık istemci istediği kadar odaya girsin, gönderdiği mesajlar sadece bir kere kendi ekranına yazdırılır. Yukarıdaki kodlar yenilenmiştir.