Aegidius
 Plüss   Aplulogo
 
in examples
Use the asterisk (*) character for wildcard searches
Searches on more than one word will be treated "as a phrase"
 
serverstatus
 www.aplu.ch
 www.java-online.eu
     Print Text 
© 2018, V10.0
 
  Jython
 
 

 

Computer communication is a fascinating and motivating subject in every informatics course because it is more and more relevant in everyday life due to the importance of the internet and the widespread if interacting systems at home. The most important communication protocol for computer systems is clearly TCP/IP. Programming TCP/IP is also called socket programming.

Socket programming is based on the client/server model, where a server system is started first and is waiting (looking for) a client system that requests a connection. After the connection link is established we say that a socket is opened and stream typed data are exchanged. To break the communication link, we say that the socket is closed.

Socket programming requires knowledge of threads and streams and a good understanding of synchronizing interacting systems. With TCP/IP there is a additional problem that systems on the internet cannot use plain sockets because of firewall protection. To solve this problem data transmission has to be "tunneled" using the HTTP protocol. To hide this additional complexity the TcpJLib package has been developed (see here for more information)

TcpJLib can be used with Java as well as with Jython with no change. Thus porting programs from Java to Jython and vice versa is rather straightforward. If you are looking for more examples and ideas than presented on this page, consult the TcpJLib tutorial and example pages. The documentation may help too.

To use TcpJLib with the TigerJython IDE, download the distribution from here and copy TcpJLib.jar in the Lib subdirectory of the IDEs <home> directory. Append the library file to the sys path with

import sys
sys.path.append("./Lib/TcpJLib.jar")

The first examples uses two TcpNodes that exchange string data. The GUI is rudimentary but it shows the basics of a chat system where two (or more) people exchange text messages.

# TcpChat.py

import sys
sys.path.append("./Lib/TcpJLib.jar")

from ch.aplu.tcp import TcpNode, TcpNodeState

def messageReceived(sender, text):
   print("Received message from " + sender + ": " + text)

def statusReceived(text):
   print("Received status: " + text)

nickname = inputString("What's your name?")    
node = TcpNode(messageReceived = messageReceived, 
               statusReceived = statusReceived)

print "Connecting as " + nickname
node.connect("$123$", nickname)

msg = ""
while msg != None:
  msg = inputString("I am " + nickname, False)
  if msg != None and msg != "":
     node.sendMessage(msg) 

node.disconnect()
print "Disconnected" 
Select TcpChat.py (Ctrl+C to copy, Ctrl+V to paste)

Discussion: TcpJLib makes use of the event model extensively. The TcpNode constructor takes named parameters to register two callback functions messageReceived() and statusReceived() that are invoked when incoming data or incoming status messages are detected. TcpNodes are interlinked through a relay server and identified by a string-typed sessionID. In multi-user game applications the sessionID becomes the game room name.

Every TcpNode has its own nickname as defined in the connect() method. (If two nicknames are the same, they are made distinguishable by appending a nickname count.). When connect() returns the connection may net yet been established. To get informed you must use the connection notifications from the statusReceived() or the nodeStateChanged() callback.

To play around with the program start it (on the same or different PCs) in several TigerJython IDEs.

The next example is used in many introductory computer communication tutorials. It shows a working system that implements the client/server model. A server waits for incoming connections from a client system. Once the client is connected it handles incoming data in some specific way, in our example it just sends the data back, so we call it an echo server.

# EchoServer.py
# Sends back the received line to EchoClient 
# Simulates a delay due to long lasting program code

import sys
sys.path.append("./Lib/TcpJLib.jar")

from ch.aplu.tcp import TcpNode, TcpNodeState, TcpTools
from javax.swing import JOptionPane

def nodeStateChanged(state):
   if state == TcpNodeState.DISCONNECTED:
      print "State changed: DISCONNECTED"
   if state == TcpNodeState.CONNECTING:
       print "State changed: CONNECTING"
   if state == TcpNodeState.CONNECTED:  
      print "State changed: CONNECTED"
      print "Listening..."
  
def messageReceived(sender, message):
   print "Message received: " + message
   print "Working now..."
   # Simulate some work
   # Set this delay time to 5000 for a 'lazy' server and
   # the corresponding delay time in EchoClient to 1000 for a 'fast' client
   TcpTools.delay(1000)
   print "Work done. Reply now..."
   tcpNode.sendMessage(message)   # Echo message
   print "Listening..."
 
def statusReceived(status):
   print "Got status: " + status
 
sessionID = "echo&1&&asdf87u823";
nickname = "echoserver";
tcpNode = TcpNode(nodeStateChanged = nodeStateChanged, 
  messageReceived = messageReceived, statusReceived = statusReceived)
print "Trying to connect to " + tcpNode.getRelay() + \
  " using port " + str(tcpNode.getPort()) + "..."
tcpNode.connect(sessionID, nickname, 2, 2, 2)

JOptionPane.showMessageDialog(None, "Echo server running. Shutdown?")
tcpNode.disconnect()
print "Disconnected"
Select EchoServer.py (Ctrl+C to copy, Ctrl+V to paste)

Discussion: We also register the nodeStateChanged() callback that reports details about the connection. In the messageReceived() callback we add some delay that simulates a heavily loaded server. You may change the delay time to change the simulated load. We use a modal JOptionPane dialog that can be closed to terminate the program.

For the sake of demonstration the echo client sends increasing numbers to the server and checks for the response. It uses a ModelessOptionPane from the ch.aplu.util package to display incoming data. By registering actionPerformed() and notifyExit() the program terminates when the OK button or the title bar close button is hit. Any other GUI component could do the same.

# EchoClient.py
# Sends message to echo server EchoServer and waits for reply

import sys
sys.path.append("./Lib/TcpJLib.jar")

from ch.aplu.tcp import TcpNode, TcpNodeState, TcpTools
from ch.aplu.util import ModelessOptionPane

def nodeStateChanged(state):
   if state == TcpNodeState.DISCONNECTED:
      print "State changed: DISCONNECTED"
   if state == TcpNodeState.CONNECTING:
       print "State changed: CONNECTING"
   if state == TcpNodeState.CONNECTED:  
      print "State changed: CONNECTED"
  
def messageReceived(sender, message):
   global replied
   print "Message received: " + message
   replied = True

def statusReceived(status):
   print "Got status: " + status

def actionPerformed(evt):
   notifyExit()
 
def notifyExit():
   global isRunning
   tcpNode.disconnect()
   mop.dispose()
   isRunning = False

sessionID = "echo&1&&asdf87u823";
nickname = "echoclient";
tcpNode = TcpNode(nodeStateChanged = nodeStateChanged, 
  messageReceived = messageReceived, statusReceived = statusReceived)
print "Trying to connect to " + tcpNode.getRelay() + \
  " using port " + str(tcpNode.getPort())
tcpNode.connect(sessionID, nickname, 2, 2, 2)
while tcpNode.getNodeState() != TcpNodeState.CONNECTED:
   TcpTools.delay(1)
print "Echo client ready"
mop = ModelessOptionPane("Client running. Shutdown?", None, "Ok", 
  actionPerformed = actionPerformed, notifyExit = notifyExit)

n = 1
isRunning = True
while isRunning:
   replied = False
   msg = "#" + str(n)
   n += 1
   tcpNode.sendMessage(msg)
   print "Message sent: " + msg
   print "Waiting for reply..."
   while not replied and isRunning:
      TcpTools.delay(1)
print "All done. Disconnecting..."
tcpNode.disconnect()  
Select EchoClient.py (Ctrl+C to copy, Ctrl+V to paste)

Discussion: After invoking connect() we wait in a while loop until the client is connected. This is dangerous because the program hangs if the connection fails. To solve the problem, a timeout could be implemented by using a loop counter.

The data receiving loop is also subject to block if there is no reply. Here we use the boolean flag isRunning to break out of the loop by clicking the OK or close button.

 

Internet communication is widely used for for multi-user games. The following demonstration combines the power of the JGameGrid and the TcpJLib libraries. It shows all the important features of a two-player TCP game (game board setup and mouse handling, connection/disconnection, game over).

# TcpBattleShip.py

import sys
sys.path.append("./Lib/TcpJLib.jar")

from ch.aplu.jgamegrid import GameGrid, X11Color, Actor, GGMouse, 
   GGMouseListener, Location
from ch.aplu.tcp import TcpNode, TcpNodeState, TcpTools

# ---------- class Ship --------------------
class Ship(Actor):
   def __init__(self):
      Actor.__init__(self, "sprites/boat.gif")

# ---------- class MyMouseListener --------
class MyMouseListener(GGMouseListener):
   def mouseEvent(self, mouse):
      global isMyMove, loc
      if not isMyMove:
         return True
      loc = gg.toLocationInGrid(mouse.getX(), mouse.getY())
      tcpNode.sendMessage(str(loc.x) + str(loc.y))  # send string
      gg.setTitle("Wait enemy bomb!")
      isMyMove = False
      return True
 
# ---------- connect() --------------------
def connect():
   id = requestEntry("Enter unique server ID (more than 3 characters)");
   if id == None:
      gg.dispose()
      return
   sessionID = sessionPrefix + id
   gg.setTitle("Trying to connect to host")
   tcpNode.connect(sessionID, nickname)
 
# ---------- messageReceived() ------------
def messageReceived(sender, message):
   print "Got message: " + message
   global isMyMove, enemyScore, myScore, loc
   
   x = ord(message[0]) - 48 # We get ASCII code of number
   y = ord(message[1]) - 48
    
   if x == 9:  # Got command
      if y == GAME_START:
          isMyMove = False
          gg.setTitle("Wait! Enemy shoots first.")
      if y == SHIP_HIT:
          gg.addActor(Actor("sprites/checkgreen.gif"), loc)
          myScore += 1
          if myScore == nbShips:
            gameOver(True)
      if y == SHIP_MISSED:
          gg.addActor(Actor("sprites/checkred.gif"), loc)
   else: # Got coordinates
      loc = Location(x, y)
      actor = gg.getOneActorAt(loc, Ship)
      if actor != None:
         actor.removeSelf()
         gg.addActor(Actor("sprites/explosion.gif"), loc)
         tcpNode.sendMessage("9" + str(SHIP_HIT))
         enemyScore += 1
         if enemyScore == nbShips:
            gameOver(False)
            return
      else:
         tcpNode.sendMessage("9" + str(SHIP_MISSED))

      gg.setTitle("Shoot now! Score: " + str(myScore) + " (" + str(enemyScore) + ")");
      isMyMove = True
 
# ---------- statusReceived() -------------
def statusReceived(status):
   print "Got status: " + status
   global isMyMove, myScore, enemyScore
   if "In session:--- (0)" in status:  # we are first player
      gg.setTitle("Connected. Wait enemy..")
   if "In session:--- (1)" in status:  # we are second player
      tcpNode.sendMessage("9" + str(GAME_START))
      gg.setTitle("It is you to play")
      isMyMove = True  # Second player starts
   if "Disconnected:--- " in status:  # the partner is disconnected
      gg.setTitle("Enemy disconnected. Wait next..")
      gg.removeAllActors()
      isMyMove = False
      myScore = 0
      enemyScore = 0
      deployShips()
  
# ---------- requestEntry() ---------------
def requestEntry(prompt):
   entry = ""
   while len(entry) < 3:
      entry = inputString(prompt, False)
      if entry == None:
         return None
   return entry.strip()
    
# ---------- gameOver() -------------------
def gameOver(isWinner):
   global isMyMove
   isMyMove = False
   gg.removeAllActors()
   gg.addActor(Actor("sprites/gameover.gif"), Location(3, 3))
   if isWinner:
      gg.setTitle("You win!")
   else:
      gg.setTitle("You lost!")

# ---------- notifyExit() ----------------
def notifyExit():
   tcpNode.disconnect()
   gg.dispose()

def deployShips():
   for i in range(nbShips):
      gg.addActor(Ship(), gg.getRandomEmptyLocation())
   
# ---------- main() ----------------------
sessionPrefix = "ama&19td"
nickname = "captain"
tcpNode = TcpNode(messageReceived = messageReceived, statusReceived = statusReceived)
isMyMove = False
nbShips = 7
loc = None
myScore = 0
enemyScore = 0

GAME_START = 0
SHIP_MISSED = 1
SHIP_HIT = 2

gg = GameGrid(6, 6, 50, X11Color("red"), False, notifyExit = notifyExit)
gg.setTitle("TcpBattleShip")
deployShips()
gg.addMouseListener(MyMouseListener(), GGMouse.lClick);
gg.show()
connect()
Select TcpBattleShip.py (Ctrl+C to copy, Ctrl+V to paste)
Download sprites

Discussion: Some knowledge of the JGameGrid library is required to understand the code. Please refer either to the JGameGrid site or the Python game development examples. The sprite image files must reside in the sprites subdirectory of TigerJython's home directory. Use two instances of the TigerJython IDE on the same or different computers.

  Jytut33 Jytut34