Wiley.Mobile.Python.Rapid.prototyping.of.applications.on.the.mobile.platform.Dec.2007 (779889), страница 33
Текст из файла (страница 33)
The requested URLspecifies the service and its parameters.In this case, the result is a string that includes a JSON-encoded dictionary with contents that are specified in the Yahoo! API documentation.If the example generates an error, it may be because of API changeson the Yahoo! side. You can check the current documentation providedby Yahoo! and try to fix the example. Chapter 9 will cover many moreexamples like this one.172MOBILE NETWORKING8.3.4 Setting the Default Access PointAfter trying out the previous examples, you have seen that PyS60 showsthe access point selection menu, like the one in Figure 8.4, almost alwayswhen a new network connection is about to be opened.On some phone models, the menu is shown only the first time when anetwork connection is opened on the PyS60 interpreter and the interpretermust be restarted to choose another access point.PyS60 provides the necessary functions, as a part of the socketmodule, to set a desired access point programmatically.
Example 72shows the access point selection dialog and sets the default access point,so the dialog will not be shown during subsequent connection attempts.Example 72: Set the default access pointimport socketap_id = socket.select_access_point()apo = socket.access_point(ap_id)socket.set_default_access_point(apo)The function select access point() pops up the dialog andreturns the access point ID, ap id, that is chosen by the user. Thefunction access point() converts the ID to an access point object.
Thereturned object is then given to the function set default accesspoint(), which sets the default access point.In Chapter 9, the EventFu and InstaFlickr applications show how thesefunctions can be used in practice. The PyS60 API documentation includessome further examples of possible uses of these functions.8.4 Server SoftwareThe simple TCP server that was presented in Example 69 has a majordrawback: it can serve only one client at a time. If only a few clientsuse the server, it is unlikely that several clients will try to connect to itsimultaneously and each client will get a response without a noticeabledelay. However, this approach does not scale.Writing robust, secure and scalable server software from scratch is noteasy. Fortunately, in most cases you do not have to worry about this.
Ifyou are experimenting with new ideas or you are making a prototypefor a restricted number of people, you can freely use any approach thatworks, like the simple TCP server above. However, keep security in mindin these cases as well – a protected intranet or a correctly configuredfirewall keeps you safe with only little additional effort.Remember that everything sent by TCP, HTTP or JSON is directlyreadable by anyone who can tap into the network you use.
If you use anSERVER SOFTWARE173open or weakly encrypted WiFi network, capturing the traffic is especiallystraightforward. Do not rely on passwords sent over these protocols anddo not assume that requests always originate from a trusted client.PyS60 supports Secure Socket Layer (SSL) and Secure HTTP (HTTPS).These protocols are a must if you need to transfer any sensitive information.
More information about these protocols can be found in the PyS60API documentation.If you are building a production system or you are about to releaseyour code to the public, it is usually a good idea to rely on an existingserver framework, such as Twisted. An even easier approach is to makeyour server available as a web service, so you can rely on any webframework, such as Django or Ruby on Rails, for security, scalability andother advanced features.8.4.1 JSON ServerIn this section, we extend Example 69 to do something useful.
Technically,Example 73 demonstrates how to use JSON over plain TCP. The exampleconsists of a client program that runs on your phone and simple serversoftware that runs on your server.The phone client lets the user take named photos which are automatically sent to the server. The photo is taken immediately when thename dialog closes. You can easily add a viewfinder to this program, forinstance, based on Example 34.Example 73: JSON photo clientimport json, socket, camera, appuifwPHOTO_FILE = u"E:\\Images\\temp.jpg"def send_photo(name, jpeg):msg = {"jpeg": jpeg, "name": name}sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect(("192.168.0.2", 9000))out = sock.makefile("w")out.write(json.write(msg))out.close()while True:name = appuifw.query(u"Photo name", "text")if not name:breakprint "Taking photo.."img = camera.take_photo(size = (640, 480))img.save(PHOTO_FILE)jpeg = file(PHOTO_FILE).read()print "Sending photo.."send_photo(name, jpeg)print "Photo sent ok"print "Bye!"174MOBILE NETWORKINGThe function send photo() is given the JPEG contents of the newlytaken photo and the photo’s name.
These parameters are placed in a dictionary called msg. The dictionary is then encoded to a string using function json.write() and the string is sent over TCP. With JSON, dictionaries provide a handy way to construct protocols of your own. The JSONgateway, which is presented in Section 8.6, provides an example of this.The server-side code for the photo repository extends Example 69.Example 74: JSON photo serverimport SocketServer, jsonclass Server(SocketServer.TCPServer):allow_reuse_address = Trueclass Handler(SocketServer.StreamRequestHandler):def handle(self):msg = json.read(self.rfile.read())fname = msg["name"] + ".jpg"f = file(fname, "w")f.write(msg["jpeg"])f.close()print "Received photo %s (%d bytes)" % (fname, len(msg["jpeg"]))server = Server((’’, 9000), Handler)print "WAITING FOR NEW CONNECTIONS.."server.serve_forever()First, the request handler function handle() receives all data fromthe TCP connection until it is closed by the client.
This is done withself.rfile.read(), where self refers to the Handler object andrfile to a file-like object inside it that represents the open TCP connection (’r’ in ‘rfile’ stands for reading).The client has encoded the data with JSON, so we decode it here withjson.read(), which returns the dictionary created by the client. TheJPEG data is written to a file using the provided file name. You can checkthat the image is transferred correctly by opening the JPEG file with yourfavorite image viewer.Note that, to be more secure, the server would have to check the filename in msg["name"] to make sure that the client cannot overwrite anyprevious files or write images to inappropriate locations on your hard disk.8.4.2 HTTP ServerSome of the following examples require a working web server.
If youhave already installed a web server and you have a working web backend, such as PHP, Ruby On Rails or TurboGears, to handle requests, youcan use it – this applies also to environment D. You should be able toport the server-side examples to your framework of choice without toomuch effort.SERVER SOFTWARE175If you do not have such a back end already in place, now is agood time to install one! However, if you want to get into actionquickly, a simple web server with a naı̈ve request-handling mechanismis provided. The code relies on two HTTP server modules in Python’sstandard library, BaseHTTPServer and SimpleHTTPServer, whichimplement a rudimentary HTTP server.The HTTP server example is divided into two parts. Example 75implements a generic web server that is re-used in later examples.Example 76 shows simple request handlers that demonstrate the serverusage. The later examples implement their own functions.
You shouldcombine the generic web server (Example 75) and example-specificfunctions to make the full server.Example 75: HTTP serverimport BaseHTTPServer, SimpleHTTPServer, cgi, traceback, jsonclass Server(BaseHTTPServer.HTTPServer):allow_reuse_address = Trueclass Handler(SimpleHTTPServer. SimpleHTTPRequestHandler):def do_POST(self):try:size = int(self.headers["Content-length"])msg = json.read(self.rfile.read(size))reply = process_json(msg)except:self.send_response(500)self.end_headers()print "Function process_json failed:"traceback.print_exc()returnself.send_response(200)self.end_headers()self.wfile.write(json.write(reply))def do_GET(self):if '?’ in self.path:path, query_str = self.path.split("?", 1)query = cgi.parse_qs(query_str)else:path = self.pathquery = {}try:mime, reply = process_get(path, query)except:self.send_response(500)self.end_headers()print >> self.wfile, "Function process_query failed:\n"traceback.print_exc(file=self.wfile)return176MOBILE NETWORKINGself.send_response(200)self.send_header("mime-type", mime)self.end_headers()self.wfile.write(reply)When a web browser requests a URL from this server, the functiondo GET is called.
First, the function checks whether the URL containsany parameters, specified after a question mark. If it does, the parametersare parsed into the dictionary query with function cgi.parse qs().Otherwise, query parameters are initialized with an empty dictionary.According to the HTTP specification, GET requests must not changethe internal state of the server. All information which affects the server issent in POST requests and encoded in JSON. Replies to the POST requestsare also encoded in JSON.Example 76: Simple request handlers for the HTTP serverdef process_json(msg):return msgdef process_get(path, query):return "text/plain", "Echo: path '%s’ and query '%s’" % path, query)def init_server():print "Server starts"init_server()httpd = Server((’’, 9000), Handler)httpd.serve_forever()Each example may perform example-specific initialization in the function init server().The requests are handled by the functions process get() andprocess json().