Plib Networking

silentpolygon

28-12-2007 08:50:27

Has anyone ever done networkprogramming with plib?
Just want to get one foot into it.

jaaku1111

28-01-2008 01:13:40

I wrote this lib (mostly cobbled it together from posts and a bit of duct-tape) works pretty good for simple stuff:

import socket
import threading
import select
import sys
import struct

class pyNetServer:
"Simple-to-use Network Server"
def __init__(self):
self.HOST = socket.gethostname()
#self.HOST = 'localhost'
self.BUFSIZ = 1024
self.BACKLOG = 2
self.ssocket = None
self.threads = []
self.listener = None
self.lock = threading.Lock()
self.Running = 1

def Start(self,port):
try:
print 'Starting Server At Host: ' + str(self.HOST) + ' on Port: ' + str(port)
self.ADDR = (self.HOST,port)
self.ssock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.ssock.bind(self.ADDR)
self.ssock.listen(self.BACKLOG)
except socket.error, (value, message):
if self.ssocket:
self.ssocket.close()
print "Couldn't open socket: " + message
sys.exit(1)

#now kick off the listener thread (run)

self.listener = pyNetServerListener(self)
self.listener.start()

def Close(self):
self.Running = 0
#self.lock.acquire()
for t in self.threads:
t.Close()
#self.lock.release()

self.listener.running = 0
self.ssock.close()


def SendAll(self, msg):
self.lock.acquire()
for t in self.threads:
t.Send(msg)
self.lock.release()

def GetMessages(self):
msgs = []
clients_to_remove = []
try:
#self.lock.acquire()
for c in self.threads:
o = c.GetMessage()

while o != None:
client_closed = False
if isinstance(o,str):
if o == 'client closed!@#$%':
#client closed, so kill their thread.
#print 'client closed!'
client_closed = True
clients_to_remove.append(c)
msgs.append( (c,'-disconnected-') )


if not client_closed:
msgs.append( (c,o) )
o = c.GetMessage()

finally:
pass#self.lock.release()

while len(clients_to_remove) > 0:
c = clients_to_remove.pop(0)
c.Close()


return msgs






class pyNetServerListener(threading.Thread):
def __init__(self,parent):
threading.Thread.__init__(self)
self.Parent = parent
self.running = 1

def run(self):
while self.running:
inputready,outputready,exceptready = select.select([self.Parent.ssock],[],[],2)
for s in inputready:
if s == self.Parent.ssock:
try:
client, address = self.Parent.ssock.accept()

sc = pyNetServerClient((client,address),self.Parent)
sc.start()

self.Parent.lock.acquire()
self.Parent.threads.append(sc)
self.Parent.lock.release()

except socket.error, (value,message):
pass#print 'got a socket error in the server listener ' + message



else:
print 'NON Parent.ssock event!!!'




class pyNetServerClient(threading.Thread):
def __init__(self,(csocket,address),parent):
threading.Thread.__init__(self)
self.Parent = parent
self.csocket = csocket
self.address = address
self.BUFSIZ = 1024
self.running = True
self.msgqueue = []
self.lock = threading.Lock()

def run(self):
while self.running:
try:

inputready,outputready,exceptready = select.select([self.csocket],[],[],2)

for s in inputready:
if s == self.csocket:
finished = 0
remaining = ""
while not finished:
finished = 1 # preset to finished, is reset if theres remaining data


total_len=0;total_data=[];size=sys.maxint
size_data=sock_data='';recv_size=8192
while total_len<size:
if len(remaining) != 0:
sock_data = remaining
else:
sock_data=self.csocket.recv(recv_size)

if not total_data:
if len(sock_data)>4:
size_data+=sock_data
size=struct.unpack('>i',size_data[:4])[0]
recv_size=size
if recv_size>524288:recv_size=524288
a = size_data
b = size_data[4:]
#print ' has: ' + a + 'appending: ' + b
total_data.append(b)
else:
size_data+=sock_data
else:
total_data.append(sock_data)
total_len=sum([len(i) for i in total_data ])

data = ''.join(total_data)

remaining = ""
if len(data) != size:
remaining = data[size:]
#print 'GOT EXTRA DATA!: ' + str(remaining)
data = data[:size]
finished = 0

#print 'packet actual size: ' + str(len(data))



if data: # add the data to the queue
#print 'got data: ' + data
try:
self.lock.acquire()
self.msgqueue.append(data)
finally:
self.lock.release()

#pass
else:
self.Close()
else:
print 'non socket message!!!'




except socket.error, (value, message):
#print "error: " + message
self.running = 0

#THIS IS WHAT IS TRIGGERED IF THE CLIENT DISCNNECTS
self.msgqueue.append( 'client closed!@#$%' )

def Send(self,msg):
if self.running:
try:
self.csocket.sendall(struct.pack('>i',len(msg)) + msg)
#print 'sending a packet of length: ' + str(len(msg))
except socket.error, (value,message):
print "send error: " + message
self.Close()
else:
print 'attempted to send from a closed client thread.'

def Close(self):

#self.Parent.lock.acquire()
try:
self.Parent.threads.remove(self)
except:
print 'networklib: unable to remove self from network thread list.'
#self.Parent.lock.release()

self.csocket.close()
self.running = False

def GetMessage(self):
o = None
try:
self.Parent.lock.acquire()
if len(self.msgqueue) > 0:
o = self.msgqueue.pop(0)
finally:
self.Parent.lock.release()
return o















class pyNetClient:
"Simple-To-Use Network Client"
def __init__(self):
self.HOST = 'localhost'
self.PORT = 21000
self.BUFSIZ = 1024
self.ADDR = (self.HOST,self.PORT)
self.csock = None
self.thread = None
self.lock = threading.Lock()
self.msgqueue = []
self.Running = 1

def Connect(self,adr):
self.ADDR = adr
self.csock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.csock.connect(self.ADDR)
#csock.send('yo')
#csock.close()

self.thread = pyNetClientThread(self)
self.thread.start()


def Send(self, msg):
try:
self.csock.sendall(struct.pack('>i',len(msg))+msg)
except socket.error, (value, message):
pass

def Close(self):
self.Running = 0
self.thread.running = 0
self.thread = None
print 'networklib: Closing Client Network Socket.'

def GetMessage(self):
o = None
try:
self.lock.acquire()
if len(self.msgqueue) > 0:
o = self.msgqueue.pop(0)
finally:
self.lock.release()
return o

def Connected(self):
return self.thread.running

class pyNetClientThread(threading.Thread):
"thread listener for client"
def __init__(self,parent):
threading.Thread.__init__(self)
self.Parent = parent
self.running = 1

def run(self):
while self.running:
try:
inputready,outputready,exceptready = select.select([self.Parent.csock],[],[],2)

for s in inputready:
if s == self.Parent.csock:
finished = 0
remaining = ""
while not finished:
finished = 1 # preset to finished, is reset if theres remaining data
total_len=0;total_data=[];size=sys.maxint
size_data=sock_data='';recv_size=8192

while total_len<size:
if len(remaining) != 0:
sock_data = remaining
else:
sock_data=self.Parent.csock.recv(recv_size)

if not total_data:
if len(sock_data)>4:
size_data+=sock_data
size=struct.unpack('>i',size_data[:4])[0]
#print 'packet of size: ' + str(size)
recv_size=size
if recv_size>524288:recv_size=524288
a = size_data
b = size_data[4:]
#print ' has: ' + a + 'appending: ' + b
total_data.append(b)
else:
size_data+=sock_data
else:
#print 'else appending: ' + sock_data
total_data.append(sock_data)

total_len=sum([len(i) for i in total_data ])

data = ''.join(total_data)
remaining = ""
if len(data) != size:
remaining = data[size:]
#print 'GOT EXTRA DATA!: ' + str(remaining)
data = data[:size]
finished = 0

#print 'packet actual size: ' + str(len(data))


if data:
#print 'got ' + data
try:
self.Parent.lock.acquire()
#print 'appending: ' + str(data)
self.Parent.msgqueue.append(data)
finally:
self.Parent.lock.release()


else:
#print 'get error'
self.running = 0
else:
print 'client got NON self.Parent.csock event!!!'
except socket.error, (value, message):
print 'got exception ' + message
self.running = 0





Very easy to use, server side just do something like this:

nserver = networklib.pyNetServer()
nserver.Start(6000)

msgs = nserver.GetMessages()
for clnt, msg in msgs:
if msg == "-disconnected-":
print 'client disconnected'

msgo = pickle.loads(msg) #convert the message into an instance of an object
if msgo[0] == "join": pass


so basically, the first 2 lines create the server and get the thread accepting connections. when someone connects and then sends a message the GetMessages() will pick them up (probably want to put this whole thing in your game loop) and the if statements let you do actions based on what the msg is (in my case, I pickle the message, and then this unpickles it and looks at the first element of the list (I use the first element as a packet_id) )

heres the client side:

client = networklib.pyNetClient()
client.Connect(( socket.gethostname() , 6000)) #connects localhost
client.Send( pickle.dumps( ("join", '' ) ) ) # this is how to send a packet

#heres the loop that peels off messages and allows you to do actions on them (messages from the server)
o = self.client.GetMessage()
if o != None:
#print 'new packet!: ' + str(o)
nobj = pickle.loads(o)

if nobj[0] == "pname":
print 'my name is!: ' + str(nobj[1])





Anyway's, I hope this helps, if you have any troubles just leme know... I put the lib part in its own file called networklib.py and then import it.

another thing to note, this doesn't do any sort of login-auth type stuff, so you can implement that on top if you like. this just gives you a nice little 'string based' chat client/server, although its TCP based, its still probably faster than what most people would ever need.