Network programming for PyS60 (III)

by Marcelo Barros

As I promised, today we will start with some programming, writing a simple “fortune client/server”. Instead Chinese fortune cookies, the fortune server will send to its clients a random statement from Zen of Python. I am assuming that you have a mobile and a PC connected to a WiFi router, as depicted below.

post_3

Client will run in your mobile and server at your PC. In fact, it could run in mobile as well, but I think it is interesting to introduce clients first and leave servers for further posts. Even with a simple client it is possible to clarify many network programming issues. Take a look in the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Fortune client - PyS60 network programming
# Marcelo Barros de Almeida
# marcelobarrosalmeida@gmail.com
 
# Supporting Python 1.4.x, 1.9.x
try:
    from btsocket import *
except:
    from socket import *
 
HOST = "10.0.0.100"
PORT = 54321
 
s = socket(AF_INET,SOCK_STREAM)
s.connect((HOST,PORT))
 
fortune = u""
while True:
    data = s.recv(1024)
    if not data:
        break
    fortune += data
 
s.close()
 
from appuifw import note
note(fortune,"info")

First action is to import the module socket. Just for keeping compatibility, first we try to import btsocket (Python 1.9.x). If it fails, the standard socket module is used (Python 1.4.x).

Next step is to define where we want to connect, I mean, where the service we want to use is running. When you create a network server, you must define exactly where people can find it. In TCP/IP, this is specified as a combination of IP (Internet Protocol) address and TCP/UDP port. While IP is used to locate a machine in the network, the port is used to locate a service inside this machine. You may run several services in the same machine, just using a different port for each one. The common analogy is to use a company phone number for representing the IP address and an extension number for representing the service, like a department or person.

In fact, TCP/IP uses a third parameter as well, known as the transport protocol. The transport protocol defines how the communication between the two end systems will be performed. TCP (Transmission Control Protocol) and UDP (User Datagram Protocol) are the most common transport methods used nowadays. TCP provides a reliable and ordered byte stream between two end systems (similar to make a telephone call to someone) and UDP uses a simpler model based on messages, not byte stream. UDP does not have any order or delivery guarantee (similar to send several letters to someone).

The function socket creates a network file descriptor, very similar to that one received when a file is opened, but only used for network communications. It is important to set which address family you are using and the transport protocol. Common address families are AF_INET (IPv4 addresses) and AF_INET6 (IPv6 addresses). TCP can be selected with SOCK_STREAM and UDP with SOCK_DGRAM. All these constants are located inside socket module.

After socket creation, we are ready to connect to our server. You need to provide a tuple with IP (or host domain name, like “croozeus.com”) and port. Since the transport protocol was set when you created the socket, all elements for localizing the server are prepared. In our mobile phone the connection dialog will be presented and connection will take place. In this example, our server is running on a PC with IP address equals to 10.0.0.100. Probably your local network has different settings so check you PC IP address typing ipconfig (Windows) or ifconfig (Linux) inside some command prompt window. Use this IP in the server and clients scripts.

The “fortune protocol” is very simple: just connect and wait for your fortune message, no needs for requesting.  Other socket function is called for handling the reception, named recv. recv need at least one argument: the maximum amount of bytes to receive at each recv call. When the connection is closed, recv will return an empty string. People erroneously believe that each recv call will receive a complete message or the amount of bytes specified in the call. Take care, TCP is byte oriented, not message oriented. So, network and operating system conditions (delay, buffers, throughput and so on) may change the amount of bytes received at each recv call. Even the streaming is guaranteed by TCP, the programmer must check for his protocol integrity. In our case, we are just receiving bytes and adding them to the fortune buffer while the connection is not closed by server.  In this case, the connection is closed by client as well. This line could be omitted, since sockets are automatically closed when they are garbage-collected.

Run the server script in our PC and execute the client script to test. Some screenshots below:

post3_ss2 post3_ss1

As you can see, there is a lot of knowledge even in this very short program. On purpose, I have omitted several details like exception handling and network configuration, just to keep the focus. I will address these topics in the future. See you there !
Server code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Fortune server - PyS60 network programming
# Marcelo Barros de Almeida
# marcelobarrosalmeida@gmail.com
 
# Supporting Python 1.4.x, 1.9.x and PC
try:
    from btsocket import *
except:
    from socket import *
 
from random import randint
 
# Fortune database from The Zen of Python, by Tim Peters (import this)
PHRASES =[ u"Beautiful is better than ugly.",
           u"Explicit is better than implicit.",
           u"Simple is better than complex.",
           u"Complex is better than complicated.",
           u"Flat is better than nested.",
           u"Sparse is better than dense.",
           u"Readability counts.",
           u"Special cases aren't special enough to break the rules.",
           u"Although practicality beats purity.",
           u"Errors should never pass silently.",
           u"Unless explicitly silenced.",
           u"In the face of ambiguity, refuse the temptation to guess.",
           u"There should be one-- and preferably only one --obvious way to do it.",
           u"Although that way may not be obvious at first unless you're Dutch.",
           u"Now is better than never.",
           u"Although never is often better than *right* now.",
           u"If the implementation is hard to explain, it's a bad idea.",
           u"If the implementation is easy to explain, it may be a good idea.",
           u"Namespaces are one honking great idea -- let's do more of those!" ]
 
HOST = "10.0.0.100"
PORT = 54321
 
s = socket(AF_INET,SOCK_STREAM)
s.bind((HOST,PORT))
s.listen(5)
 
while True:
    (cs,addr) = s.accept()
    fortune = PHRASES[randint(0,len(PHRASES)-1)]
    cs.sendall(fortune)
    cs.close()

PS: images from www.opencliparts.org.

Related posts:

  1. Network programming for PyS60 (VII) Everything is about “protocols” in computer networks, doesn’t it ?...
  2. Network programming for PyS60 (XV) As discussed in post III, TCP sockets have flow...
  3. Network programming for PyS60 (IX) A new element was presented in our last post: exception...
  4. Network programming for PyS60 (XII) Until now we have used only TCP in our examples...
  5. Network programming for PyS60 (V) Before some more practicing, it is time to finish the...

Related posts brought to you by Yet Another Related Posts Plugin.