Exercise 11 Code

exer11b/codec.py

"""
Codings for PDU.
"""
import logging
log = logging.getLogger(__name__)
import struct
from exer11b.interfaces import *
from util.common import *


class Join(Join_pdu):
  def __init__(self):
    super(Join,self).__init__()

  def encode(self):
    self.type = 1
    self.size = struct.calcsize("HH")
    self.namesize = len(self.name)
    self.size += struct.calcsize("H")
    self.size += self.namesize
    data = str()
    data += struct.pack("HH",self.type,self.size)
    data +=struct.pack("H",self.namesize)
    data += self.name
    log.debug("Join.encode: " + buf_debug(data))
    return data

  def decode(self,data):
    log.debug("Join.decode: " + buf_debug(data))
    hdrsize = struct.calcsize("HH")
    self.type, self.size = struct.unpack("HH",data[0:hdrsize])
    assert self.size == len(data)    
    offset = hdrsize 
    namesizesize = struct.calcsize("H")
    self.namesize, = struct.unpack("H",data[offset:offset + namesizesize])
    offset += namesizesize
    self.name = data[offset:offset+self.namesize]

class Leave(Leave_pdu):
  def __init__(self):
    super(Leave,self).__init__()

  def encode(self):
    self.type = 2
    self.size = struct.calcsize("HH")
    self.namesize = len(self.name)
    self.size += struct.calcsize("H")
    self.size += self.namesize
    data = str()
    data += struct.pack("HH",self.type,self.size)
    data += struct.pack("H",self.namesize)
    data += self.name
    log.debug("Leave.encode: " + buf_debug(data))    
    return data

  def decode(self,data):
    log.debug("Leave.decode: " + buf_debug(data))
    hdrsize = struct.calcsize("HH")
    self.type, self.size = struct.unpack("HH",data[0:hdrsize])
    assert self.size == len(data)
    offset = hdrsize 
    namesizesize = struct.calcsize("H")
    self.namesize, = struct.unpack("H",data[offset:offset + namesizesize])
    offset += namesizesize
    self.name = data[offset:offset+self.namesize]

class SendMsg(SendMsg_pdu):
  def __init__(self):
    super(SendMsg,self).__init__()    

  def encode(self):
    self.type = 3
    self.size = struct.calcsize("HH")
    self.size += struct.calcsize("H")
    self.namesize = len(self.name)
    self.size += self.namesize
    self.size += struct.calcsize("H")    
    self.msgsize = len(self.msg)
    self.size += self.msgsize     
    data = str()
    data += struct.pack("HH",self.type,self.size)
    data += struct.pack("H",self.namesize)
    data += self.name
    data +=struct.pack("H",self.msgsize)
    data += self.msg
    log.debug("SendMsg.encode: " + buf_debug(data))    
    return data
  
  def decode(self,data):
    log.debug("SendMsg.decode: " + buf_debug(data))
    hdrsize = struct.calcsize("HH")
    self.type, self.size = struct.unpack("HH",data[0:hdrsize])
    assert self.size == len(data),"%d vs %d" % (self.size,len(data))
    offset = hdrsize 
    namesizesize = struct.calcsize("H")
    self.namesize, = struct.unpack("H",data[offset:offset + namesizesize])
    offset += namesizesize
    self.name = data[offset:offset+self.namesize]
    offset += self.namesize      
    msgsizesize = struct.calcsize("H")
    self.msgsize, = struct.unpack("H",data[offset:offset + msgsizesize])
    offset += namesizesize      
    self.msg = data[offset:offset+self.msgsize]

class Codec(object):

  def decode_data(self,data):
    ## read common header
    hdrsize = struct.calcsize("HH")
    type, size = struct.unpack("HH",data[0:hdrsize])
    msg = None
    if type == 1:
      msg = Join()
      msg.decode(data)
    elif type == 2:
      msg = Leave()   
      msg.decode(data)         
    elif type == 3:
      msg = SendMsg() 
      msg.decode(data)           
    else:
      log.error("Unknown message %d" % type)
    return msg

exer11b/interfaces.py

'''
Created on Sep 7, 2010

@author: hevi
'''

class IUser_req(object):
  """
  Chat user interaction.
  """

  def join(self,addr,port,name):
    """
    Join user to chat.
    """
    raise NotImplementedError()
  
  def leave(self,name):
    """
    Leave user from chat.
    """
    raise NotImplementedError()
  
  def send_msg(self,name,msg):
    """
    Send message to chat.
    @param msg: text message. 
    """
    raise NotImplementedError()

class IUser_ind(object):

  def join_ind(self,name):
    """
    Join user to chat.
    """
    raise NotImplementedError()
  
  def leave_ind(self,name):
    """
    Leave user from chat.
    """
    raise NotImplementedError()
    
  def recv_msg(self,name,msg):
    """
    Receive message from char.
    @param msg: received text message. 
    """
    raise NotImplementedError()
    
class IService(object):
  """
  Chat service interaction.
  """

  def join(self,user):
    """
    Add user to chat service.
    @param user: user object. 
    """
    raise NotImplementedError()
    
  def leave(self,user):
    """
    Remove user from chat service.
    @param user: user object to remove 
    """
    raise NotImplementedError()    
    
  def send_msg(self,who,msg):
    """
    Broadcast message between users.
    @param who: Where the message originates from.
    @param msg: Chat message.  
    """
    raise NotImplementedError()    

class Message_pdu(object):
  def __init__(self):
    self.type = 0
    self.size = 0    

class Join_pdu(Message_pdu):
  def __init__(self):
    super(Join_pdu,self).__init__()
    self.namesize = 0
    self.name = None

class Leave_pdu(Message_pdu):
  def __init__(self):
    super(Leave_pdu,self).__init__()
    self.namesize = 0
    self.name = None

class SendMsg_pdu(Message_pdu):
  def __init__(self):
    super(SendMsg_pdu,self).__init__()    
    self.namesize = 0
    self.name = None
    self.msgsize = 0
    self.msg = None

exer11b/main_client.py

'''
Chat client.
'''

import logging
log = logging.getLogger(__name__)
from tcp.interfaces import *
from tcp.tcp_usage import *
import select
import sys
import os
from exer11b.interfaces import *
import struct

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from util.common import *
import exer11.codec as codec
import socket

#from PySide.QtCore import *
#from PySide.QtGui import *



class ChatUi(QWidget,IUser_ind):
  
  def __init__(self,app,client):
    super(ChatUi,self).__init__(None)
    self.name = "test." + str(os.getpid()) 
    self.app = app
    self.client = client
    self.client.set_ui(self)
    self.make_layout()
    
  def make_layout(self):
    self.resize(200,200)
    self.setWindowTitle("Chat Client")
    vbox = QVBoxLayout()
    ##
    hbox = QHBoxLayout()
    b = QPushButton("Join")
    b.connect(b,SIGNAL("clicked()"),self.on_join)
    hbox.addWidget(b)
    b = QPushButton("Leave")
    b.connect(b,SIGNAL("clicked()"),self.on_leave)
    hbox.addWidget(b)
    vbox.addLayout(hbox)
    ##
    self.text = QTextEdit()
    self.text.setEnabled(False)
    vbox.addWidget(self.text)
    ##
    self.input = QLineEdit()
    #self.input.setValidator(None)
    self.input.connect(self.input,SIGNAL("returnPressed()"),self.on_return)
    vbox.addWidget(self.input)
    ##
    self.setLayout(vbox)
    self.show()
    
  def run(self):
    log.debug("Running Qt thread ..")
    sys.exit(self.app.exec_())

  def closeEvent(self,event):
    log.debug("Window closing ..")
    self.on_quit()

  def on_quit(self):
    self.app.exit(0)
    self.client.close()

  def on_join(self):
    log.debug("Connect.join()")
    self.client.join("localhost",12345,self.name)
    self.text.append("Joining .. \n")

  def on_leave(self):
    log.debug("Disconnect.leave()")
    self.client.leave(self.name)

  def on_return(self):
    log.debug("Input.returnPressed(): " + self.input.text())
    self.client.send_msg(self.name,str(self.input.text()))
    self.input.clear()
    return None

  def join_ind(self,name):
    self.text.append("*** %s joined" % name)
  
  def leave_ind(self,name):
    self.text.append("*** %s leaved" % name)
    
  def recv_msg(self,name,msg):
    self.text.append("<%s> %s" % (name,msg))

  def error(self,text):
    self.text.append("ERROR %s" % text)

  def state_change(self,state_name):
    self.setWindowTitle("Chat Client %s (%s)" % (self.name,state_name))    

##############################################################################
## ChatClient

class State(object):

  def name(self): return "NOSTATE"

  def join(self,ctx,addr,port,name):
    ctx.ui.error("Wrong state")
  
  def join_ok(self,ctx):
    ctx.ui.error("Wrong state")  
  
  def join_error(self,ctx):
    ctx.ui.error("Wrong state")  
  
  def leave(self,ctx,name):
    ctx.ui.error("Wrong state")

  def leave_done(self,ctx,name):
    ctx.ui.error("Wrong state")
  
  def send_msg(self,ctx,name,chatmsg):
    ctx.ui.error("Wrong state")

class Idle(State):

  def name(self): return "IDLE"

  def join(self,ctx,addr,port,name):
    ctx.change_state(ctx.CONNECTING)
    ctx.tmp_name = name
    try:
      ctx.conn = ctx.tcp.connect(addr,port)  
      ctx.conn.set_ind(ctx)
      ctx.join_ok()
    except socket.error, e:
      log.error(str(e))
      ctx.join_error()

class Connecting(State):

  def name(self): return "CONNECTING"

  def join_ok(self,ctx):
    msg = codec.Join()
    msg.name = ctx.tmp_name
    ctx.conn.send(msg.encode())    
    ctx.change_state(ctx.CONNECTED)    
  
  def join_error(self,ctx):
    ctx.change_state(ctx.IDLE)

class Connected(State):

  def name(self): return "CONNECTED"

  def send_msg(self,ctx,name,chatmsg):
    ctx.change_state(ctx.CONNECTED)    
    msg = codec.SendMsg()
    msg.name = name
    msg.msg = chatmsg
    log.debug("ChatClient sending %s %s" % (name,chatmsg))
    ctx.conn.send(msg.encode())        

  def leave(self,ctx,name):
    msg = codec.Leave()
    msg.name = name
    ctx.conn.send(msg.encode())
    ctx.change_state(ctx.DISCONNECTING)
    
class Disconnecting(State):

  def name(self): return "DISCONNECTING"

  def leave_done(self,ctx):
    ctx.conn.close()  
    ctx.conn.set_ind(None)
    ctx.change_state(ctx.IDLE)

class ChatClient(TCP_conn_ind,IUser_req):
  
  def __init__(self,tcp):
    self.tcp = tcp
    self.ui = None
    self.conn = None
    self.codec = codec.Codec()
    tcp.set_ind(self)
    ## States
    self.IDLE = Idle()
    self.CONNECTING = Connecting()
    self.CONNECTED = Connected()
    self.DISCONNECTING = Disconnecting()
    self.state = None
    self.change_state(self.IDLE)

  def set_ui(self,ui):
    self.ui = ui
    self.ui.state_change(self.state.name())

  def change_state(self,new_state):
    if self.state is not None:
      log.debug("%s => %s",self.state.name(),new_state.name())
    self.state = new_state
    if self.ui is not None:
      self.ui.state_change(self.state.name())

  def close(self):
    log.debug("ChatClient.close")    
    self.conn.close()

  def join(self,addr,port,name): self.state.join(self,addr,port,name)  
  def leave(self,name): self.state.leave(self,name)  
  def send_msg(self,name,chatmsg): self.state.send_msg(self,name,chatmsg)
  def join_ok(self): self.state.join_ok(self)  
  def join_error(self): self.state.join_error(self)
  def leave_done(self,name): self.state.leave_done(self,name)

  def recv(self,data):
    msg = self.codec.decode_data(data)
    if msg.type == 1:
      self.ui.join_ind(msg.name)
    elif msg.type == 2:
      if msg.name == self.tmp_name:
        self.state.leave_done(self)
      self.ui.leave_ind(msg.name)
    elif msg.type == 3:
      self.ui.recv_msg(msg.name,msg.msg)
    else:
      log.error("Unknown message %d" % type)

##############################################################################
## Deploying and running

def run():
  tcp = TCP()
  client = ChatClient(tcp)
  app = QApplication(sys.argv) 
  ui = ChatUi(app,client)
  ## activate
  tcp.start()
  ui.run()

if __name__ == '__main__':
  logging.basicConfig(level=logging.DEBUG)
  run()

exer11b/main_server.py

'''
Chat server.
'''

import logging
log = logging.getLogger(__name__)
from tcp.interfaces import *
from tcp.tcp_usage import *
import exer11b.codec as codec

class User(TCP_conn_ind):

  def __init__(self,server,conn):
    self.server = server
    self.conn = conn
    self.conn.set_ind(self)
    self.codec = codec.Codec()
  
  def recv(self,data):
    msg = self.codec.decode_data(data)
    if msg.type == 1:
      self.join_recv(msg.name)
    elif msg.type == 2:
      self.leave_recv(msg.name)      
    elif msg.type == 3:
      self.sendmsg_recv(msg.name,msg.msg)    
    else:
      pass
     
  def join_recv(self,name):
    log.debug("Server recv join %s" % name)
    self.server.join(name)

  def leave_recv(self,name):
    log.debug("Server recv leave %s" % name)
    self.server.leave(name)

  def sendmsg_recv(self,name,msg):
    log.debug("Server recv sendmsg %s %s" % (name,msg))
    self.server.sendmsg(name,msg)

  def join_send(self,name):
    msg = codec.Join()
    msg.name = name
    self.conn.send(msg.encode())

  def leave_send(self,name):
    msg = codec.Leave()
    msg.name = name
    self.conn.send(msg.encode())    

  def sendmsg_send(self,name,chatmsg):
    msg = codec.SendMsg()
    msg.name = name
    msg.msg = chatmsg
    self.conn.send(msg.encode())    

  def close(self):
    log.debug("connection closed")
    self.conn.close()
    self.server.remove_user(self)

class Server(TCP_ind):
  
  def __init__(self,tcp):
    self.tcp = tcp
    tcp.set_ind(self)
    self.users = list()

  def join(self,name):
    for user in self.users:
      user.join_send(name)
  
  def leave(self,name):
    for user in self.users:
      user.leave_send(name)

  def sendmsg(self,name,msg):
    for user in self.users:
      user.sendmsg_send(name,msg)

  def new_connection(self,conn):
    log.debug("new connection")
    user = User(self,conn)
    self.users.append(user)
  
  def remove_user(self,user):
    self.users.remove(user)
  
def run():
  tcp = TCP()
  tcp.be_server(12345)
  server = Server(tcp)
  tcp.start()

if __name__ == '__main__':
  logging.basicConfig(level=logging.DEBUG)
  run()

exer11b/test_codec.py

'''
Test PDU Codec.
'''
import logging
log = logging.getLogger(__name__)
import unittest
import exer11b.codec as codec

class Test(unittest.TestCase):

  def test_join_01(self):
    emsg = codec.Join()
    emsg.name = "test"
    data = emsg.encode()
    dmsg = codec.Join()
    dmsg.decode(data)
    assert emsg.name == dmsg.name

  def test_leave_01(self):
    emsg = codec.Leave()
    emsg.name = "test"
    data = emsg.encode()
    dmsg = codec.Leave()
    dmsg.decode(data)
    assert emsg.name == dmsg.name

  def test_sendmsg_01(self):
    emsg = codec.SendMsg()
    emsg.name = "test"
    emsg.msg = "message"        
    data = emsg.encode()
    dmsg = codec.SendMsg()
    dmsg.decode(data)
    assert emsg.name == dmsg.name
    assert emsg.msg == dmsg.msg

  def test_join_02(self):
    emsg = codec.Join()
    emsg.name = "test"
    data = emsg.encode()
    c = codec.Codec()
    dmsg = c.decode_data(data)
    assert emsg.name == dmsg.name

  def test_leave_02(self):
    emsg = codec.Leave()
    emsg.name = "test"
    data = emsg.encode()
    c = codec.Codec()
    dmsg = c.decode_data(data)
    assert emsg.name == dmsg.name

  def test_sendmsg_02(self):
    emsg = codec.SendMsg()
    emsg.name = "test"
    emsg.msg = "message"        
    data = emsg.encode()
    c = codec.Codec()
    dmsg = c.decode_data(data)
    assert emsg.name == dmsg.name
    assert emsg.msg == dmsg.msg


if __name__ == "__main__":
  logging.basicConfig(level=logging.DEBUG)  
  unittest.main()
Last modified: 2013/07/01 14:42