فصل اكبر من ان يغطيه كتاب مثل هذا و لمقدمة عن ال sockets راجع مقالة ويكيبديا
http://en.wikipedia.org/wiki/Internet_socketسرفر بسيط
#simpleserver.py
import socket
class EchoServer(object):
def __init__(self, host='', port=51002):
self._host, self._port=host, port
self._endpoint=(host, port) #host, addr
self.sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
def start(self):
self.sock.bind(self._endpoint)
self.sock.listen(1)
print "Server running on: ", self._port
self.handle_request()
def handle_request(self):
while True: #Waits for a client.
clientsock, addr=self.sock.accept()
#clientfile=clientsock.makefile('rw', 0) #Create a file-like object.
print "Connection from: ", addr
clientsock.sendall(str(addr)+" you are connected to server...")
while True: #communication loop
#clientfile.write(str(addr)+" you are connected to server.\n")
msg=clientsock.recv(8092)
if msg:
print ">> ", msg
clientsock.sendall(msg)
#msg=clientfile.readline().strip() #clean it up.
#print "Recieved: ", msg
#clientfile.write("Got: "+msg+"\n")
#Cleaning UP
#clientfile.close()
clientsock.close()
if __name__=="__main__":
try:
es=EchoServer()
es.start()
except KeyboardInterrupt:
exit()
فى هذا الكود انشأنا صف جديد بإسم EchoServer
class EchoServer(object):
def __init__(self, host='', port=51002):
self._host, self._port=host, port
self._endpoint=(host, port) #host, addr
self.sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
1- قمنا بتحديد ال endpoint (الهوست والبورت)
2- انشأنا TCP Socket
3- قمنا بتفعيل استخدام SO_REUSEADDR لإلغاء شغل البورت عنده ايقاف السرفر
(بتفيد عند الحاجة لعمل ريستارت)
قمنا بتعريف طريقة start لبدأ السرفر
def start(self):
self.sock.bind(self._endpoint)
self.sock.listen(1)
print "Server running on: ", self._port
self.handle_request()
1- عمل bind (ربط بال endpoint اللتى تم تحديدها)
2- تجهيز وتشغيل ال tcp listener بإستخدام الطريقة listen
3- نستدعى الطريقة handle_request اللتى سيتم فيها التعامل مع العميل
مكونة من حلقتين الأولى للتعامل مع العملاء المنتظرين والثانية لمعالجة عميل ما
def handle_request(self):
while True: #Waits for a client.
clientsock, addr=self.sock.accept()
#clientfile=clientsock.makefile('rw', 0) #Create a file-like object.
print "Connection from: ", addr
clientsock.sendall(str(addr)+" you are connected to server...")
while True: #communication loop
msg=clientsock.recv(8092)
if msg:
print ">> ", msg
clientsock.sendall(msg)
#Cleaning UP
clientsock.close()
الطريقة accept تقبل اتصالا وتعيد لنا كائن socket وعنوان
تستطيع استخدام الطريقة makefile لإنشاء file-like object للتعامل مع ال socket
الطريقة sendall لإرسال رسالة
الطريقة recv للحصول على الرسالة القادمة (ويتم تحديد حجمها عن طريق معامل ال bufsize)
كلاينت بسيط
#simpleclient.py
import socket
class SimpleClient(object):
def __init__(self, endpoint=('127.0.0.1', 51002)):
self._endpoint=endpoint
self.sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(self._endpoint)
def start(self):
while True:
data=self.sock.recv(8096)
if not data:
break
print data
msg=raw_input("> ")
if not msg:
break
self.sock.send(msg)
self.sock.close()
if __name__=="__main__":
try:
sc=SimpleClient()
sc.start()
except KeyboardInterrupt:
exit()
هنا انشأنا صف جديد SimpleClient
class SimpleClient(object):
def __init__(self, endpoint=('127.0.0.1', 51002)):
self._endpoint=endpoint
self.sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(self._endpoint)
نقوم بعمل اتصال مع السرفر (تحديد ال endpoint) بإستخدام الطريقة connect
الطريقة start تقوم بعمل حلقة الإتصال مع السرفر
def start(self):
while True:
data=self.sock.recv(8096)
if not data:
break
print data
msg=raw_input("> ")
if not msg:
break
self.sock.send(msg)
self.sock.close()
الخ الخ
SocketServer
هى موديل فيها تجميع للصفات الشائعة فمثلا لاحاجة لكتابة الأكواد السابقة لمجرد انشاء TCPServer او UDPServerوهكذا ولكن الأساس ثابت وهناك بعض المتغيرات اللتى يمكن اعادة تعريفها (التعامل مع العميل على سبيل المثال)
سرفر
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from SocketServer import TCPServer, StreamRequestHandler
class MyStreamRequestHandler(StreamRequestHandler):
def handle(self):
print "Got connection from: ", self.client_address
self.wfile.write(str(self.client_address)+" you are connected to server.")
#Communication loop...
while True:
msg=self.request.recv(1024)
if not msg:
break
print ">> ", msg
#Send it back...
self.request.send(msg)
print "Done handling..."
def go(endpoint=('', 52002)):
addr=endpoint
tcpServer=TCPServer(addr, MyStreamRequestHandler)
tcpServer.allow_reuse_address=1
print "Server started..."
tcpServer.serve_forever() #inf. loop
if __name__=="__main__":
try:
go()
except KeyboardInterrupt:
exit()
هنا نشتق صف من ال StreamRequestHandler ويتم اعادة تعريف الطريقة handle اللتى يتم تنفيذها عند اتصال اى عميل
ولإنشاء السرفر نقوم بإستدعاء الصف TCPServer ونمرر له العنوان وايضا الصف الخاص بالتعامل مع طلب العميل MyStreamRequestHandler
ونتيح ايضا امكانية استخدام العنوان بإستخدام allow_reuse_address
ويبدأ السرفر فى العمل بإستخدام الطريقة serve_forever
كلاينت
import socket
class SimpleClient(object):
def __init__(self, endpoint=('127.0.0.1', 52002)):
self._endpoint=endpoint
self.sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(self._endpoint)
def start(self):
#self.sock.sendall("Hey you!!")
while True:
data=self.sock.recv(1024)
if not data:
break
print data
msg=raw_input("> ")
if not msg:
break
self.sock.sendall(msg)
#self.sock.close()
if __name__=="__main__":
try:
sc=SimpleClient()
sc.start()
except KeyboardInterrupt:
exit()
MixIns
تستطيع بكل سهولة ان تجعل سرفرك يعالج اكثر من عميل سواء بإستخدام ال Threading او ال Forking
وذلك بإشتقاقك لل ThreadingMixIn او ال ForkingMixIn الموجودة فى SocketServer module
class MyServer(ThreadingMixIn,TCPServer):
pass #Done!
او هكذا
class MyServer(ForkingMixIn, TCPServer):
pass #Done!
الفرق ان ال Forking يتم معالجة كل عميل فى بروسيس جديدة بينما ال Threading يتم معالجتها داخل نفس العملية ولكن بخيط جديد (تعدد مهام مثل محرر النصوص اللذى تكتب فيه ويقوم بالترقيم وتصحيح الأخطاء الإملائية والعديد من هذه العمليات فى ان واحد)
تطبيق دردشة
السرفر
#!bin/python
import socket
import threading
class ChatServer(object):
'''Indexer...'''
def __init__(self, port):
self.port=port
addr=('', self.port)
self._bufsize=2048
self.listener=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #Quick restarts.
self.listener.bind(addr)
self.alSocks=[]
#self.tListening=threading.Thread(target=self.listeningHandler, args=[])
#self.tListening.start()
self.listeningHandler() #Start listening...
def listeningHandler(self):
self.listener.listen(5)
print "Server started.."
while True:
clientSocket, clientAddr=self.listener.accept()
#Handle the client in a new thread...
self.tHandleClient=threading.Thread(target=self.clientHandler, args=[clientSocket])
self.tHandleClient.start()
def clientHandler(self, clientSocket):
self.alSocks += [clientSocket]
print "connection from: ", clientSocket.getpeername()
self._bufsize=2048
try:
while True:
data=clientSocket.recv(self._bufsize)
if not data:
break
#handle sending all recieved in another thread..
#serverToAll=threading.Thread(target=self.serverToAll, args=[clientSocket, data])
#serverToAll.start()
self.serverToAll(clientSocket, data)
except Exception:
#don't act
print clientSocket.getpeername(), " closed..."
finally:
self.alSocks.remove(clientSocket)
clientSocket.close()
def serverToAll(self, currentClient, data):
try:
for sock in self.alSocks:
if not sock == currentClient:
sock.send(data)
else:
pass
except Exception, e:
print e
if __name__=="__main__":
chatServer=ChatServer(8030)
ملحوظة لأنشاء خيط جديد فى برنامجك قم بإستخدام الصف threading.Thread لأنشاء كائن وقم بتحديد ال target وهى الميثود اللتى سيتم تنفيذها بصورة خارجية فى ذلك الثريد و args هى عبارة عن list تحوى المعاملات (args) الخاصة بتلك الميثود
الكلاينت
#!bin/python
import socket
import threading
class Peer(object):
def __init__(self, serverAddr=('localhost', 8030), alias="anonymouse"):
self.serverAddr=serverAddr
self.tcpClient=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.alias=alias
self._bufsize=2048
self.tcpClient.connect(self.serverAddr)
print "\nConnected to server.."
#self.tClientToServer=threading.Thread(target=self.clientToServerHandler, args=[])
#self.tClientToServer.start()
self.clientToServerHandler()
def clientToServerHandler(self):
print "Start Chattin' \n"
while True:
data=raw_input()
msg=alias+": "+data
if not data:
break
serverToClient=threading.Thread(target=self.serverToClientHandler, args=[])
serverToClient.start()
#self.serverToClientHandler()
self.tcpClient.send(msg)
def serverToClientHandler(self):
while True:
data=self.tcpClient.recv(self._bufsize)
if not data:
break
print data
if __name__=="__main__":
alias=raw_input("Alias: ")
peer=Peer(alias=alias)