Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007
Team: Everyone Gets Paid
Welcome to our case page for CS2340 Spring 2005. First off I will briefly go over what we had to do to accomplish our project, then I will provide you with some good pointers on how to trick Squeak into doing what you (and the professor) want it to do. Since I wrote most of the networking code most of what I post will be network related, but I will also give some nice tricks for saving object state via XML.
Jeremy - Network Programmer & L33t Case Page Writer
Will - GUI/View Programmer
Duckett - DB
Nate - UML
For the spring semester 2005 we were supposed to implement an air graffiti service (like the real one that made the CoC website a while back except muuuuuch lamer) for the desktop and for a PDA. Generally, our project was quick and painless. Two of us concentrated on design and two of us coded. Some screenshots of the final product:
It's a very simple interface. It's not flashy, but it always met the required functionality for each milestone.
So the Air Grafitti server is supposed to allow users to log on and leave notes in various locations for other people. A user should be able to search for notes within a certain radius in a location and then have a list of all the notes cooresponding to their username displayed. Designing this for a Desktop interface was really easy, but porting to the PDA posed some problems (which I will describe later). Milestones 1 and 2 were trivial prerequisites for the final product so I won't describe them.
Design time. Since we were all but given a working server client pair (thanks Lex =) ) all we needed to do was design our protocol and our client model view. Lex's original networking example used a Morph as a server stepper but we planned on actually implementing a server view (see screenshots) for more control. The Morph worked fine for Milestone 2, but I was always having problems with non-terminated connection queues (see bug list) and I had no idea why. The class comment for our server describes our protocol. Note that the items with parenths around them are actually arrays of strings.
This is the main server for our location based service. When the server is initialized it will read in a list of
locations from an xml file called "locations.xml". If it cannot find this file, it will return an error and shut down.
DEFAULT PORT: 7878
Instance variable descriptions:
locations -> A list of all the locations that this server can currently support.
queue -> A ConnectonQueue that will handle new connections.
connections -> A list of all the currently attached to this server.
port -> The port that this server listens to.
After successfully starting up, it will listen on a default port for incoming connections. When the server receives a request
for a connection, it will accept the connection, create the user by parsing the login string and assign them a client ID. It
will then store that person in a dictionary using the client ID as the key and assign them the default location of NOWHERE.
Clients may communicate with the server by using the following protocol:
(LOGIN,userID,name,decription) - This message will be sent if a client wishes to log in. It will contain the name, description,
and the serialized map of the location.
(MOVE,userID,new location) - This message will change the users location and cause the server to sent an UPDT message to all
connected clients (or just clients within a location) so that they reflect the change.
(CHME,userID,new name,new description) - This message will change the users details and cause the server to send an UPDT message
to all connected clients so that they reflect the change.
(GTUSR,userID) - This message will tell the server to send back the details about a person.
(BYE,userID) - This message will be sent when a client wishes to log off. The server will shut down the socket associated with
The server may communicate with the clients by using the following commands:
(WELC,location list) - This message will signal to the client that the logon was successful.
(NUSR,serialized location object) - This message will be sent to all the clients in a location whenever a new user appears in
(NLOC,serialized location object) - This message will be sent to a client when it requests a location change.
(USRDAT,serialized person object) - This message will be received when the client requests user information.
(BYE,user ID) - The server will send this message to all clients to signify that the client with the supplied ID has logged of
or left the current location.
NOTE: Most of the code for this server was originally written by Lex Spoon. I just modified it to suite our purposes. ~jld
Milestone 4 was interesting. We were forced to port our desktop app to the constrained resources of a HP IPAQ 5550. The port was semi-painless. All we had to do was rewire our view. Due to the way we designed our model/view interaction we had to change around 3 methods and the coding side of the project was finished. One big problem we ran into during this project was trying to get our PDA to connect to Tech's wireless network. We sat around for about an hour trying to figure it out then gave up and connected the PDA to Will's laptop and used the laptop's wireless. After getting the project to run on the PDA we had to perform a few minor GUI tweaks then we were finished. We had no problems with memory whatsoever, and the network ran decently fast. The most agitating part of this milestone (and prehaps the entire project) was exiting Squeak on the PDA without a custom world menu. It requires some skill.
The following additions were made to the protocol for milestone 4:
CLIENT -> SERVER
(NGET, location, user name) - Gets all the notes for a user at a specific location
(NADD, location, note object) - Gives a note to the server to add to some location.
(NREM, location, note, id) - Tells the server that the client wishes to delete a note at a specific location.
Note that the client has to send its ID along with the rest of the info. If the id doesnt match the creator or
recipient of the note then it cannot be deleted (this is a primitive form of security).
(NLIST, id) - Gets a list of all the notes on the server for the passed in id.
The following changes were made to the server for milestone 4:
1) Completely changed the view of the server. It is now controlled by a system window that will step the server
accordingly. This cuts down on the number of classes but does not add to the complexity of the system; instead it
made it much cleaner and easier to understand.
This milestone was the bulk of the project for us. This is also the milestone where all the SUnit tests actually worked. SUnit was a continuing source of frustration for all of us. Either they didnt work correctly or we couldnt figure out how to test a certain aspect of a class (networking for instance). By the end of this milestone we had around 25 working SUnit cases that helped us track down a couple of bugs when we changed something (so yes, they are worth something). This milestone is where the server was upgraded to a SystemWindow instead of a Morph. This took care of the connection queue problem I mentioned earlier (due to cleaner code, not the fact that we were using a morph). I also had an extensive system of Transcript show: calls that were crucial to debugging the new server system.
Major Project Problems
Annoying Bug Number 1:
Occurance: Milestone 4
Environment: A standard Squeak image with Sixx XML serializer installed and the freaky eyeball mouse dead.
The Symptoms: After around 10 connections to the server from within the image the client would no longer connect to the server.
Debug Procedures and Resolution: After verifying via self halts and self inspects that the client was trying to connect to the correct port and net name I had a hunch that it was something to do with orphaned sockets remaining open with the client after getting a primitive failed exception. So after much cursing Squeaks name I loaded up a new image and filed in our changes. Everything worked fine again for around 10 or 11 connects, then I had the same problem. After retracing every line of client and server code I found that this bug was caused by my ignorance. I wasn't properly closing sockets so my code for pruning dead connections wasn't working. So I fixed that, loaded up a new image, and I ended up with the exact same problem AGAIN! At this point I want to take a sledgehammer to the States Machine I was working on, so I bit the bullet, forgot about the bug, and made sure we demo'd on a fresh image.
Return of Annoying Bug Number 1:
Occurance: Milestone 5
Environment: A standard Squeak image with Sixx SML serializer installed and the freaky eyeball mouse still dead.
The Symptoms: Still cant connect to the server more than 10 times.
Debug Procedures and Resolution: So ignoring a bug obviously wont make it go away. I rewrote our server to use a system window stepper instead of a morph stepper. This allowed me to control when the connection queue was shut down via a start and stop button. Im sure you can do this with an override to the delete method in morph, but for some reason that just wasn't working correctly for me. After properly setting up the SystemWindow to control my server everything worked fine. This bug was officially dead!
The #@$%^! Squeak is Broken Bug:
Occurance: Milestone 4 & 5
Environment: Our LocationViewer's TextMorphs.
The Symptoms: When hitting backspace inside a TextMorph Squeak randomly throws an Object not Indexable Error.
Debug Procedures and Resolution: This one, well, it kinda just went away. We think it's a problem within the Squeak environment (which is VERY moody anyways). We have no idea why this happens so we just ignored it. It never threw the error during demo so why bother.
How to trick Squeak into doing something useful
In this section I will describe a couple of techniques that really helped us during debugging (which was usually only about an hour of our meetings) and some other nice Squeak hacks.
General Debugging Tips:
- self halt: Use these religiously. They are great for determining the state of your program before and or after the bug occurs. Once you have a general idea as to where your bug is located you can place self halts throughout the method thats giving you trouble. These are invaluable.
- self inspect: Almost as useful as self halts. These little guys will call inspect on the class thats currently doing a method call. I used these to help figure out how many times a method was called and to debug alot of my lists during multiple operations. They are great when you dont need to inspect the state of the entire system.
- Morph Halos: Not the Halo that I should be playing right now but program halos. Alt click on any window in the Squeak environment and you get these funny little spheres with pictures on them. This one is especially useful: . It is the debug halo, and it pulls up this menu:
This gives you the option to look at the model or the view of the morph that you called it on. Both favorites.
Hacking Networking in Squeak:
- Networking in Squeak is really easy, but unfortunately some of the classes in 3.5 are completey broken. ArbitraryObjectSocket doesnt work so you can forget about passing objects around...or maybe not! There is a ray of hope: SIXX. SIXX is an object serializer for Squeak that converts an object to an XML string. That, along with the working StringSocket class, was all we needed to complete the project. Everything that is sent across our network is serialized using SIXX, slapped in a string array, and deserialized on the other side. Once you have your basic framework coded all you need to do is define your protocol and implement it. Then get your client and server talking to one another by connecting to localhost. Once thats happening your network is nearly finished.
- Now figure out which objects you need to send across the network, serialize them using SIXX, send them across, deserialize, analyze the data, and then reply. I find its much easier to have a server that doesnt care about sending to individual nodes on your network for world updates. In our case, one would think it would be nice if the server only updated the people in specific locations if someone within that location made a change. This would cut down on useless network traffic to the people not in that place, but its alot tougher to try and keep up with which socket belongs to which client than to just keep up with a list of all the connected sockets. Our server just didn't care. Updates were global and the client decided if it needed that update or not.
- The one item that kept giving me trouble was getting the client to log off the server nicely. If you tell the server that you want to log off, then destroy the connection you will get a primitive failed error unless you give the client enough time to send its request to the server. Also, you can only send this message once (which rules out looping over the sockets processIO method) else when the server gets the logoff message and kills the socket the client will bomb. So whats the solution?
- Making it work: I had a boolean that told the client if the connection is doomed or not (i.e. the client wants to go away). Inside a method called logoff I send the server (when told to by the view) the logoff message and call processIO once on the connection. Inside the clients processIO method I loop over the processIO of the clients socket unless the connection is doomed. This, along with a reset of the connection when the client wishes to log on again, allows the client to log on and log off without any trouble. You can check all this out in the client.st file posted below.
- Another (cleaner) way of doing it would be to have the server send an ACK to the client. But since I like hacking Squeak my way is better.
Saving the State of Objects Using SIXX:
I stumbled upon this whilst tinkering with our M6. I needed a way to save and load the state of two lists called userAccts and Vendors so that those accounts persist after the server is shut down. So instead of thrashing through the DOM class and figuring out how to manually parse XML (for loading state), I figured why not use SIXX to do the dirty work for me? After all, it converts objects to XML as it is! So, heres a smidgeon of code to consider:
(FileStream isAFileNamed: 'usracts.xml') ifTrue: [
usrActsFile := FileStream fileNamed: 'usracts.xml'.
userAccts := Object readSixxFrom: (usrActsFile contentsOfEntireFile).
] ifFalse: [ userAccts := Set new. ].
This code will look for a file called usracts.xml in the Squeak root directory, and if it exists will load the last saved state of our userAccts list from that file! If the file doesnt exist, then theres nothing to load so it will simply create a new userAccts set. Neat huh? Now for the saving state case:
((userAccts notNil) and: [userAccts size > 0]) ifTrue: [
Transcript show: 'SERVER: Saving user accounts...'; cr.
usrActsFile := FileStream forceNewFileNamed: 'usracts.xml'.
usrActsFile nextPutAll: (userAccts sixxString).
usrActsFile close. ].
It couldn't be any easier! Oh, and remember this will only work if the file is created per example 2 and read in per example 1. Else SIXX will get confused.
Make sure you have SIXX filed in or the whole system wont work. If you're just downloading it to hack my server/client, inside LBSServ2 delete the the users, locations, and notes instance variables, delete the method categories 'other' and 'testing/debugging', delete the SUnit test class, change the initDebugMode class method, the processMessage:fromConnection: instance method, and the initialization instance method to fit your system and it should work. Do the same kind of thing to the client class and you are good to go.
Get the almighty SIXX here!
Link to this Page
- Cases last edited on 30 July 2011 at 2:33 am by r59h132.res.gatech.edu