وصول Python إلى BaseRequestHandler
-
21-09-2019 - |
سؤال
يحتاج الكود الخاص بي أساسًا إلى بدء تشغيل خادم دردشة بسيط مع عميل. حيث يمكن للخادم والعميل التحدث ذهابًا وإيابًا إلى بعضهما البعض. لقد حصلت على كل شيء ليتم تنفيذه بشكل صحيح ، لكن لا يمكنني معرفة كيفية إغلاق الخادم كلما انتهيت. (انا اعلم انه ss.shutdown()
).
أرغب في النهاية الآن بناءً على كلمة رئيسية مشتركة بين الاثنين (شيء مثل "bye"
) ، لكنني لا أعرف ما إذا كان بإمكاني إرسال رسالة إلى بلدي بطريقة ما SocketServer
من عند BaseRequestHandler
ل shutdown()
كلما استقبلت الرسالة.
في النهاية ، هدفي هو دمج Tkinter
لصنع واجهة المستخدم الرسومية ، لكنني أردت أن أعمل كل شيء آخر أولاً ، وهذه هي المرة الأولى التي أتعامل فيها مع مآخذ في بيثون.
from sys import argv, stderr
from threading import Thread
import socket
import SocketServer
import threading
import sys
class ThreadedRecv(Thread):
def __init__(self,socket):
Thread.__init__(self)
self.__socket = socket
self.__message = ''
self.__done = False
def recv(self):
while self.__message.strip() != "bye" and not self.getStatus():
self.__message = self.__socket.recv(4096)
print 'received',self.__message
self.setStatus(True)
def run(self):
self.recv()
def setStatus(self,status):
self.__done = status
def getStatus(self):
return self.__done
class ThreadedSend(Thread):
def __init__(self,socket):
Thread.__init__(self)
self.__socket = socket
self.__message = ''
self.__done = False
def send(self):
while self.__message != "bye" and not self.getStatus():
self.__message = raw_input()
self.__socket.send(self.__message)
self.setStatus(True)
def run(self):
self.send()
def setStatus(self,status):
self.__done = status
def getStatus(self):
return self.__done
class HostException(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class EchoServer(SocketServer.BaseRequestHandler):
def setup(self):
print self.client_address, 'is connected!'
self.request.send('Hello ' + str(self.client_address) + '\n')
self.__done = False
def handle(self):
sender = ThreadedSend(self.request)
recver = ThreadedRecv(self.request)
sender.start()
recver.start()
while 1:
if recver.getStatus():
sender.setStatus(True)
break
if sender.getStatus():
recver.setStatus(True)
break
def finish(self):
print self.client_address, 'disconnected'
self.request.send('bye client %s\n' % str(self.client_address))
self.setDone(True)
def setDone(self,done):
self.__done = done
def getDone(self):
return self.__done
def setup(arg1, arg2, arg3):
server = False
defaultPort,defaultHost = 2358,"localhost"
hosts = []
port = defaultPort
serverNames = ["TRUE","SERVER","S","YES"]
arg1 = arg1.upper()
arg2 = arg2.upper()
arg3 = arg3.upper()
if arg1 in serverNames or arg2 in serverNames or arg3 in serverNames:
server = True
try:
port = int(arg1)
if arg2 != '':
hosts.append(arg2)
except ValueError:
if arg1 != '':
hosts.append(arg1)
try:
port = int(arg2)
if arg3 != '':
hosts.append(arg3)
except ValueError:
if arg2 != '':
hosts.append(arg2)
try:
port = int(arg3)
except ValueError:
if arg3 != '':
hosts.append(arg3)
port = defaultPort
for sn in serverNames:
if sn in hosts:
hosts.remove(sn)
try:
if len(hosts) != 1:
raise HostException("Either more than one or no host "+ \
"declared. Setting host to localhost.")
except HostException as error:
print error.value, "Setting hosts to default"
return (server,defaultHost,port)
return (server,hosts[0].lower(),port)
def main():
bufsize = 4096
while len(argv[1:4]) < 3:
argv.append('')
settings = setup(*argv[1:4])
connections = (settings[1],settings[2])
print connections
if not settings[0]:
try:
mySocket = socket.socket(socket.AF_INET,\
socket.SOCK_STREAM)
except socket.error, msg:
stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(1)
try:
mySocket.connect(connections)
except socket.error, msg:
stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(2)
message = ""
print "Enter a message to send to the server. "+\
"Enter \"bye\" to quit."
sender = ThreadedSend(mySocket)
recver = ThreadedRecv(mySocket)
sender.start()
recver.start()
while 1:
if sender.getStatus():
recver.setStatus(True)
break
if recver.getStatus():
sender.setStatus(True)
break
else:
xserverhandler = EchoServer
serversocket = SocketServer.ThreadedTCPServer(\
connections,xserverhandler)
server_thread = Thread(target = serversocket.serve_forever)
server_thread.setDaemon(True)
server_thread.start()
# I would like to shut down this server whenever
# I get done talking to it.
"""while 1:
if xserverhandler.getDone():
print 'This is now true!'
serversocket.shutdown()
break"""
if __name__ == '__main__':
main()
نعم ، أعرف أن الإعداد () هو وظيفة فظيعة الآن مع المحاولة والمصيد ، لكنها تعمل في الوقت الحالي ، لذلك كنت سأصلحها لاحقًا.
سؤالي هو بشكل أساسي: كيف يمكنني الحصول على الخادم فعليًا بناءً على رسالة تتلقاها؟ إذا كان ذلك ممكنًا ، هل هناك طريقة للوصول إلى معالج الطلب بعد بدء تشغيله؟
المحلول
يرجى إصلاح الرمز الخاص بك بحيث يعمل ، وتضمين طريقة لاستخدامه. تحتاج إلى إضافة
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
نظرًا لأن SocketServer لا يتضمن بالفعل تلك الفئة (على الأقل ليس في نسختي من 2.6 ولا 2.7). بدلاً من ذلك ، إنه مثال من تعريف Socketserver.
يرجى تضمين مثال على كيفية بدء/استخدام الرمز. في هذه الحالة لبدء تشغيل الخادم ، عليك القيام به:
ss.py SERVER localhost 8001
والعميل باسم
ss.py localhost 8001
إذا قمت بذلك ، فلا يمكنك القيام بـ Server_thread.setDaemon (صحيح) لأنه لا توجد مؤشرات ترابط أخرى تعمل ، مما يعني أن الخادم سيخرج على الفور.
بمجرد الانتهاء من ذلك ، فإن الحل هو إضافة مكالمة (أو اثنين) إلى self.server.shutdown () insdie من طريقة echoServer.handle ، مثل:
while 1:
if recver.getStatus():
sender.setStatus(True)
self.server.shutdown()
break
ومع ذلك ، لا يمكنني الحصول على ذلك للعمل ، وأعتقد أن ذلك لأنني ورثت الأمور خاطئة ، أو خمنت خطأ في ما فعلته.
ما يجب عليك فعله هو البحث عن شخص آخر قام بخادم دردشة في بيثون. باستخدام Google وجدت http://www.slideshare.net/didip/socket-programming-in-python وهناك بالتأكيد آخرون.
أيضًا ، إذا كنت ستختلط في واجهة المستخدم الرسومية والبرمجة الخيوط ، فيجب عليك النظر في أمثلة بناءً على ذلك. هناك عدد من الزيارات عندما بحثت عن "دردشة Tkinter". أيضًا ، قد ترغب في النظر إلى Twisted ، والتي حلت الكثير من هذه المشكلات بالفعل.
ما هي المشاكل؟ حسنًا ، على سبيل المثال ، من المحتمل أن تريد خيار SO_REUSEADDR Socket.
نصائح أخرى
يتم إنشاء كائن معالج الطلب لكل طلب جديد. لذلك عليك تخزين العلم "المنجز" في الخادم ، وليس المعالج. شيء مثل ما يلي:
class EchoServer(SocketServer.BaseRequestHandler):
...
def setDone(self):
self.server.setDone() # or even better directly self.server.shutdown()