2007年11月21日
Test Server and Client
Hello,
Today we are going to go through a quick tutorial on using the OOO library I introduced in my last post. First, I’ll explain how to get the libraries, build them and run the test server and client. After that I’ll introduce some details about distributed objects which are the backbone of the OOO library.
Before starting I’m going to assume that you have a basic understanding of Java and your working in Linux. It’s possible to build the OOO library in a Windows environment but it requires some changes to the build files. If people really want to use Windows I’d be happy to go over that later. Also, Apache Ant knowledge is a plus but it’s not required.
So let’s begin!
The OOO libraries require some tools to build and run it, Apache Ant 1.6 or higher and JDK version 1.5. Grab these via aptitude or from their websites and install them.
After you have installed the prerequisites you can grab the OOO libraries using subversion “svn co svn://code.threerings.net/gardens/trunk gardens“. Gardens is just a nice package that OOO provides for people who wish to develop games using their library. For the sake of simplicity I’m going to assume that you’ll checkout the gardens library under “myworkfolder/gardens/“. Grab a cup of coffee it’s quite a big download.
Once the source has finished downloading go into the root folder “myworkfolder/gardens/” and type “ant distall” this will use ant to build all the libraries and create .jar files that you need to run the server.
After the library has been built a folder called “dist” (distribution) should have been created in the root gardens folder. This contains all the jar library files needed for your application or game. For the time being we don’t need to worry about this it’s just something you should be aware of.
Now go into “myworkfolder/gardens/projects/narya/tests/” folder. Inside here is a sample server and client. But before we do that we should add a few lines to build.xml to make it easier to run. If you want to, you can back up the build.xml file before editing. Open “myworkfolder/gardens/projects/narya/tests/build.xml” and add these lines to the end of the file.
<target name=“runserver”>
<java classname=“com.threerings.presents.server.TestServer“ fork=“true”>
<classpath refid=“classpath”/>
</java>
</target>
<target name=“runclient“>
<java classname=“com.threerings.presents.client.TestClient“ fork=“true”>
<classpath refid=“classpath”/>
</java>
</target>
then save the file. These define two ant targets that will let your run the server and client besides having to manually run it using the java command.
Next it’s time to run the sample server and client. Open one terminal and type “myworkfolder/gardens/projects/narya/tests/ant runserver“. Then open another terminal and type “myworkfolder/gardens/projects/narya/tests/ant runclient“.
If everything goes right you should see one line with the words “test request …” appear on the server side and the client should pop out a message like “test response …”. That basically means the server and client were able to communicate with each other. For right now ignore any warnings that you see. I’ll explain that later.
So let’s take a step back and look at what just happened by looking at the client code. Open up “myworkfolder/gardens/projects/narya/src/com/threerings/presents/client/PresentsClient.java“.
In the main function we create a instance of something called “UsernamePasswordCreds“. These are the credentials we send to the server when the client logs on. We pass this to the client object which is an object which holds all the connection data. When we call “client.logon()” the client sends these to the server and the server authenticates these credentials and sends back some kind of bootstrap data the client can use to connect.
public static void main (String[] args)
{
TestClient tclient = new TestClient();
UsernamePasswordCreds creds = new UsernamePasswordCreds(new Name(“test”), “test”);
…
client.logon();
…
}
After the client gets the “OK” from the server the client-side framework calls back the client listener function clientDidLogon. This is done by implementing a special observer interface called “SessionObserver” and registering the client as a listener by calling “client.addClientObserver(tclient)“. Many of the client server communication features use this kind of Observer Pattern including DObjects which I will talk about later. Inside this function a lot of things are happening but we want to focus only on two things for now, requesting a service and calling it. Services are functions on the server side that can be called directly by the client using a normal function call, basically RPC. These provide a private means of communcation between one client and the server. The code “client.requireService” grabs the object on which the client can call the server functions. Once we have that instance the client can call the server functions by simply calling the functions. “service.getTestOid” is a service function created to return the object id of a distributed object or DObject.
public void clientDidLogon (Client client)
{
…
TestService service = (TestService)client.requireService(TestService.class);
…
// get the test object id
service.getTestOid(client, this);
}
A distributed object is a way to replicate data between the server and client. It’s a object in which both the server and client have a instance. When the server updates any field in the object the changes are automatically replicated in the clients copy. But before the client can use the object they must subscribe to it. One way to think of it is as a magazine subscription. Each time the DObject (magazine) gets updated by the server (publisher) they send the updated versions to the subscribers. This way the client (customer) will always have the latest version. This also means all the clients will have synchronized objects because the client broadcasts this change to call clients.
So how do we subscribe to the DObject? Well, we need to first find it’s oid (Object ID) or if your thinking in terms of magazines, its unique name like Newsweek. We get this by asking the server (magazine publisher) for the id (magazine name) by calling “service.getTestOid“. When we call this function the server (magazine publisher) gets our request and calls back our listener function “gotTestOid” which passes in the oid (magazine name) of our DObject (magazine).
public void gotTestOid (int testOid)
{
// subscribe to the test object
_client.getDObjectManager().subscribeToObject(testOid, this);
}
Once we have the oid (magazine name) we can subscribe to it. Inside of “gotTestOid” we call “subscribeToObject” with the oid (magazine name). The server (magazine publisher) gets our request to subscribe to the DObject (magazine) and sends back the real DObject (magazine) to the function listener “objectAvailable“.
public void objectAvailable (TestObject object)
{
object.addListener(this);
Log.info(“Object available: “ + object);
object.setBar(“lawl!”);
}
Before going further I want to say a few things about security of the DObjects. With many clients connected to one server, data security becomes a concern. Which means that you need to always assume that the client cannot be trusted. The client should usually first send a request to the server. Then, the server should verify the client’s request, process the request and return the results to the client. So what does this mean for DObjects? DObjects are shared objects that multiple clients including the server use at the same time so if one client manages to place malicious data into the object then everyone would be affected. This means that the clients can never directly change the values of the DObject. They can only request to change it via the services which I described before. In terms of the magazine publisher, if the customer wants a new feature or article in the magazine the customer has to first request it to the publisher. Then the publisher updates their master copy and re-sends it to all subscribers. When you ran your server you might have seen a “WARNING: Event failed permission check“. This was because the client was trying to modify the DObject locally. The server refused the request because the client doesn’t have authority directly modify the DObject.
public static void main (String[] args)
{
TestClient tclient = new TestClient();
UsernamePasswordCreds creds =
new UsernamePasswordCreds(new Name(“test”), “test”);
Client client = new Client(creds, tclient);
tclient.setClient(client);
client.addClientObserver(tclient);
client.setServer(“localhost”, Client.DEFAULT_SERVER_PORTS);
client.logon();
// start up our event processing loop
tclient.run();
}
Next time we will create our own project and use the DObject and services to broadcast messages to multiple clients.
- Posted by brian on 11:33 filed in Threerings, English
- パーマリンク



Leave a Comment
Trackbacked