Download Figure 6. Testing result for socket client/server running on

Document related concepts
no text concepts found
Transcript
COMPSCI 380FT
UNDERGRADUATE COMPUTER SCIENCE PROJECT
Distributed Eliza
Student Name: Guoxiang Lu
Student ID: 2371657
Project Supervisor: Dr S. Manoharan
Date Completed:
May 2001
Abstract
The main aim of this project is to test and compare the performances of several different distributed
communication media. Also, we would like to find out how difficult or easy to implement them.
Due to their availability on our UNIX servers, C sockets, Message Queue and Java/Remote
Method Invocation (Java RMI) are chosen as the distributed communication media to be tested.
All the three distributed paradigms are tested and compared on a single machine. However, C
sockets and JavaSoft’s Java RMI are tested remotely on different servers and then compared. As
Message Queue does not support network communications, it can only be tested on the same
machine.
To make the client/server programs running interactively and continuously, a popular Artificial
Intelligence program called “Eliza” is used as a message generator for the client/server programs.
To modularise our C programs, two static libraries are created respectively for C socket and
message queue programs.
To integrate these three communication media into one complete program and also make the whole
program presentable, the following three-layer communication design model is implemented.

The outer layer
A GUI client is implemented using Java. The GUI client has a socket connection to the
middle layer.

The middle layer
The middle layer allows switching from using one type of communication medium running
Eliza client/server programs to using another type. It passes the messages by the Eliza client
and server to the outer layer for displaying.

The inner layer
The Eliza client and server programs communicate using different communication media.
-i-
Table of Contents
§1. Introduction……………………………………………………………….….……..…….…1
1.1 Aim of the project.………………………………………….............……………..…….......….1
1.2 The design model of program integration...……………………………….....….....….….….....1
§2. Building up the Distributed Eliza Programs….…………………………....….……....….….3
2.1
2.2
2.3
2.4
2.5
Setting up the C Client/Server Socket Programs………………………………............……….3
Implementing my Simple Version of “Eliza” in C……………………….....…......…………...5
Putting the Eliza program and C Socket Client/Server Together…………..….........……….…7
Setting up the C Message Queue Eliza Programs…..……………………....….......…………...7
Setting up the Java RMI Client/Server Eliza Programs……..…………………....….........…....9
§3 Testing and Comparison of Performances……………………………….……………...…...13
3.1
3.2
3.3
3.4
3.5
Testing performance of C Socket Client/Server………………………..…....…......…………..13
Testing performance of C Message Queue……………………………….…...….......………...18
Testing performance of Java RMI Client/Server………………………………....…......….…..20
Comparison of Performances………………………………………………………..….............23
Comparison of learning and usages………………………………….……………..…..............25
§4 Program Modularization………………………………………………………….……….…27
4.1 Creating Libraries for C Socket Programs………………………….…………….….…............27
4.2 Creating Libraries for Message Queue Programs…………………..…………….…….............28
§5 Program Integration…………………………………………………….……………..……..30
5.1 Writing the General Interface to Three Types of Eliza programs……….…….………..............30
5.2 Running the Integrated Eliza Programs……………………………………….………..…….....32
Conclusion......................................................................................................................…...........35
Acknowledgement...........................................................................................................…..........36
References....................................................................................................................…..............36
Programs Appendix …..................................................................................................…........…37
- ii -
List of Figures
Figure 1. The three-layer data communication model of Distributed Eliza ..…………………….…...2
Figure 2. The basic model of the socket client/server communication………………….….................3
Figure 3. The basic model of the message queue communication…………………….…................…7
Figure 4. The design model of the Eliza Client/Server communication in Java RMI….…...................9
Figure 5. PING--PONG client/server communication model for performance testing……….………13
Figure 6. Testing result for socket client/server running on “wedge”………………………...........…15
Figure 7. Testing result for socket client/server on “wedge” and “m3r”………………….....….….....16
Figure 8. Testing result for socket client/server running on “m3r”…………………………….……..17
Figure 9. Testing result for message queue on “wedge” terminals……………………..……..............19
Figure 10. Testing result for Java RMI client/server running on “wedge”………………….….…......22
Figure 11. Testing result for Java RMI client/server running on “wedge” and “m3r”…….…..….......23
Figure 12. Performance Comparison (client/server running on “wedge”)………….………................24
List of Tables
Table 1. Testing result for socket client/server running on “wedge”…………....…......….….….…....15
Table 2. Testing result for socket client/server on “wedge” and “m3r”…………..…..........….….…...16
Table 3. Testing result for socket client/server on “wedge” and “m3r”…………..…..........….….…...17
Table 4. Testing result for message queue on “wedge” terminals…………………….....….......…..…19
Table 5. Testing result for Java RMI client/server running on “wedge”……………............…...….…22
Table 6. Testing result for Java RMI client/server on “wedge” and “m3r”………….............…...……23
Programs Index
File Name
sserver.cpp
sclient.cpp
eliza.cpp
mqserver.cpp
mqclient.cpp
iRmiEliza.java
RmiElizaServer.java
rmiElizaServer.cpp
makefile
RmiElizaClient.java
rmiElizaClient.cpp
clientmakefile
testSockClient.cpp
testMqClient.cpp
Timer.h
Timer.cc
Page
37
38
39
45
47
48
49
49
50
50
51
52
52
53
54
55
File Name
iRmiTest.java
testRMIClient.java
testRMIServer.java
eliza.h
GLlibSocket.cpp
sock.h
sockserver.cpp
sockclient.cpp
GLlibMq.cpp
mq.h
mqueueserver.cpp
Controller.cpp
RmiElizaClient2.java
ElizaClient.java
ElizaClientGUI.java
Page
57
57
58
59
59
61
63
64
65
66
69
69
71
72
75
- iii -
§1. Introduction
1.1 Aim of the project




Examine and compare the performances of different communication media
Compare how easy or difficult to learn and implement them
Learn the C/C++ programming language
Learn program modularisation and integration
With the fast growing Internet, distributed programming plays a more and more important role in
the Information Technology world. Different communication media have been used to provide
distributed services. Therefore, It is essential to know what is the appropriate programming
paradigm to be applied for different situations.
In order to tell which one is more suitable or better than another, we have to examine and compare
the performances of different communication media for a particular purpose, as well as investigate
which is easier to learn and implement than another.
The implementation of this project largely depends on what is available on our system. There are
several popular distributed paradigms available today, such as Microsoft's Distributed
Component Object Model (DCOM), OMG's Common Object Request Broker Architecture
(CORBA) and JavaSoft's Java RMI. In order to learn C/C++, I choose to implement the programs
using the C/C++ compiler on “m3r“ and “wedge” servers. Here only Java RMI together with C
socket and message queue are examined, as these are the only distributed programming tools
available on the servers.
Socket and message queue can be implemented purely by using C. For Java RMI, we have to use
the Java Native Interface (JNI) to embed a C++ program which acts as the communication message
generator. In that Java RMI provides for remote communication between programs written in the
Java programming language.
“Eliza” is a famous AI program written in 1966 by Joseph Weizenbaum at MIT. It is used to study
the communication between man and machine in natural language. Here I will just borrow its idea
and write my simple version of “Eliza“ as a message generator for client/server. The idea of Eliza
program is to make the client/server requests and responses more interesting. This is just for
presentation issue and not necessary for testing purposes.
1.2 The design model of program integration
The next thing to think about is how to implement the program as a design model or architecture.
We plan to use Java to implement the GUI part. Then we will make all the other client/server
programs running on “m3r” or “wedge” server. An interface to the client/server is introduced to
switch client/server from using different communication media. This interface acts as a server
accepting socket connections from the GUI client so that the GUI client can send requests such as
which one of the communication media to be used and when to start Eliza server and client
programs. The model will contain three communication layers which will be fully implemented in
chapter 5.
-1-
The three-layer distributed data communication model for completing the task is shown below.
This general model is just for program integration purpose only. Notice that the ElizaServer and
ElizaClient programs are implemented in three different patterns. This part will be used as the basic
model to examine the performances of the three communication media. The idea of the Interface is
to plug in different media for Eliza client/server, which are requested by the GUI Client. In the
following sections, some of the implementation details will be discussed so as to see the differences
among them.
GUI
(Client)
s
o
c
k
e
t
Interface
(Server
for GUI)
Run/Stop
eliza.cpp
ElizaServer
Socket
communication
media
eliza.cpp
ElizaClient
Eliza Client/Server Programs
Figure 1. The three-layer data communication model of Distributed Eliza
The sequence of writing the following sections roughly represents the project development steps.
-2-
§2 Building up the Distributed Eliza Programs
To start with in this chapter, a C client/server socket connection is built up. After it is tested
successfully, the mimic Eliza program “eliza.cpp” is implemented in C/C++. Then the “eliza.cpp”
program is applied to the socket programs to make interactive conversations between client and
server. Consequently, the message queue is implemented in C with the “eliza.cpp” program as its
message generator. Finally, Java RMI client and server are implemented in Java language, using
the Java Native Interface (JNI) to connect to the “eliza.cpp” program.
2.1 Setting up the C Client/Server Socket Programs
The basic model of socket connection is as the following.
sockfd
Server
S
O
C
K
E
T
nsockfd1
Client1
nsockfd2
Client2
nsockfdN
.
.
.
.
ClientN
Figure 2. The basic model of the socket client/server communication
The development of the C socket is briefly discussed below.
#include
#include
#include
#include
#include
<netinet/in.h>
<netdb.h>
<sys/socket.h>
<arpa/inet.h>
<unistd.h>
1. Writing the server program

Make a server socket
AF_INET – the Internet domain
SOCK_STREAM – stream type
0 – TCP protocol
int sockfd = socket(AF_INET, SOCK_STREAM, 0);

Set the server properties
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(portNumber);
server.sin_addr.s_addr = INADDR_ANY;

Server binds to the file descriptor sockfd. Now server uses this sockfd to exchange data
with its clients
bind(sockfd,(struct sockaddr *)&server,sizeof(struct sockaddr));

Server listens to the channel, up to numConn clients
-3-
listen(sockfd, numConn);

Server accepts clients’ connection, nsockfd keeps the clients information
nsockfd = accept(sockfd, client, &addrsLen);

char *buf
holds the data to be sent out and read in from socket.It is important to know the
size of data to send and receive. Here sizeof(char *buf)-1 and strlen(char *buf) are used to
determine the number of bytes to receive and send respectively.
int nbytes = recv(nsockfd, buf, sizeof(buf) - 1, 0);
buf[nbytes] = ‘\0’; //char array terminator
//fill buf with some other data then send it
send(nsockfd, buf, strlen(buf), 0);


Deal with errors
If function calls return –1, the server will close the current nsockfd for a client and continue
accepting other client connetions
And lasty close the socket when communication ends
close(nsockfd);
close(sockfd);
2. Writing the client program

Make the client socket
AF_INET – the Internet domain
SOCK_STREAM – stream type
0 – TCP protocol
int nsockfd = socket(AF_INET, SOCK_STREAM, 0);

Set the client properties (struct sockaddr_in server)
char *hostname = “wedge.tcs.Auckland.ac.nz”;
const int portNumber = 1234;
struct hostent *hostentry;
hostentry = gethostbyname(hostname);
char hostIP = inet_ntoa(*((struct in_addr *)hostentry->h_addr));
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(portNumber);
server.sin_addr.s_addr = inet_addr(hostIP);

Connecting to server
connect(sockfd,(struct sockaddr *)&server, sizeof(struct sockaddr));

Reading from and writing to socket (Same as server side)

Deal with errors
If function calls return –1, the client will close the nsockfd. Program ends.

And lasty close the socket when communication ends
close(nsockfd);
The client can make a socket connection to the server only when the server is running. Refer to
files “sserver.cpp” and “sclient.cpp” from the program appendix for more details.
-4-
2.2
Implementing my Simple Version of “Eliza” in C
The “eliza.cpp” program is used to generate random char arrays for Eliza Client/Server programs
to communicate with each other. As the purpose of the project is not to develop an intelligent AI
program, here only a simple version is written in such a way that the server can get its messages to
be sent to client from a message pool according to the client’s requests. Similarly, the client also
gets its messges to be sent to server from its message pool according to the server’s responses.
Following is the brief discussion of the program.
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define KSIZE 38 //pool size
#define NUM 9 //maximum items in a category
#define MAXSIZE 256 //maximum length of a message
char sub[MAXSIZE]; // global variable
char appchs[MAXSIZE]; // global variable holds a char array after appending

The initial char array that server sends to client on starting the conversion
char *init = "Hi! I'm Eliza. Please tell me your problem.\nType initial
query: \nQ> ";
char *initDialog() { return init; }

The keywords pool is used to check if the message received by client or server contains any
keywords belonging to a particular category. The next message to be chosen is according to
the keyword detected in the current message.
char *keywords[KSIZE][NUM]={
{"computer", "computers"},
{"dream", "dreams", "nightmare", "nightmares"},
{"you are", "you're", "youre"},
{"i like", "i am fond of", "i'm fond of", "enjoy"},
. . . . . .
};

The server message pool is used for server to select appropriate messages according to
client’s requests. Notice that a “*” is to be replaced by a moidified substring in a client’s
message.
char *response[KSIZE][NUM]= {
{"Why do you like*?",
"When did you decide you like*?",
"What makes you fond of*?"},
{"Why don't you*?",
"Do you wish to be able to*?"},
. . . . . .
};

The client message pool is used for client to select appropriate messages according to
server’s responses.
char *questions[KSIZE][NUM]={
{"I feel very confident.",
"I often feel nervous.",
"I enjoy feeling empty.",
-5-
"Empty is no worry."},
. . . . . .
};

Function char *eliza(char inputline[]) is called by server side to generate a
response. Parameter inputline is the message passed in after receiving it from client.
Refer to the file “eliza.cpp” from the program appendix for more details.
char *eliza(char inputline[]) { . . . . . . }

Function char *clienteliza(char resp[]) is called by client side to generate a
request. Parameter resp is the message passed in after receiving it from server. Refer to the
file “eliza.cpp” from the program appendix for more details.
char *clienteliza(char resp[]) { . . . . . . }

Function char *tolowercase(char line[]). Parameter line is the message
either from client or server. The function is used to convert all characters in a char array to
lowercase so that we can check its substrings with the keywords.

Function char *substring(char line[], int start, int length)
returns a substring with length length in a char array from position start. It is used to
find a keyword in a message.

Procedure void replace(char line[], char *keys1[], char *keys2[],
int pos) repaces first class pronouns with the second class pronouns and vice versa. Eg.
“i” to “you” and “my” to “your”, etc.

Procedure void toappend(char line[], int pos). First it replaces the
pronouns. Then char array appchs holds the substring starting from position pos in line.

Function int getcatnum(char line[], int side) returns the category number
in keywords by checking if any of the substrings in line belongs to a specific category.
Then if calling from client side, it does no appending. But if calling from server side, the
appchs will hold the substring from line to be modified and then appended to the response.
Parameter side is used to distinguish the calling from server or client.

Procedure void insert(char resp[], char chars[], int pos) inserts
chars into resp at position pos.

Function char *getFirstWord(char *line) returns the first small word in line.
The word is to be checked to see if it is contained in { “how”,”why”,”what”,”when”,”where”,
etc. }. If not, and the sentence ends with a “?”, we can generate a sentence starting with “Yes”
or “No”.

Procedure void getInput(char buf[]) puts user’s key typing into buf. It is used
to give a prompt and accept the first request char array from the user from the client
program.
We can see that the program “eliza.cpp” mainly does the operations on a char array.
-6-
2.3
Putting the Eliza program and C Socket Client/Server Together
We are now ready to use our “eliza.cpp” to genarate char arrays for the C Socket Client/Server
programs. Following is the idea how the program runs continuously.





Server accepts client’s connection and sends the initial prompt char array to client
Client accepts the initial prompt from server, accepts user input from keyboard and sends it
to the server as the first query
Server receives the first query and uses the Eliza program to generate a response. Then it
sends the response to the client
Client receives the response from server and uses the Eliza program to generate the next
query. Then it sends the query to the server
Server (client) continuously receiving messages, generating responses (queries) and sending
them to client (server)
Refer to files “sserver.cpp” and “sclient.cpp” from the program appendix for more details.
2.4
Setting up the C Message Queue Eliza Programs
The basic model of message queue communications to be used in our programs is as the following.
eliza.cpp
mqfd1
server queue
.
eliza.cpp
mqfd1
mqserver
mqfd2
mqclient
client queue
mqfd2
Figure 3. The basic model of the message queue communication
Actually, there is no such thing called server or client for message queue. The message queue is
used for IPC (Interprocess Communication). Here for the purpose of explanation, we will give a
process name called server and the other called client. Their names are dertermined by the task of a
process. If it is similar to the socket server or client, we name it mqserver or mqclient.
The development of the message queue programs is briefly discussed below.
#include
#include
#include
#include
<sys/fcntl.h>
<mqueue.h>
<stdlib.h>
“eliza.cpp”
#define MSGSIZE 255
#define PMODE 0660 //specifies access mode ---rw-rw----
1. Writing the message queue server and client

Fill in attributes for message queues
-7-
struct mq_attr attr;
attr.mq_maxmsg = 1; // max # of messages allowed in MQ
attr.mq_msgsize = MSGSIZE; // max size of any one message
attr.mq_flags
= 0; // actions and state for mq operations

For the server side program, set the flags for the open of the server and client queues and
open them. Specify O_CREAT so that the file will get created if it does not already exist.
Specify O_WRONLY for server side queue mqfd1 since we are only planning to write to it.
Specify O_RDONLY for client side queue mqfd2 since we are only planning to read from it.
Make them blocking on opening, meaning they will block if this process tries to send to a
queue and the queue is full. “smq” and “cmq” are strings naming the message queues.
mqd_t mqfd1, mqfd2;
mqfd1 = mq_open("smq", O_WRONLY|O_CREAT, PMODE, &attr);
mqfd2 = mq_open("cmq", O_RDONLY|O_CREAT, PMODE, &attr);

Send the initial prompt to the server queue mqfd1. Function Char *initDialog()
is defined in eliza.cpp.
char msg_buffer[MSGSIZE];
strcpy(msg_buffer,initDialog());
mq_send(mqfd1,msg_buffer,num_bytes_to_send,priority_of_msg);

Read the first query into msg_buffer from the client queue mqfd2
mq_receive(mqfd2, msg_buffer, MSGSIZE, priority_of_msg);

Generate first response, send it to mqfd1
strcpy(msg_buffer, eliza(msg_buffer));
mq_send(mqfd1, msg_buffer, MSGSIZE, priority_of_msg);

Continuously read queries from mqfd2, generate new responses and send them to
mqfd1. Function Char *clienteliza(char *msg_buffer) is defined in
eliza.cpp.
mq_receive(mqfd2, msg_buffer, MSGSIZE, priority_of_msg);
strcpy(msg_buffer, eliza(msg_buffer));
mq_send(mqfd1, msg_buffer, MSGSIZE, priority_of_msg);

Deal with errors
If function calls return –1, program terminates by calling exit(0).

Close the client queue mqfd2 and dispose it after finishing reading messages from it
mq_close(mqfd2);
mq_unlink("cmq");

The client side of the message queue programs is similar to the server side. We only show
something differemt here.
o Specify O_RDONLY for client side queue mqfd2 since we are only planning to read
from it. Specify O_WRONLY for server side queue mqfd1 since we are only planning
to write to it.
mqd_t mqfd1, mqfd2;
mqfd1 = mq_open("smq", O_RDONLY|O_CREAT, PMODE, &attr);
mqfd2 = mq_open("cmq", O_WRONLY|O_CREAT, PMODE, &attr);
o After receiving the prompt from server queue, it gets the user input query from keyboard.
-8-
Procedure void getInput(char *msg_buffer) is defined in eliza.cpp.
getInput(msg_buffer); //accept first query string from user
o After receiveing the responses from server queue, it generates the next query.
Function Char *clienteliza(char *msg_buffer) is defined in eliza.cpp.
strcpy(msg_buffer, clienteliza(msg_buffer));
2. Compiling and running the message queue server and client
Compiling of the progams involves a reference to a static library librt.a, which defines functions
such as mq_open, mq_receive, mq_send, mq_close, mq_unlink etc. So we use
the compilation commands cxx mqserver.cpp -o mqserver -lrt and cxx mqclient.cpp -o
mqclient -lrt to compile the files mqserver.cpp and mqclient.cpp respectively. Then type
./mqserver and ./mqclient to start server and client from two different terminals. Either the
message server or client can be started first.
2.5
Setting up the Java RMI Client/Server Eliza Programs
In this section, we will make use of our eliza.cpp program for Java RMI Client/Server programs.
Because we can not directly call a C function in Java, the JNI (Java Native Interface) is
introduced to complete the task.
librmiEliza.so
JNI
rmiEliza.cpp
eliza.cpp
rmiElizaClient.cpp
librmiElizaClient.so
iRmiEliza.java (Interface)
JNI
Naming_lookup
RMI calls
RmiElizaServer.java
Registry
RmiElizaClient.java
Figure 4. The design model of the Eliza Client/Server communication in Java RMI
Figure 4 is the basic design model used to develop the Java RMI Eliza client/server programs.
Notice that first the RmiElizaServer.java class has to register its remote objects so as to accept
calls on a specific port. Then it binds the specified URL formatted name to the remote object (the
stub) by calling Naming.bind("rmi:///"+"ServerEliza", remoteObject) method.
An interface iRmiEliza has to be introduced as the remote interface which specifies the methods
that can be invoked remotely by a client. The RmiElizaServer.java class is the implementation of
the remote interface to be a remote object.
The RmiElizaClient.java class first obtains a reference to a remote object by calling
Naming.lookup("rmi://"+host+"/ServerEliza") method where host is the host
(remote or local) where the registry is located with a default port 1099, “ServerEliza” is the
remote object name. Then the client invokes remote methods through the reference to the remote
object.
Following we will briefly discuss the implementations of the Eliza RMI programs.
1. The remote interface “iRmiEliza.java”
-9-
import java.rmi.Remote;

Subclass of interface java.rmi.Remote to be a remote object.
public interface iRmiEliza extends Remote {. . .}

Declare two methods which will be concreted by the implemented class. Both throw
RemoteException.
String getInitStr() throws RemoteException;
String getReplytStr(String request) throws RemoteException;
2. “RmiElizaServer.java”: implementing the remote object
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;

Implement interface iRmiEliza to be a remote object. Subclass of
UnicastRemoteObject to obtain remote behavior
public class RmiElizaServer extends UnicastRemoteObject
implements iRmiEliza {. . .}

Declare two native methods which will be defined in a dynamic library librmiEliza.so.
Methods getAnswer() and init() eventually call the functions char
*eliza(char *buf) and char *initDialog() respectively in eliza.cpp.
public native String getAnswer(String request) throws
RemoteException;
public native String init() throws RemoteException;

Load the library librmiEliza.so
static { System.loadLibrary("rmiEliza");

}
Implement methods declared in interface “iRmiEliza.java”. These two methods will be
invoked by the remote objects.
public String getInitStr() throws RemoteException {
return init();
}
public String getReplytStr(String request) throws
RemoteException{
return getAnswer(request);
}

Set up “RmiElizaServer” and start it up. Refer the program appendix for more details.
public static void main(String args[]) { . . . }
3. “rmiEliza.cpp”: C program defining the native methods in “RmiElizaServer.java”
Once the “RmiElizaServer.java” has been compiled into “RmiElizaServer.class”, we can use
command javah -jni RmiElizaServer to create the header file “RmiElizaServer.h” which
contains automatically generated function templates. Our C program “rmiEliza.cpp” is based on
this header file. The native C functions involve converting a C char array terminated by ‘\0’, into
a Java String Object jstring. Of course, we have to call the functions defined in “eliza.cpp” to
generate char arrays required by the RMI server. Refer to the program appendix for more details.
4. “makefile”: makefile for the RMI server programs
- 10 -

Generate the skeleton and stub class files. In reality, the remote method invocation starts asa
local method invocation on a stub. The stub packages any parameters and sends the request
to the skeleton for the remote object. The skeleton unpacks the parameters that were sent
and dispatches the invocation to the target method. Command
/usr/opt/java122/bin/ rmic RmiElizaServer
is used to generate class RmiElizaServer _Skel.class and RmiElizaServer_Stub.class
which are the objects doing the real job. When a client makes a request for a reference to
the remote object, it is the stub for the remote object that is sent to the client.

Generate the dynamic library librmiEliza.so. We have to put all the native C code to a
dynamic library for the reference of the Java RMI Server programs. Command
cxx -shared -I/usr/opt/java122/include -I/usr/opt/java122/include/alpha -I.
-pthread rmiEliza.cpp -o librmiEliza.so
is used to generate librmiEliza.so.
5. “RmiElizaClient.java”: the Eliza RMI Client definition
import java.rmi.*;

Declare two native methods which will be defined in a dynamic library
librmiElizaClient.so. Methods getInput() and getReqStr() eventually call the
functions void getInput(char *buf) and char *clienteliza(char
*buf) respectively in “eliza.cpp”.
public native String getInput();
public native String getReqStr(String request);

Load the library librmiElizaClient.so
static { System.loadLibrary("rmiElizaClient");

}
Obtain a reference to the remote object using lookup.
String host = "wedge.tcs.auckland.ac.nz:1099";
IRmiEliza remoteElz = (iRmiEliza)Naming.lookup("rmi://" + host +
"/ServerEliza");

Perform continuous conversation with server.
o First the client does a remote method call to get the prompt string from server.
String init = remoteElz.getInitStr();
o Then it receives the first query string from user and passes it as a parameter to the
remote method call. This remote method will return the first response from server.
RmiElizaClient me=new RmiElizaClient();
String resp = remoteElz.getReplytStr( me.getInput() );
o The client calls the native method String getReqStr(String reqt) to
generate the next query string, performs a remote method call to get server response,
and uses this response to generate the next query again.
for(int i=0; i< number_of_times; i++) {
resp = me.getReqStr(resp);
System.out.println("Q> "+resp);
resp = remoteElz.getReplytStr( resp );
System.out.println( "A> "+resp );
}
6. “rmiElizaClient.cpp”: C program defining the native methods in “RmiElizaClient.java”
- 11 -
This is similar to the “rmiElizaServer.cpp” program. Refer to the program appendix for more
details.
7. “clientmakefile”: C program defining the native methods in “RmiElizaClient.java”
The only thing different from the server “makefile” is that it does not have to generate the skeleton
and stub class by using command rmic. Refer to the program appendix for more details.
Now we have finished this chapter. To sum up, the three communication media: C socket,
Message Queue and Java RMI have been successfully established. Our eliza program acts as the
source of somewhat intelligent strings for client/server conversation. In the next chapter, we will
look at the performance testing of the three distributed communication media.
- 12 -
§3 Testing and Comparison of Performances
In this chapter, we will respectively test the performances of the three distributed communication
media. The testing part has nothing to do with our previous eliza.cpp program. But we can modify
our three groups of different client/server programs for our testing purpose.
Apart from the three different groups of programs, we have to pay attention to the timing of data
communication. It is not the entire time elapsed since the communication starts till ends that can
represent the real communication time. The server on which our programs are running could be
dealing with some other processes. A Timer class handles the real timing. All we need to do in our
program is to make a Timer object and call its member procedures or functions. Calling procedure
start() on the object will start the timer, calling procedure stop() will stop the timer and
function elapsedTime() will return the double value in seconds which represents the
accurate time for the communication. The Timer class is defined in file “Timer.h” and
implemented in file “Timer.cc”. As our reference, these two files are included in the program
appendix.
3.1 Testing performance of C Socket Client/Server
Since today’s computers are really fast, we have to choose a fairly large set of data to transfer
between client and server. From one or two tries I find out that it takes approximately 1ms to
transfer 1 MB data from socket client to server and server back to client again. So the data size for
all our testing will range from 1 MB to 20MB. Our testing model is based on the Ping-Pong
protocol which is illustrated as the following.
PING: Client sends BUFFERSIZE bytes to Server
Server
Client
Many times
PONG: Server sends the same data back to Client
Figure 5. PING--PONG client/server communication model for performance testing
Let’s now briefly look at how this is done.
1. Set up the server socket testing program
By modifying our “sserver.cpp” socket program, we can easily obtain our testing server program
“testSockServer.cpp”.
#define BUFFERSIZE 1000

Fill in the server properties and start the server program as before

Declare a buffer which will hold all data received and to be sent to client. BUFFERSIZE does
not have to big enough to hold one packet of data since the packet size may be very large.
char buf[BUFFERSIZE];
- 13 -

Then all the server does is to receiving and sending data continuously. Notice that we do not
apply the timing in the server program, since the client is in control on when to start and
stop the communication.
for ( ; ; ) {
int nbytes = recv(nsockfd, buf, sizeof(buf), 0);
if ( nbytes == -1 || nbytes == 0 ) break;
buf[nbytes]='\0';
send(nsockfd, buf, strlen(buf), 0);
}
2. Set up the client socket testing program
Similarly, the client socket program can be obtained by modifying our “sclient.cpp” socket
program.
#include "Timer.cc"
#define BUFFERSIZE 1000

Here we also have to declare and initialize a data buffer which will hold the data to be sent
and received.
char txBuf[BUFFERSIZE];
for(int i = 0; i < BUFFERSIZE; i++ ) {
txBuf[i] = 'd';
}
txBuf[i]='\0';

Declare a Timer object and start the timer
Timer timer;
Timer.start();

Start the continuous communication. Our data transmission has 21 cycles. Each cycle
involves sending and receiving #MB data for both server and client. Each further cycle will
transmit 1MB more than the previous one. This is accomplished by increasing 1000 times
of transmitting 1KB each cycle. Record the number of bytes transmitted and the time spent
on each cycle. Refer to the file “testSockClient.cpp” in the appendix for more details.
3. Socket testing result
The result of the performance has something to do with the situation when we run the testing server
and client programs on the same or different servers. We choose to run the programs on “m3r” or
“wedge” server. There are three cases to be tested.



Client and server both run on “wedge”. This is to test the socket performance on the same
machine.
Client runs on “wedge” and server runs on “m3r”. This is to test the socket performance on
two different machines.
Client and server both run on “m3r”. This is to compare the speed of these two different
machines.
Following are the testing results for each of the above cases.
- 14 -

Table 1 and Figure 6 show the testing result of four different situations when we have both
client and server running on “wedge” terminals.
Time (sec)
Data (MB)
buffer size
server = 10000
client = 1000
buffer size
server = client
= 1000
buffer size
server = 1000
client = 10000
buffer size
server = client
= 10000
0
0.000976
0.002928
0.006832
0.008784
0.01464
0.023424
0.033184
0.03904
0.052704
0.060512
0.076128
0.090768
0.104432
0.119072
0.138592
0.15616
0.16592
0.187392
0.208864
0.225456
0
0.002928
0.007808
0.00976
0.013664
0.018544
0.028304
0.03416
0.04392
0.0488
0.060512
0.074176
0.08296
0.0976
0.111264
0.121024
0.145424
0.166896
0.18544
0.210816
0.236192
0
0.001952
0.002928
0.00488
0.011712
0.015616
0.01952
0.023424
0.0244
0.035136
0.04392
0.054656
0.066368
0.08296
0.101504
0.122
0.139568
0.162992
0.171776
0.187392
0.20496
0
0
0.002928
0.005856
0.007808
0.013664
0.017568
0.022448
0.031232
0.037088
0.0488
0.05856
0.066368
0.07808
0.095648
0.103456
0.114192
0.12688
0.14152
0.153232
0.17568
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Table 1. Testing result for socket client/server running on “wedge”
Test Socket Performance on "wedge" Server (Single Machine)
0.25
buffer size: (server, client) = { (1KB,1KB),(10KB,10KB),(10KB,1KB),(1KB,10KB) }
time (sec)
0.2
0.15
0.1
0.05
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
data size (MB)
Figure 6. Testing result for socket client/server running on “wedge”
- 15 -

Table 2 and Figure 7 show the result of four different situations when we have server
running on a “m3r” terminal, client running on a “wedge” terminal
Time (sec)
Data (MB)
buffer size
server = 10000
client = 1000
buffer size
server = client
= 1000
buffer size
server = 1000
client = 10000
buffer size
server = client
= 10000
0
0.000976
0.001952
0.00488
0.008784
0.016592
0.0244
0.031232
0.038064
0.050752
0.061488
0.0732
0.085888
0.0976
0.116144
0.134688
0.150304
0.162992
0.177632
0.196176
0.215696
0
0.00488
0.006832
0.008784
0.010736
0.017568
0.023424
0.02928
0.035136
0.040016
0.050752
0.065392
0.084912
0.0976
0.11712
0.135664
0.15128
0.169824
0.186416
0.211792
0.231312
0
0.000976
0.002928
0.003904
0.005856
0.012688
0.018544
0.02928
0.030256
0.038064
0.054656
0.062464
0.070272
0.081008
0.105408
0.116144
0.132736
0.147376
0.169824
0.192272
0.20496
0
0.002928
0.00488
0.008784
0.016592
0.020496
0.023424
0.02928
0.036112
0.044896
0.050752
0.065392
0.079056
0.08784
0.099552
0.119072
0.13176
0.14152
0.158112
0.174704
0.194224
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Table 2. Testing result for socket client/server on “wedge” and “m3r”
Test Socket Performance on Diffferent Servers (Two Machines)
0.25
buffer size: (server, client) = { (1KB,1KB),(10KB,10KB),(10KB,1KB),(1KB,10KB) }
time (sec)
0.2
0.15
0.1
0.05
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
data size (MB)
Figure 7. Testing result for socket client/server on “wedge” and “m3r”
- 16 -
Table 3 and Figure 8 show the testing result of four different situations when we have both
client and server running on “m3r” terminals.
Time (sec)
Data (MB)
buffer size
server = 10000
client = 1000
buffer size
server = client
= 1000
buffer size
server = 1000
client = 10000
buffer size
server = client
= 10000
0
0.000976
0.00488
0.007808
0.013664
0.023424
0.040992
0.05368
0.06344
0.077104
0.090768
0.113216
0.129808
0.150304
0.167872
0.194224
0.215696
0.243024
0.276208
0.298656
0.340624
0
0.000976
0.007808
0.015616
0.026352
0.042944
0.05368
0.075152
0.103456
0.123952
0.15616
0.198128
0.235216
0.276208
0.309392
0.333792
0.384544
0.4148
0.475312
0.528992
0.599264
0
0.001952
0.003904
0.007808
0.011712
0.01952
0.0244
0.033184
0.041968
0.052704
0.065392
0.08296
0.101504
0.121024
0.135664
0.154208
0.17568
0.196176
0.222528
0.243024
0.269376
0
0.001952
0.00488
0.007808
0.017568
0.026352
0.033184
0.035136
0.047824
0.055632
0.062464
0.076128
0.095648
0.109312
0.120048
0.142496
0.159088
0.18056
0.201056
0.22936
0.247904
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Table
3. Testing result for socket client/server running on “m3r”
Test Socket Performance on "m3r" Server (Single Machine)
0.7
buffer size: (server, client) = { (1KB,1KB),(10KB,10KB),(10KB,1KB),(1KB,10KB) }
0.6
0.5
time (sec)

0.4
0.3
0.2
0.1
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
data size (MB)
Figure 8. Testing result for socket client/server running on “m3r”
- 17 -

There are a few points we can conclude from the above results.
o Firstly, client/server communication using C socket is very fast. On average, it takes
about 0.2 to 0.3 second to transfer 20MB data around.
o Generally to say, when receiving and sending the char array with BUFFERSIZE =
10000, we get better performance than using the char array with BUFFERSIZE =
1000. When we use larger buffer for client and server, we will run fewer loops to
send a certain large amount of data around. Also, because the socket uses packets for
communication, the packets can be very large but our buffer size maybe too small.
So we need to wait till several buffer sizes of data arriving to make one packet.
Therefore, the buffer size affects the communication speed.
o Comparing Table 1 with Table 3, when we run C socket client/server on a single
machine, we can see that “m3r” server is slower than “wedge” server.
o The resulting data can vary from time to time. When the network is busy, it may
takes longer to send a certain amount of data around.
3.2 Testing performance of C Message Queue
There are several problems to be mentioned for testing message queue performance. First, how do
we choose the maximum size of a message? We cannot make it too small, since it will add too
much overhead when sending and receiving too many times for 1MB data. But it cannot be too big,
since the underlying message queue definition only accepts maximum 1KB for one message. So we
choose to use 1KB as the message size.
Second, how many messages can be contained in one message queue to obtain best performance?
We can try this out by setting it to different numbers. The experiment shows that we choose
mq_attr.mq_maxmsg = 10 to get the best performance.
And the third, do we send messages continuously to one queue and receive continuously from
another queue, or just send to and receive from the same queue? By experiment, we choose to use
two queues as before for the server and client. As one queue is too easy to block the program.
As message queue does only interprocess communications, we can only choose to run our namely
client queue and server queue on the same server terminals. So we only compare with socket for
one situation, ie. server and client both on the “wedge” server terminals.
We still want to make data sizes range from 1MB to 20 MB to compare with socket. One cycle will
include client sending #MB to queue#1, server receiving #MB from queue#1, server sending #MB
to queue#2 and client receiving #MB from queue#2, same as the socket testing. The program
should stop when the client has received the last byte from the (client) queue. The way to make
these data sizes is the same as the testing socket programs in the last section.
1. Set up the server queue testing program
By modifying our “mqserver.cpp” program, we can easily obtain our testing server program
“testMqServer.cpp”. As in “testSockServer.cpp”, we have to declare a buffer (char array) with
size 1KB, and use it to hold data to be received from and sent to the message queues. The general
idea is similar to the testing socket server. Refer to the program appendix for more details.
- 18 -
What is the data structure of message queue? Does it affect the result? We will briefly look at how
this is done.
2. Set up the client queue testing program
By modifying our “mqclient.cpp” program, we can easily obtain our testing client program
“testMqClient.cpp”. As in testing socket client program, we also have 21 cycles, dealing with data
size from 0.001MB to 20.001MB (0MB to 20MB for short). Also, the data size and time spent on
the transmission are recorded. For more details of the program, refer to the program appendix.
3. Message queue testing result
The message queue testing result is obtained by running the “testMqServer.cpp” and
“testMqClient.cpp” programs on “wedge” server terminals. It is clear that the performance of
message queue is not as good as socket communication. The main reason might be that the data
structure of the message queue is a list or linked list. Too many insertions, shifts, deletions and also
blockings occurring during this large amout of data transmission.
size (MB)
time (sec)
size (MB)
time (sec)
0
0
11
4.38126
1
0.020496
12
5.52904
2
0.072224
13
6.87982
3
0.166896
14
8.43264
4
0.325008
15
10.2187
5
0.562176
16
12.2527
6
0.901824
17
14.5336
7
1.33419
18
17.0878
8
1.87197
19
19.9377
9
2.54638
20
23.1351
10
3.38477
Table 4. Testing result for message queue on “wedge” terminals
Test Message Queue Performance on "wedge" server
25
time (sec)
20
15
10
5
0
1
2
3
4
5
6
7
8
9
10 11 12 13
data size (MB)
14
15
16
17
18
19
20
21
Figure 9. Testing result for message queue on “wedge” terminals
- 19 -
Again even the result seems far too bad, the attributes we use for message queues are the best
choices, ie. mq_attr.mq_maxmsg = 10; mq_attr.mq_msgsize = 1000;. Next will look at the
performance testing of Java RMI client/server.
3.3 Testing performance of Java RMI Client/Server
There are several things to mention about the testing of Java RMI client/server.
Firstly, we need not use the Java Native Interface because we only deal with Java String here.
Secondly, we will have a new remote interface called iRmiTest.java to be implemented by our
testRMIServer.java testing program.
Next, we have to choose an appropriate length for the data (a Java String) transmitted by calling a
remote method. We could have a Java String with a length of 1MB. But with Java RMI, this is not
realistic. The maximum length of the parameter for the remote method call is 65535 bytes. That is
216 – 1. Otherwise, a java.rmi.MarshalException will occur. For our testing, we want to choose
1MB as one unit for one cycle. “62500” is the closest and best number to be selected and this will
make the increment of 16 times for every next cycle.
The way of data transmission is totally different from that of socket and message queue. With Java
RMI, the server does not actually receive and send messages. The data is passed to the remote
method as a parameter and stored to a variable. How long does it take to pass 62500 bytes of data
as a parameter to the remote method? On the other hand, the client receives data by making a
remote mothod invocation which will return the data. Then the client stores the data into a variable.
These definitely incur very much overhead to the timing.
Again our testing involves 21 cycles, transmitting data sizes from 0MB to 20MB. First the client
calls remoteObject.send(String data) which will pass the data to the remote object on server
side. Then the client calls String remoteObject.receive() which returns the data to client.
Depending on size of the data to be transmitted, this will be performed many times as needed. Now
let’s briefly look at our Java RMI testing programs.
1. The remote interface “iRmiTest.java”
import java.rmi.Remote;

Subclass of interface java.rmi.Remote to be a remote object.
public interface iRmiTest extends Remote {. . .}

Declare two methods which will be concreted by the implemented class. Both throw
RemoteException.
public void receive(String clientMsg) throws RemoteException;
public String send() throws RemoteException;
2. Set up the Java RMI Server Testing Program
By modifying our RmiElizaServer.java program, we can easily obtain our testRMIServer.java
program.

Implement the iRmiTest.java interface to be a remote object.
- 20 -
public class testRMIServer extends UnicastRemoteObject implements
iRmiTest {

Declare a String variable which will be used to store the received data.
String msg=new String();

The receive(String clientMsg) remote method will be called by client side to store
data to the variable msg.
public void receive(String clientMsg) throws RemoteException {
msg = clientMsg;
}

The String send() remote method will be called by client to return the data to client.
public String send() throws RemoteException {
return msg;
}

Registry and binding. Same as RmiElizaServer.java.
public static void main(String args[]) { . . . }
3. Set up the Java RMI Client Testing Program
We can set up our testRMIClient.java by modifying our RmiElizaClient.java program.

Declare and initialise a String variable msg to store data.
static final int MSGSIZE=62500; // string length
char temp[]=new char[MSGSIZE];
for(int i=0; i< MSGSIZE; i++){
temp[i] = 'd';
}
String msg = new String(temp);

The send(String clientMsg) local method is used to call remote method to send data to
server.
public void send(String clientMsg){
try{
server.receive(clientMsg);
} catch (Exception e) { e.printStackTrace(); }
}

The String receive() local method is used to call remote method to obtain data from
server. On exception, program terminates.
public String receive(){
try{
return server.send();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
return null;
}

The connect() local method is used to connect to server.
public void connect(){
try{
server = (iRmiEliza)Naming.lookup("rmi://"+host+"/ServerEliza");
} catch (Exception e) { e.printStackTrace(); }
- 21 -
}

Continuously send and receive data by calling remote methods on the remote object until all
the required sizes (0MB to 20MB) of data is transmitted completely. Record time spent for
each cycle using System.CurrentTimeMillis(). Refer to the program appendix for more
details.
public static void main(String args[]) { . . . }
4. Java RMI client/server testing result
As socket testing, we will test the Java RMI on same server terminals as well as on different server
terminals. First, we run RMI server and client programs both on “wedge” server terminals. Then we
run RMI server program on an “m3r” terminal and RMI client on a “wedge” terminal.

The result when we have both client and server running on “wedge” terminals
size (MB)
0
1
2
3
4
5
6
7
8
9
10
time (sec)
size (MB)
11
12
13
14
15
16
17
18
19
20
0.006
0.301
0.581
0.871
1.171
1.435
1.755
2.028
2.329
2.61
2.902
time (sec)
3.216
3.466
3.685
3.98
4.253
4.582
4.857
5.144
5.429
5.713
Table 5. Testing result for Java RMI client/server running on “wedge”
We have chosen the best value for MSGSIZE and from the result we can see that Java RMI communication is much slower than socket but similar to message queue with client /server running on
same server terminals.
Testing RMI on "wedge" Server
6
time (sec)
5
4
3
2
1
0
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15 16 17 18 19 20 21
data size (MB)
Figure 10. Testing result for Java RMI client/server running on “wedge”
- 22 -

The result when server running on a “m3r” terminal and client on a “wedge” terminal
size (MB) time (sec)
0
0.009
1
3.157
2
6.396
3
9.673
4
12.742
5
15.922
6
19.054
7
21.613
8
24.723
9
27.759
10
34.653
size (MB) time (sec)
11
34.434
12
36.956
13
40.279
14
43.097
15
46.274
16
49.33
17
52.573
18
55.39
19
58.522
20
61.41
Table 6. Testing result for Java RMI client/server on “wedge” and “m3r”
Testing Java RMI on "wedge" and "m3r"
70
time (sec)
60
50
40
30
20
10
0
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15 16 17 18 19 20 21
data size (MB)
Figure 11. Testing result for Java RMI client/server on “wedge” and “m3r”
We can see that the Java RMI client/server communication is extraordinarily slower than the socket
when we use different server terminals running client and server programs. Similar result can be
obtained when running Java RMI server on a “wedge” terminal and client on an “m3r” terminal.
3.4 Comparison of Performances
Now we have finished the performance testings for all three communication media. It is time to
evaluate our testing results. Following we will compare them in terms of message transmission
time and overall execution time.
1. Comparison of performances in terms of message transmission time

Client and Server running on same server (“wedge”) terminals. In this case we can compare
all of three communication media
- 23 -
Performance Comparison (C/S on "wedge" Terminals)
25
Message Queue
time (sec)
20
Java RMI
15
10
C socket
5
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
data size (MB)
Figure 12. Performance Comparison (client/server running on “wedge”)
1) Transferring 0MB data. We have used 1KB to represent 0MB data for convenience, and
used 1 byte for Java RMI. The result is very clear that it takes 0.000000 second for socket
to transfer 1KB data around no matter how we distribute socket client and server
terminals. Same result holds for message queue communication. But it takes Java RMI
0.006 second to transfer only one byte of data.
2) Transferring 1MB data from client to server and server back to client again. For socket it
takes 0.000976 second. For message queue it takes 0.020496 second to do the same job.
This is about 20 times slower than socket. For Java RMI, it takes 0.301 second to do the
same job. This is about 14 times slower than message queue and 307 times slower than
socket communication.
3) Transferring 20MB data from client to server and server back to client again. For socket it
takes 0.710528 second. For message queue it takes 23.1351 seconds to do the same job.
This is about 32 times slower than socket. For Java RMI, it takes 5.713 seconds to do the
same job. This is about 3 times faster than message queue and 7 times slower than socket
communication.
4) When data size is 10MB, message queue and Java RMI have similar performances. From
the diagram, we notice that performances of socket and Java RMI decrease linearly with
increasing data size. While performance of message queue decreases squarely with
increasing data size. Time complexity of message queue could be O(n2), while the time
complexity of socket and Java RMI should be close to O(n). Again this is because the
internal data structure of message queue is like a list or linked list. Altering the queue
involves too many data operations which increase overhead. While socket is based on IO
streams, its performance is mainly affected by packet size and network ability. Socket
communication can be extremely fast with today’s networking technology. There might
be several factors affecting the performance of Java RMI communication. But I think the
main factor may be the Java nature – the Java programs run on top of the Java Virtual
Machine (VM), not directly on the the physical machine.

Client and Server running on different server (“wedge” and “m3r”) terminals. Here we can
only compare socket with Java RMI. We will not draw the comparison diagram for them
because socket is clearly much much faster than Java RMI.
- 24 -
1) Transferring 0MB data. It takes 0.000000 second for socket to transfer 1KB data. But it
takes Java RMI 0.009 second to transfer only one byte of data.
2) Transferring 1MB data from client to server and server back to client again. For socket it
takes 0.001952 second. For Java RMI, it takes 3.157 seconds to do the same job. This is
about 1616 times slower than socket communication.
3) Transferring 20MB data from client to server and server back to client again. For socket it
takes 0.388448 second. For Java RMI, it takes 61.41 seconds to do the same job. This is
about 157 times slower than socket communication.
4) It is very clear that the performance of Java RMI is really incomparable with that of C
socket. Again with Java RMI, object running in one Java Virtual Machine (VM) invokes
methods on an object running in another Java VM. This is time consuming.

Overall performance evaluation
1) With respect to processes running on same server terminals, C socket is the fastest communication medium. Message queue is relatively fast when dealing with small bunch of
data (up to 2 or 3MB), and it is only appropriate for interprocess communications on
same server terminals. We cannot make a message queue process running on a “wedge”
terminal communicate with another process running on “m3r”. Java RMI is the slowest in
general for dealing with medium sized data. On the other hand, both socket and Java RMI
have the advantage of communication over the network. In the sense of process distribution, they are more likely to be called distributed media than message queue.
2) With respect to processes running on different server terminals, we can only compare C
socket and Java RMI for our testing. C socket is still the fastest communication medium.
Java RMI is far too slow. They both provide network communication ability. One advantage Java RMI has is that it is Object Oriented and supports distributed object application.
But this is at the cost of its slow performance.
2. Comparison of performances in terms of overall execution time
This is based on the program execution time when I run the three types of testing programs. I find
that the amount of time used is different from time to time when running a same program on a same
server terminal. Also the testing result may be different when using different server terminals.
There may be many processes running on the server at the same time. But from all the program
executions, I conclude that C socket uses the least time upon execution. It has no blocking at all so
we can obtain the result very quickly. Message queue testing is really time consuming. It may
involve too many queue reading and writing blockings and the program is running very slowly. The
execution time of Java RMI program is definitely worse than socket. But it advances faster than
message queue when we have the Java programs running on same server terminals. For programs
running on different server terminals, Java RMI is the worst.
3.5 Comparison of learning and usages
Following we will discuss how easy or difficult to learn and implement each communication
medium based on our three types of Eliza client/server programs. This involves program complexity, debugging, compiling and executing.
Now, let’s have a look at the program complexity. First of all we have to include or import the
proper header files or classes for each type of programs.
- 25 -
1) With C socket, we have to set up a server with an IP address and a port when using TCP/IP
protocol. We have to specify the socket properties to make a server socket file descriptor. Then we
bind the file descriptor to the server address and start listening on the file descriptor, accepting
connections from clients. Then we can do the reading and writing. Similarly with the client socket,
we also have to do a few things before making a connection to server.
2) With message queue IPC, we just need to specify the queue attributes and on opening a queue,
just specify the parameters to get a queue file descriptor. Then we can do the reading and writing
on the queue. All the message queue programs are set up in the same way.
3) With Java RMI, we have to set up the remote object as the server side. This involves a remote
interface and an implementation class of the interface. After having done the registry and Naming
binding of the remote object, we almost finish the coding of the server side. Then we write a client
class. First we specify the RMI host URL. After making a remote object reference by a Naming
lookup, we are ready to make remote method invocations on the remote object.
4) Generally to say, in my own opinion, the message queue programs are the simplest. It has nothing to do with hostIP and does not depend on the network. C socket client/server can contain only
two standalone C programs. In my opinion it is less complicated than Java RMI which involves a
few Java classes using JNI connecting to the C Eliza program.
Next, let’s see the program debugging, compiling and executing. I have found C socket and message queue is easier to debug than Java RMI because Java RMI Eliza programs are more complicated. Also, C socket is the easiest to compile and execute, followed by message queue. So in terms
of usage, we have the following order from best to worst.
C socket (better than) C message queue (better than) Java RMI
Note, however, that different people may have different views on the ease of learning and using
them.
- 26 -
§4 Program Modularisation
So far we have almost finished the major tasks of the project. As program modularisation is very
important to professional programmers, this chapter is endeavoured to modularise our existing C
programs. First we will create two static libraries respectively for C socket and message queue.
Then we will have several header files to refer to the functions defined in the libraries. By including
the header files in our program, we can use all the data and functions defined in the static libraries.
4.1 Creating Libraries for C Socket Programs
The steps of creating a static library are as the following.




Write the C/C++ source code.
Compile the source to a .o object file using command: cxx –c CxxSource.cpp
Create the archive library libxxx.a using command: ar –q libxxx.a CxxSource.o others.o
Convert the archive to a random library using command: ranlib libxxx.a
Now we can use a header file to link to the library. Of course, our program has to include the
header file to use the data and functions defined in the library. When compiling our program, we
have to use -L. -lxxx as the option, where ”.” refers to the current directory. Next let’s briefly
look at how this is done.
1. Write C source file “GLlibSocket.cpp” for the socket library
“GL” in the file name represents my initial. It is just for file naming convenience. In this source file
we still have to include any header files that all the standard functions come from.
#include
#include
#include
#include
#include
<netinet/in.h>
<iostream.h>
<arpa/inet.h>
<netdb.h>
<stdlib.h>

Wrap all necessary data and functions to make the GLServerSocket. From now on our C
program can setup the server socket easily by a call to function GLServerSocket(int
portNumber).

Server accepts clients’ connection by GLAccept. This function returns the file descriptor
dealing with one client connection.

To make the code integrated, functions GLReceive and GLSend are created. They are for
both client and server. Also, error handling is wrapped in.

Wrap all necessary data and functions to make the GLConnect. This makes the code of
client side much tidier. The client socket is setup by just calling this function, providing you
know what are the hostname and the port you want to connect to.
Now the source for socket library is completed. Refer to the program appendix for more details.
- 27 -
2. Rewrite our C socket client/server programs using the library
We have included the “eliza.cpp” program in our archive library libsock.a by executing the
following commands: ar –q libsock.a GLlibSocket.o eliza.o . Two header files “sock.h” and
“eliza.h” are introduced for our C source files. All the necessary standard header files are included.
By using “extern” keyword, the function templates will be linked to the library. In the new server
program “sockserver.cpp” and new client program “sockclient.cpp” we need to write #include
"sock.h" and #include "eliza.h" to use functions defined in libsock.a.
The server and client programs turn out to be much simpler. Now to compile the server and client
programs, we use commands
cxx sockserver.cpp -o server -L. -lsock and cxx sockclient.cpp -o client -L. –lsock
4.2 Creating Libraries for Message Queue Programs
Similarly, we can write our new version of message queue programs by creating a static library for
message queue.
1. Write C source file “GLlibMq.cpp” for the socket library
#include
#include
#include
#include


<mqueue.h>
<iostream.h>
<sys/fcntl.h> // for "O_WRONLY", "O_CREAT", "O_RDONLY"
<stdlib.h> //for exit(0)#include <netinet/in.h>
Wrap all necessary data and functions to make the GLMqCreate. Two message queues are
created after calling this procedure.
Also for code integration, procedure GLMqSend and function GLMqReceive are given. So
are procedures GLMqClose and GLMqUnlink.
Now the source for message queue library is completed. Refer to the program appendix for more
details.
2. Rewrite our C message queue programs using the library
Similar to socket library, we have included the “eliza.cpp” program in our archive library libmq.a
by executing the following commands: ar –q libmq.a GLlibMq.o eliza.o . Two header files
“mq.h” and “eliza.h” are introduced for our C source files. All the necessary standard header files
are included. Also by using “extern” keyword, the function templates will be linked to the library.
In the new server program “mqueueserver.cpp” and new client program “mqueueclient.cpp” we
need to write #include "mq.h" and #include "eliza.h" to use functions defined in libmq.a.
The two new version message queue files are included in the program appendix. Now to compile
the namely server and client programs, we use commands
cxx mqueueserver.cpp -o mqserver -L. -lmq -lrt and
cxx mqueueclient.cpp -o mqclient -L. -lmq -lrt
- 28 -
This chapter turned out to be a short one. But this does not imply that I have spent little time and
made little effort to do it. Actually it took me a long time to understand how the header files are
used, what a library is and what should be put into the library, how our programs link to the library
etc. It was really not so easy as it seems to be. I believe that understanding these concepts will be
very helpful to my future study.
- 29 -
§5 Program Integration
Now we have all the standalone Eliza client/server programs. The next thing to do is having control
on running each type of the client/server programs from a Graphical User Interface (GUI). In this
chapter, the three-layer communication model (shown in Figure 1) is implemented using C/C++,
Java RMI and Java. As stated before, the GUI is implemented by using Java.
5.1 Writing the General Interface to Three Types of Eliza programs
The general interface which will be called Controller is introduced as a bridge for the GUI and
three types of Eliza client/server programs. It controls the starting and stopping of the inner layer
communications. Considering the GUI may not be local to the Controller, we use a socket connection to deal with user requests. This is the outer layer. To extract the exchanging messages
between Eliza client/server, the Controller also serves as a socket server accepting socket connections from Eliza client (which is also a socket client), getting messages from it. This is the middle
layer.
In order to make the Controller program simple, we will make use of the Object-Oriented feature
of C++ language. The socket client and server, message queue “client” and “server” become C++
objects. We start processes by calling procedures on the objects. Then we can use UNIX sigsend
system calls to terminate a process.
We use UNIX execl system calls to start the Java RMI Eliza client/server programs. This needs a
prerequisite that the RMI programs must be ready to run under a specific directory.
1. Define C++ classes inside header files “sock.h” and “mq.h”


Define socket server and client classes. We will make use of the existing file “sock.h” to
hold our socket objects definitions.
o class sockClass is the super class for serverClass and clientClass. It has two
data members and it defines a few public functions or procedures that will be used
or concreted in the subclasses.
o class serverClass is the server object definition. The constructor and the run()
member procedure are two definitions to be used in the Controller program.
o class clientClass is the client object definition. As serverClass, the constructor
and the run() member procedure are two definitions to be used in the Controller
program. Refer to file “sock.h” for more details.
Define message queue “server” and “client” classes. We will make use of the existing file
“mq.h” to hold our message queue objects definitions.
o class mqServerClass is the message queue server object definition. It only has
one member procedure void run() in it which will be used in the Controller
program.
o class mqClientClass is the client object definition. As mqServerClass, the void
run() member procedure contains everything we need for message queues. It is also
a socket client which makes connection to the Controller, receives the user input
coming from GUI client and sends all the exchanging messages to the Controller.
2. The Controller program
- 30 -
The Controller program is fairly complicated. To make it work, it is very important that we are
clear with the sequence of the message flow. The Controller program acts as two socket servers at
the same time and it also has parent and child processes (Eliza client/server programs) running
simultaneously.

Code outline
1)
2)
3)
4)
Set up the GUI server GUI_server and client Eliza server ceserver.
GUI_server starts to accept GUI client connection.
GUI_server starts to receive first request (the media type).
Start the appropriate type of Eliza client/server programs as requested. One child process is
forked to start Eliza server and then another child process is forked to handle Eliza client.
In parent process ceserver accepts Eliza client’s connection.
Every time ceserver receives a message, it will send a confirm message back to Eliza
client. Then the Eliza client can carry on with the next task.
GUI_server forwards each message received from Eliza client to GUI client
When GUI_server receives “END” from Eliza client, the connections to Eliza client and
GUI client are closed.
Use UNIX sigsend system call to terminate two child processes which are the running
Eliza client and server programs. The program finishes the current loop and gotos step 3)
again to get the next Eliza type request from GUI client.
5)
6)
7)
8)
9)

Deal with exceptions
1) If cannot create any server socket, program terminates by exit(1).
2) On error receiving from and sending to Eliza client or GUI client, both connections are
closed. Continue with the step 2) above.
3) When performing a Java RMI communication, if execl UNIX system calls fail, terminate
the child processes and both connections. Continue with the step 2) above.
4) If error occurs in the underlying Eliza processes, do the same as above.

Several comments on the Controller program
1) A message char array received from GUI client ends with a character “carriage return”, a
“line feed” and lastly the ‘\0’ array terminator. We have to take this into account when
using the standard C funtion strcmp to compare two char arrays.
2) A '\n' character must be added right before ‘\0’ to each char array for Java GUI client to
read the message using BufferedReader.readLine(). If the char array is received from
Java RMI client, who already appends a ‘\n’ at the end of each message, adding ‘\n’ is
not needed.
3) The first message sent to GUI client is the prompt string from Eliza server. It contains some
‘\n’ characters. We have to replace them to allow GUI client to receive the whole string
using one BufferedReader.readLine(). So all the ‘\n’ characters are replaced by a
specific character “~” except for the one at the end. On receiving the string, the GUI client
will recover it to its original pattern.
4) Here the sigsend UNIX system call is used to send a SIGKILL signal to the two child
processes. The Eliza client and server programs will be terminated after this.
5) When the child processes end, the socket ports may be still unavailable for next session.
- 31 -
3. The underlying Eliza client and server programs

Three types of Eliza server programs. We have C socket, C message queue and Java RMI
three Eliza server programs. Each server program does the similar job. For C socket and
message queue, we have two server classes, sockServerClass and mqServerClass, Each
containing a member procedure void run() which is responsible to complete all the Eliza
sever tasks. For Java RMI, we just use the original program “RmiElizaServer.java”. The
program outline of these Eliza server programs are as the following.
1)
2)
3)
4)
5)
Call char *initDialog() of “eliza.cpp” to get the prompt string.
Send the prompt string to client.
Receive from client the user request (First request is a string from the keyboard entry).
Call char *eliza(char *buf) of “eliza.cpp” to generate the response.
Send the response to client. Loop back step 3).

Three types of Eliza client programs. Similarly, For C socket and message queue, we have
two client classes, sockClientClass and mqClientClass, Each containing a member procedure void run() which is responsible to complete all the Eliza client tasks. For Java
RMI, we use “RmiElizaClient2.java”, a modified version of “RmiElizaClient.java”. Here
we create a Java Socket as a client to connect to the ceserver in Controller. The program
outline of these Eliza client programs are as the following.
Create the socket client and connect to ceserver.
Receive the prompt string from Eliza server.
Send the prompt to ceserver and receive the comfirming string.
Receive the user request from ceserver and send it to Eliza server.
Receive the response from Eliza server, send it to ceserver and receive the comfirming
string from ceserver.
6) Call char *clienteliza(char *buf) of “eliza.cpp” to generate the request.
7) Send the request to both Eliza server and ceserver, receiving the comfirming string.
8) Loop back step 5). Only a specific number of loops performed for each session.
1)
2)
3)
4)
5)
4. The GUI Client program in Java
The GUI client is a relatively simple Java application mainly implemented using Java AWT. We
use java.net.Socket class to implement the client socket. The program consists of two classes
“ElizaClient.java” and “ElizaClientGUI.java”. There are several controls such as TextField,
Choice, TextArea and Button in the application. A Choice control is to let the user select from three
different Eliza Client/Server Types.Two TextAreas are used to display the Eliza server and client
messages coming from the Controller. Three buttons are used to connect to the server Controller,
start the Eliza client/server programs and disconnect from the Controller. A status TextField is
used to display any feedbacks or messages to the user. Refer to the program appendix for more
details.
5.2 Running the Integrated Eliza Programs
Now we have finished the design of the integrated Eliza programs. The source files includes mainly
the following three parts.
- 32 -



The general interface which is the C source file “Controller.cpp”
The Three different distributed Eliza programs. These are C socket client/server classes in
file “sock.h”, C message queue classes in file “mq.h”, and Java RMI client/server programs
in files “iRmiEliza.java”, “rmiElizaServer.cpp”, “RmiElizaServer.java”,
“RmiElizaClient2.java”, “rmiElizaClient.cpp”. File “eliza.h” or “eliza.cpp” serves as a
common source for the three types of Eliza programs.
The GUI client in Java, which includes files “ElizaClient.java” and
“ElizaClientGUI.java”.
Let’s look at running program “Controller.cpp”. To use object sockClientClass for creating a
socket client in the void run() procedure of the mqClientClass in header file “mq.h”, we have
to include “sock.h” in “mq.h”. For “Controller.cpp”, we now only need to include file “mq.h” to
use the four classes sockClientClass, sockServerClass, mqServerClass and mqClientClass.
When compiling, we have to link to the two static libraries “libsock.a” and “libmq.a” which also
contains the “eliza.cpp” definition. We still have to link to the message queue definition library
“librt.a”. Header file “eliza.h” is already included in the above two header files. Therefore, the
command for compiling the file “Controller.cpp” under “wedge” UNIX server is as the following.
cxx Controller.cpp -o ./con -L. -lsock -lmq -lrt
To run, just type ./con. Now the GUI server (GUI_server) and client eliza server (ceserver) are
running, waiting for connections from their own clients. Next we use Java to start up the GUI client
application. The GUI server IP address and port are already specified in two TextFields. We just
need to select the eliza client/server type (the default is SOCKET) from the Choice control. When
we click the “Connect” button, the connection to the GUI server is set up. Then click “Start” button
to tell the “Controller” to start up the required type of eliza program. The session is set to generate
ten question and answer messages each for the eliza client and server. When a session ends, the
GUI server will wait for the next type of eliza client and server session. If you click “Disconnect”
button in the mid of the session, the “Controller” will close the server socket and terminate the
running Eliza programs, waiting for the next connection from GUI client.
Following are the screen dumps of the Controller program running as a “wedge” termianl through
“telnet” and the Java GUI client running a message queue Eliza “client/server” session.
- 33 -
- 34 -
Conclusion
Finaly, the project is completed. We have successfully accomplished all our aims of the project.
Three types of distributed Eliza programs have been built up using different communication media.
Testing performances has been done successfully, which is our main purpose of the project. The
comparisons of performances and usages tell us which distributed communication medium is more
suitable than another for our particular purpose.
Generally to say, when we do not have to use Distributed Objects, and we want to transmit large
amounts of data remotely (or even locally) and quickly, we use the C socket. If we just want to
perform inter-process communications locally on a server background, and the speed is not so
essential for a small amount of data, we choose to use message queue. If we have to use Distributed
Objects and we want the remote object to complete our complex tasks remotely and return the
result to us, we use Java RMI (in sense of the project only, not exactly the case when considering
many other techniques such as Corba, Dcom, etc).
In my opinion, C socket and message queue is easier to learn and use than Java RMI. The stan-dard
C library has complete data structure and function definitions for our purposes. It is not too hard to
understand how they work. Java RMI is rather difficult to understand with some important concepts
encapsulated in several Java classes, especially the use of skeleton and stub, the meaning of remote
object registry and the use of the remote object on the client side. We have to use JNI if we want
connect to our C (or Assembly etc) native code.
Through doing this project, a strong basis of learning and using the C/C++ language has been
established for my future study. Before, the C/C++ language seemed to me as an untraversable
obstacle due to its complicated operators, pointers operations, data structures, predefined macros
and standard or extended libraries etc. Now I can use pointer data types without much difficulty. I
have a clearer overview of writing C/C++ programs. By doing modularisation of C programs, I
have gained further understanding of creating, linking, and utilizing the libraries in programming
practice.
Through doing this project, I am more accustomed to program in UNIX environment. I have learnt
to use many important UNIX commands and other resources including the standard C libraries and
extensions under UNIX. To me, the UNIX command line user interface is as comfortable as the
Windows GUI. But clearly there are many more powerful tools in UNIX to be explored later in my
life.
The last chapter of this report involves program integration. I have learned to coordinate and
control the execution of several processes. The differences between each programming context
have been resolved to make our C programs and Java programs perform the expected behaviours.
We can see that the project contains two fairly large parts: the programs and the report. Through
doing this project I have gained much experience in many aspects including C/C++ programming,
Java programming and English writing etc. My self-learning ability has been considerably
improved by doing the project, eg. searching for references from libraries, the World Wide Web,
the UNIX system and other media. I have learned much in program design, testing and integration.
Also, I have learned to analyse the experiment processes and results, and develop a well-organized
report.
- 35 -
Acknowledgement
Without the help from Dr S. Manoharan, this project could not have been finished today. During
the initial stage, Dr S. Manoharan guided me to understand the project. He provided me with clear
project instructions. I gradually got to know what was to be accomplished by the project, where to
start and how to implement each part step by step. Whenever having been struggling for hours at
my deadlock, I turned to Dr S. Manoharan for help, he would support me with his strong hands.
With his help, I became more and more confident in C/ C++ programming. He also clearly
explained to me about program modularisation and library concepts and helped me understand the
three-layer comminication model and how to implement it in the right way.
It is hard to estimate how much time I spent on doing this project, because eventually the project
had to be done independently. I got stuck every now and then due to some misunderstandings. I
might spend hours fixing a minor bug. But I have learned a lot from failures. I do not regret spending a lot of time on the project. The important thing is never giving up when facing difficulties.
I appreciate very much all the help from Dr S. Manoharan. It is really an important experience to
me that I have done this project “Distributed Eliza” with Dr S. Manoharan at the University of
Auckland.
References







Digital Unix manpage. http://www.cs.auckland.ac.nz/cgi-bin/duman.cgi
DEC C Language Reference Manual.
http://www.cs.auckland.ac.nz/references/unix/digital/AQTLTBTE/TITLE.HTM
“m3r” UNIX server man pages. m3r.tcs.auckland.ac.nz
The Java Tutorial. http://www.cs.auckland.ac.nz/JavaCache/tutorial/rmi/index.html
UNIX System V system calls. Programmer’s rapid reference / Baird Peterson. Van
Nostrand Reinhold, c1992. USA
C++ Primer. 2nd Editiom / Stanley B. Lippman. Addison-Wesley, c1991. USA
The C++ Programming Language. 2nd Editiom / Bjarne Stroustrup. Addison-Wesley,
c1991. USA
- 36 -
Programs Appendix
/*
File "sserver.cpp": primitive version
To compile: cxx sserver.cpp -o sserver
To execute: ./sserver
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<iostream.h>
<stdlib.h>
<stdio.h>
<netinet/in.h>
<netdb.h>
<sys/socket.h>
<arpa/inet.h>
<unistd.h>
<sys/wait.h>
<strings.h>
"eliza.cpp"
int main(int argc, char *argv[]) {
int sockfd;
struct sockaddr_in server; // server socket properties
const int portNumber = 2888;
pid_t childpid;
int rstatus;
char buf[256];
//Internet domain, type SOCK_STREAM, TCP protocol
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
/* htons: Converts an unsigned short integer from
host-byte order to Internet network-byte order.
*/
server.sin_port = htons(portNumber);
server.sin_addr.s_addr = INADDR_ANY; /* use my IP address */
if(bind(sockfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {
cout << "Cannot bind to port " << portNumber << endl;
return -1;
}
if( listen(sockfd, 20) == -1 ) {
cout << "Cannot listen to port " << portNumber << endl;
return -1;
}
int count=1; // how many client connections
for ( ; ; ) {
int nsockfd;
int addrsLen = sizeof(struct sockaddr_in);
struct sockaddr_in client;
nsockfd = accept(sockfd, (struct sockaddr *)&client, &addrsLen);
if ( nsockfd == -1 ) {
cout << "Cannot accept connection" << endl;
continue;
}
printf("server: got connection from client No.%d: %s\n", count,
inet_ntoa(client.sin_addr));
count++;
- 37 -
int nbytes;
strcpy(buf,initDialog()); // get prompt message from eliza.cpp
send(nsockfd, buf, strlen(buf), 0);
// continuous conversation
while(1) {
nbytes = recv(nsockfd, buf, sizeof(buf) - 1, 0);
if(nbytes <1) break;
buf[nbytes]='\0';
cout << "received: " << buf << endl;
strcpy(buf, eliza(buf)); //generate next response
send(nsockfd, buf, strlen(buf), 0);
}
close(nsockfd);
}
close(sockfd);
return 0;
} //end of file “sserver.cpp”
/*
File "sclient.cpp": primitive version
To compile: cxx sclient.cpp -o sclient
To execute: ./sclient <hostname>
*/
#include
#include
#include
#include
#include
#include
#include
#include
<iostream.h>
<netinet/in.h>
<netdb.h>
<sys/socket.h>
<arpa/inet.h>
<unistd.h>
<strings.h>
"eliza.cpp"
int main(int argc, char *argv[]) {
int sockfd, nbytes;
struct sockaddr_in server;
char *hostname = "localhost";
char *hostIP;
const int portNumber = 2888;
struct hostent *hostentry;
char buf[MAXSIZE]; // MAXSIZE defined in file "eliza.cpp"
int n=0; //times of conversation
if ( argc > 1 ) {
hostname = argv[1];
cout << "connecting to: " << hostname << endl;
}
hostentry = gethostbyname(hostname);
if ( hostentry == 0 ) {
cout << "DNS lookup for " << hostname << " failed" << endl;
return -1;
}
hostIP = inet_ntoa(*((struct in_addr *)hostentry->h_addr));
cout << "hostIP: " << hostIP << endl;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
- 38 -
server.sin_port = htons(portNumber);
server.sin_addr.s_addr = inet_addr(hostIP);
if (connect(sockfd,(struct sockaddr *)&server,sizeof(struct sockaddr))==-1) {
cout << "Connection to " << hostname << " failed" << endl;
return -1;
}
nbytes = recv(sockfd, buf, sizeof(buf)-1, 0);
buf[nbytes] ='\0';
char msg[MAXSIZE];
printf("%s",buf);
getInput(buf); //accept user input
send(sockfd, buf, strlen(buf), 0);
// continuous conversation
for( ; ; ) {
if( (nbytes = recv(sockfd, buf, sizeof(buf)-1, 0) ) == -1) {
cout << "connection to host lost" << endl;
break;
}
buf[nbytes] ='\0';
cout << "A> " << buf << endl;
if(n==20) break;
strcpy(msg, clienteliza(buf));
cout << "Q> " << msg <<endl;
send(sockfd, msg, strlen(msg), 0);
//putchar(7);
sleep(0.5);
n++;
}
close(sockfd);
return 0;
} //end of file "sclient.cpp"
/* File "eliza.cpp": program to provide messages for eliza
client/server.
To compile: cxx -c eliza.cpp
*/
#include <iostream.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define KSIZE 38
#define NUM 9
#define MAXSIZE 256
char *tolowercase(char line[]);
void substring(char line[], int start, int length);
int isrepeat(int newpos[], int foundkeys, int j);
void replace(char line[], char *keys1[], char *keys2[], int pos);
void toappend(char line[], int pos);
int getcatnum(char line[], int side);
- 39 -
int hasstar( char *resp);
void insert(char resp[], char chars[], int pos);
char *getFirstWord(char *line);
int contains(char *ask[], char *word);
void trim(char str[]);
void getInput(char buf[]);
char sub[MAXSIZE];
char appchs[MAXSIZE];
char *init="Hi! I'm Eliza. Please tell me your problem.\n\nType
initial query:\nQ> ";
/*keyword pool to select types of client requests and server
responses*/
char *keywords[KSIZE][NUM]={
{"family", "mother", "father", "sister", "brother", "wife"},
{"friend", "friends", "buddy"},
{"computer", "computers"},
{"can you"},
{"can i"},
{"you are", "you're"},
{"i like", "i am fond of", "i'm fond of", "enjoy"},
. . . . . .
. . . . . .
. . . . . .
{"yes"}, {"no"}, {"nokeyfound"}
};
// response pool used for server
char *response[KSIZE][NUM]={
{"Tell me more about your family.", "How do you get along with your family?",
"Is your family important to you?", "Do you often think about your family?",
"How would you like to change your family?"},
{"Why do you bring up the topic of friends?", "Do your friends worry you?",
"Do your friends pick on you?", "Are you sure you have any friends?",
"Perhaps your love for your friends worries you"},
. . . . . .
. . . . . .
. . . . . .
{"Why not?", "Why no?", "Are you sure?"},
{"Say, do you have any psychological problems?",
"Come, come; elucidate you thoughts.",
"Can you elaborate on that?", "That is quite interesting.",
"You are being short with me."}
};
// questions pool used for client
char *questions[KSIZE][NUM]={
{"My family is important to me.", "I spend little time with them."
"I have one brother.", "I often think about my family.",
"I want to be with my family."},
{"That is good. Carry on.", "I need your explicit answer."},
. . . . . .
. . . . . .
. . . . . .
{"I think I am a good worker.", "Don't you know I am a good student?"},
{"My family makes me worry.", "Hard to learn programming.",
"I don't like to be like that."},
{"I have a psychological problem.", "I am smarter than a computer.",
"I need your help.", "You are very friendly to me.",
"Can I introduce myself?"}
};
- 40 -
/* --- char *initDialog()
* returns the initial dialog prompt for the server to send it to client
*/
char *initDialog(){
return init;
}
/* --- char *eliza(char inputline[])
* server generates a suitable answer to client's request: inputline.
* first find key word from input.
* according to the keyword found respond with a randomly selected response
* from the corresponding response list.
*/
char *eliza(char inputline[]) {
int idx;
int count, items;
int select;
char temp[MAXSIZE];
strcpy(inputline, tolowercase(inputline));
idx = getcatnum(inputline, 0); //serverside is 0
for(count=0; count < NUM; count ++)
if(response[idx][count] == NULL) {
items=count;
break;
}
select = (int)( (rand()/(float)RAND_MAX) *items);
int pos;
if( (pos=hasstar(response[idx][select])) != -1) {
strcpy(temp, response[idx][select]);
insert(temp, appchs, pos);
strcpy(inputline, temp);
}
else
strcpy(inputline, response[idx][select]);
return inputline;
}
/* --- char *clienteliza(char resp[])
* according the response from server, client selects a suitable request to ask
*/
char *clienteliza(char resp[]) {
char *yesno[2]={"Yes. ","No. "};
char *ask[5]={"how","why","what","when","where"};
int idx, count, items;
char reply[MAXSIZE];
strcpy(resp, tolowercase(resp));
strcpy(reply, getFirstWord(resp));
trim(reply);
if(resp[strlen(resp)-1]=='?' && ! contains(ask, reply) ) {
int num=(int)( (rand()/(float)RAND_MAX) *2);
strcpy(reply, yesno[num]);
}
idx = getcatnum(resp, 1);
for(count=0; count < NUM; count ++)
if(questions[idx][count] == NULL) {
items=count;
break;
- 41 -
}
int select = (int)( (rand()/(float)RAND_MAX) *items);
if( ! strcmp(reply, "Yes. ") || ! strcmp(reply, "No. ") )
return strcat(reply, questions[idx][select]);
return questions[idx][select];
}
char *tolowercase(char line[]) {
int i;
char *tmp=new char[MAXSIZE];
for(i=0; i<strlen(line); i++)
if ('A' <= line[i] && line[i] <= 'Z' )
tmp[i]=line[i] + 'a' - 'A';
else tmp[i]=line[i];
tmp[i]='\0';
return tmp;
}
void substring(char line[], int start, int length) {
int i;
int subix=0;
for(i = start; i < start+length; i++)
sub[subix++]=line[i];
sub[subix]='\0';
}
int isrepeat(int newpos[], int foundkeys, int j) {
//check if j is contained in newpos
int i;
for(i=0; i<foundkeys; i++)
if(newpos[i]==j) return i;
return -1;
}
void replace(char line[], char *keys1[], char *keys2[], int pos) {
int i,j;
int newpos[10];
int foundkeys1=0;
int acc=0;
for(i=0;i<NUM;i++){
int kwlen1 = strlen(keys1[i]);
if(kwlen1 > strlen(line)-pos) continue;
for(j=pos; j< strlen(line)-kwlen1; j++) {
substring(line, j, kwlen1);
if( ! strcmp(keys1[i], sub) && ! isalnum(line[j+kwlen1]) &&
! isalnum(line[j-1]) ){
//Exchange catone with cattwo. First delete keys1[i] from line
int count;
for(count=j; count<strlen(line)-kwlen1; count++)
line[count]=line[count+kwlen1];
line[count]='\0';
int kwlen2 = strlen(keys2[i]);
int linelen=strlen(line);
//insert keys2[i] at position j inside line
for(count=linelen; count>=j; count--)
- 42 -
line[count+kwlen2]=line[count];
for(count=j; count<kwlen2+j; count++)
line[count]=keys2[i][count-j];
newpos[foundkeys1 ++] = j;
}
}
}
for(i=0;i<NUM;i++){
int kwlen2 = strlen(keys2[i]);
if(kwlen2 > strlen(line)-pos) continue;
for(j=pos; j< strlen(line)-kwlen2; j++) {
substring(line, j, kwlen2);
if( ! strcmp(keys2[i], sub) && ! isalnum(line[j+kwlen2]) &&
!isalnum(line[j-1]) ){
if( isrepeat(newpos, foundkeys1, j+acc) != -1 ) continue;
//exchange cattwo with catone
//first delete keys2[i] from line
int count;
for(count=j; count<strlen(line)-kwlen2; count++)
line[count]=line[count+kwlen2];
line[count]='\0';
int kwlen1 = strlen(keys1[i]);
int linelen=strlen(line);
//insert keys1[i] at position j inside line
for(count=linelen; count>=j; count--)
line[count+kwlen1]=line[count];
for(count=j; count<kwlen1+j; count++)
line[count]=keys1[i][count-j];
acc+=kwlen2-kwlen1;
}
}
}
}
void toappend(char line[], int pos){
char *catone[NUM] = {"I can", "me", "I can't", "my", "ours", "I am", "I'm",
"I'll", "myself"};
char *cattwo[NUM] = {"you can", "you", "you can't","your","yours", "you are",
"you're", "you'll","yourself"};
int i, ix=0;
/* check if in line we have words from catone, if there are change it to
corresponding words in catone. */
replace(line, catone, cattwo, pos);
for(i = pos; i < strlen(line); i++) {
appchs[ix++]=line[i];
}
if(appchs[ix-1]=='.' || appchs[ix-1]=='!' || appchs[ix-1]=='?') ix--;
appchs[ix]='\0';
}
int getcatnum(char line[], int side) {
int i, j;
int num = KSIZE-1;
int linelen=strlen(line);
for(i=0; i< KSIZE; i++)
- 43 -
for(j=0; j< NUM; j++) {
if( keywords[i][j] != NULL) {
int start = 0;
int kwlen=strlen(keywords[i][j]);
if(kwlen == linelen) {
if( strcmp(keywords[i][j], line ) == 0) {
num=i;
goto end;
}
} else if(kwlen == linelen-1) {
if(! isalnum(line[linelen-1]) )
substring(line, 0, kwlen);
if( ! strcmp(keywords[i][j], sub ) ) {
num=i;
goto end;
}
}else if(kwlen < linelen-1) {
/* check if line contains the kw. If true, num = i, break */
while( (start+kwlen) <= linelen ) {
substring(line, start, kwlen);
if( !strcmp(keywords[i][j], sub ) &&
! isalnum(line[start+kwlen]) && ! isalnum(line[start-1])) {
if( start == 0 || ( start > 0 && (line[start-1] ==' ' ||
line[start-1] =='\t') ) )
if(! side) toappend(line, start+kwlen);
num=i;
goto end;
}
start++;
}
}
}
}
end:
return num;
}
int hasstar( char *resp) {
int len = strlen(resp);
if( resp[len-1] == '*' )
return len-1;
else if( resp[len-2] == '*' )
return len-2;
return -1;
}
void insert(char resp[], char chars[], int pos) {
int num;
int len=strlen(resp);
char ch=resp[len-1];
for(num=0; num<strlen(chars); num++)
resp[num+pos]=chars[num];
if(ch!='\0') {
resp[num+pos]=ch;
resp[num+pos+1]='\0';
}
else resp[num+pos]='\0';
}
- 44 -
char *getFirstWord(char *line){
char temp[20];
int i;
for(i=0; i<20;i++){
if( ! isalnum(line[i]) ) break;
else temp[i]=line[i];
}
temp[i]='\0';
return temp;
}
int contains(char *ask[], char *word){
int i;
for(i=0;i<5;i++)
if(! strcmp(ask[i],word) ) return 1;
return 0;
}
void trim(char str[]){
int i;
for(i=0; i<strlen(str);i++)
if(! isalnum(str[i]) ) break;
str[i]='\0';
}
void getInput(char buf[]){
char c;
int i=0;
aa:
while(1){
scanf("%c",&c);
if(c=='\n') break;
buf[i++]=c;
}
if(i==0) goto aa;
buf[i]='\0';
} // end of file "eliza.cpp"
/*
File "mqserver.cpp": primitive version
To compile: cxx mqserver.cpp -o mqserver -lrt
To execute: ./mqserver
*/
#include
#include
#include
#include
#include
#include
#include
#include
<stdlib.h>
<mqueue.h>
<stdio.h>
<sys/fcntl.h>
<errno.h>
<string.h>
<iostream.h>
"eliza.cpp"
#define MSGSIZE 255
#define NUMTIMES 20
#define PMODE 0666
extern int errno;
int main() {
int i=0;
- 45 -
mqd_t mqfd1, mqfd2;
char msg_buffer[MSGSIZE];
struct mq_attr attr;
int num_bytes_to_send = MSGSIZE ;
ssize_t num_bytes_received = 0;
int priority_of_msg = 1;
printf("Start of mqserver...\n");
/* Fill in attributes for message queue */
attr.mq_maxmsg = 1;
attr.mq_msgsize = MSGSIZE;
attr.mq_flags
= 0;
/* Set the flags for the open of the queue.
* Make it a blocking open on the queue, meaning it will block if
* this process tries to send to the queue and the queue is full.
* (Absence of O_NONBLOCK flag implies that the open is blocking)
*
* Specify O_CREAT so that the file will get created if it does not
* already exist.
*
* Specify O_WRONLY for server side queue since we are only planning to
* write to it.
* Open the server side "smq" queue, and create it for client
* side to receive msgs.
*/
mqfd1 = mq_open("smq", O_WRONLY|O_CREAT, PMODE, &attr);
if (mqfd1 == -1) {
perror("mq_open mqfd1 failure from main");
exit(0);
}
/* Open the client side "cmq" queue, and create it if the client side has
not created it. server side will receive queries from "cmq".
*/
mqfd2 = mq_open("cmq",O_RDONLY|O_CREAT,PMODE,&attr);
if (mqfd2 == -1) {
perror("mq_open mqfd2 failure from main");
exit(0);
}
strcpy(msg_buffer,initDialog());
//send the initial prompt for the client to type in first query
if(mq_send(mqfd1,msg_buffer,num_bytes_to_send,priority_of_msg)== -1) {
perror("mq_send failure on mqfd1");
exit(1);
}
//receive the first query from the client queue
num_bytes_received = mq_receive(mqfd2,msg_buffer,MSGSIZE,0);
/* Perform the continuous sending and receiving */
for (i=0; i<NUMTIMES; i++) {
strcpy(msg_buffer, eliza(msg_buffer));//mimic server, generate reply
if(mq_send(mqfd1,msg_buffer,num_bytes_to_send,priority_of_msg)==-1){
perror("mq_send failure on mqfd1");
exit(1);
}
//receive the next query from the client queue
if(i != NUMTIMES -1 ) { // more to receive
num_bytes_received = mq_receive(mqfd2,msg_buffer,MSGSIZE,0);
if (num_bytes_received == -1) {
perror("mq_receive failure on mqfd2");
- 46 -
exit(1);
}
}
}
/* Done with receiving from the client queue, so close it */
if (mq_close(mqfd2) == -1)
perror("mq_close failure on mqfd2");
/* destroy the client queue */
if (mq_unlink("cmq") == -1)
perror("mq_unlink \"cmq\" failure in mqserver");
printf("\nExiting the mqserver...\n");
return 0;
} //end of file "mqserver.cpp"
/*
File "mqclient.cpp": primitive version
To compile: cxx mqclient.cpp -o mqclient -lrt
To execute: ./mqserver
*/
#include
#include
#include
#include
#include
#include
#include
#include
<stdlib.h>
<mqueue.h>
<stdio.h>
<sys/fcntl.h>
<errno.h>
<string.h>
<iostream.h>
"eliza.cpp"
#define NUMTIMES 20
#define MSGSIZE 255
#define PMODE 0666
extern int errno;
int main() {
int i;
int status = 0;
mqd_t mqfd1, mqfd2;
/* Buffer to receive msg into */
char msg_buffer[MSGSIZE];
struct mq_attr attr;
int num_bytes_to_send=MSGSIZE;
int priority_of_msg = 1;
ssize_t num_bytes_received = 0;
printf("Start of mqclient...\n");
/* Fill in attributes for message queue */
attr.mq_maxmsg = 1;
attr.mq_msgsize = MSGSIZE;
attr.mq_flags
= 0;
/* Set the flags for the open of the queue.
* Specify O_RDONLY for server side queue since we are only
* planning to read from it. Client side queue O_WRONLY|O_CREAT
* Open the server side queue "smq".
*/
mqfd1 = mq_open("smq",O_RDONLY|O_CREAT,PMODE,&attr);
if (mqfd1 == -1) {
- 47 -
perror("mq_open mqfd1 failure from main");
exit(1);
}
/* Open the client side queue "cmq" */
mqfd2 = mq_open("cmq", O_WRONLY|O_CREAT, PMODE, &attr);
if (mqfd2 == -1) {
perror("mq_open mqfd2 failure from main");
exit(1);
}
//receive the initial prompt from "smq"
num_bytes_received = mq_receive(mqfd1, msg_buffer, MSGSIZE, 0);
if (num_bytes_received == -1) {
perror("mq_receive failure on mqfd1");
exit(1);
}
else printf("%s",msg_buffer);
//accept first query string from user
getInput(msg_buffer);
/* Perform the continuous sending and receiving */
for (i=0;i<NUMTIMES;i++) {
//generate the next query
if(i) { //not the first query string from user
strcpy(msg_buffer, clienteliza(msg_buffer));
printf("Q> %s\n",msg_buffer);
}
//send the query to the cmq
status = mq_send(mqfd2, msg_buffer, num_bytes_to_send, priority_of_msg);
if(status == -1) {
perror("mq_send failure on mqfd2");
exit(1);
}
//get the response from "smq"
num_bytes_received = mq_receive(mqfd1, msg_buffer, MSGSIZE, 0);
if (num_bytes_received == -1) {
perror("mq_receive failure on mqfd1");
exit(1);
}else printf("A> %s\n",msg_buffer);
}
/* close the queue */
if (mq_close(mqfd1) == -1)
perror("mq_close failure on mqfd1");
/* destroy the queue */
if (mq_unlink("smq") == -1)
perror("mq_unlink smq failure in mqclient");
printf("Exiting mqclient...\n");
return 0;
}//end of file "mqclient.cpp"
// iRmiEliza.java: The interface declaration for a remote object.
import java.rmi.*;
public interface iRmiEliza extends Remote {
/* Methods concreted in RmiElizaServer.java */
String getInitStr() throws RemoteException;
String getReplytStr(String request) throws RemoteException;
- 48 -
} // end of file “iRmiEliza.java”
/* RmiElizaServer.java: The remote object and server definition
*/
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
public class RmiElizaServer extends UnicastRemoteObject implements iRmiEliza {
String name;
// constructor
public RmiElizaServer(String name) throws RemoteException {
super();
this.name = name;
}
public native String getAnswer(String request) throws RemoteException;
public native String init() throws RemoteException;
static { System.loadLibrary("rmiEliza"); }
/* Implementation of method "getInitStr()" defined in Interface
"iRmiEliza.java"
*/
public String getInitStr() throws RemoteException {
return init();
}
/* Implementation of method "getReplytStr()" defined in Interface
"iRmiEliza.java"
*/
public String getReplytStr(String request) throws RemoteException{
return getAnswer(request);
}
public static void main(String args[]) {
try {
LocateRegistry.createRegistry(1099); //default RMI port
String myName = "ServerEliza";
RmiElizaServer elz = new RmiElizaServer(myName);
Naming.bind("rmi:///"+myName, elz);
System.out.println("Ready for RMI's");
} catch (Exception e) {
e.printStackTrace();
}
}
} // end of file “RmiElizaServer.java”
/* rmiElizaServer.cpp: native C functions
*/
#include <jni.h>
#include <RmiElizaServer.h>
#include "eliza.cpp"
/*
* Class:
RmiElizaServer
- 49 -
* Method:
getAnswer
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_RmiElizaServer_getAnswer
(JNIEnv *env, jobject obj, jstring request){
char *str;
char tmp[MAXSIZE];
/*get string "request" passed from RmiElizaServer.java and convert it to C
string */
const char *req = env->GetStringUTFChars(request, 0);
strcpy(tmp,req);
str=eliza(tmp);
env->ReleaseStringUTFChars(request,req);
//convert string req from c string to Java string
return env->NewStringUTF(str);
}
/*
* Class:
RmiElizaServer
* Method:
init
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_RmiElizaServer_init
(JNIEnv *env, jobject obj) {
// *initDialog() from eliza.cpp
return env->NewStringUTF(initDialog());
} //end of file “rmiElizaServer.cpp”
/* makefile for RMI Eliza Server
*/
RmiElizaServer: RmiElizaServer.class librmiEliza.so RmiElizaServer_Skel.class
RmiElizaServer_Stub.class
java RmiElizaServer
RmiElizaServer_Skel.class RmiElizaServer_Stub.class : RmiElizaServer.class
/usr/opt/java122/bin/rmic RmiElizaServer
RmiElizaServer.class : RmiElizaServer.java iRmiEliza.class
javac RmiElizaServer.java
iRmiEliza.class : iRmiEliza.java
javac iRmiEliza.java
librmiEliza.so : rmiEliza.cpp RmiElizaServer.h
cxx -shared -I/usr/opt/java122/include -I/usr/opt/java122/include/alpha -I. pthread rmiEliza.cpp -o librmiEliza.so
RmiElizaServer.h : RmiElizaServer.class
javah -jni RmiElizaServer
clean :
rm *.class RmiElizaServer.h librmiEliza.so
/* RmiElizaClient.java: The client definition.
*/
import java.rmi.*;
- 50 -
public class RmiElizaClient {
public RmiElizaClient() {}
public native String getInput();
public native String getReqStr(String request);
static { System.loadLibrary("rmiElizaClient"); }
public static void main(String args[]) {
String host;
RmiElizaClient me=new RmiElizaClient();
iRmiEliza remoteElz;
try {
if(args.length==1) host=args[0];
else host = "wedge.tcs.auckland.ac.nz:1099";
remoteElz = (iRmiEliza)Naming.lookup("rmi://"+host+"/ServerEliza");
System.out.print("from rmiserver: "+remoteElz.getInitStr());
String resp = remoteElz.getReplytStr( me.getInput() );
System.out.println("A> "+resp);
for(int i=0; i< 20; i++){
resp = me.getReqStr(resp);
System.out.println("Q> "+resp);
resp = remoteElz.getReplytStr( resp );
System.out.println( "A> "+resp );
}
} catch (Exception e) {
e.printStackTrace();
}
}
} // end of file “RmiElizaClient.java”
/* rmiElizaClient.cpp: native C functions
*/
#include <jni.h>
#include <RmiElizaClient.h>
#include "eliza.cpp"
/*
* Class:
RmiElizaClient
* Method:
getInput
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_RmiElizaClient_getInput
(JNIEnv *env, jobject obj){
char input[MAXSIZE];
getInput(input);
return env->NewStringUTF(input);
}
/*
* Class:
RmiElizaClient
* Method:
getReqStr
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_RmiElizaClient_getReqStr
(JNIEnv *env, jobject obj, jstring request){
- 51 -
char *str;
char tmp[MAXSIZE];
/* Get string "request" passed from RmiElizaClient.java and convert
it to C string. */
const char *req = env->GetStringUTFChars(request, 0);
strcpy(tmp,req);
str=clienteliza(tmp);
env->ReleaseStringUTFChars(request,req);
//convert string req from c string to Java string
return env->NewStringUTF(str);
} // end of file “rmiElizaClient.cpp”
/* clientmakefile: for RMI Eliza Client
*/
RmiElizaClient : RmiElizaClient.class librmiElizaClient.so
java RmiElizaClient
RmiElizaClient.class : RmiElizaClient.java iRmiEliza.class
javac RmiElizaClient.java
iRmiEliza.class : iRmiEliza.java
javac iRmiEliza.java
librmiElizaClient.so : rmiElizaClient.cpp RmiElizaClient.h
cxx -shared -I/usr/opt/java122/include -I/usr/opt/java122/include/alpha -I. pthread rmiElizaClient.cpp -o librmiElizaClient.so
RmiElizaClient.h : RmiElizaClient.class
javah -jni RmiElizaClient
clean :
rm RmiElizaClient.class RmiElizaClient.h librmiElizaClient.so
Testing Performance
/* File “testSockClient.cpp: testing performance of socket connection
testSockServer.cpp has not much difference with sserver.cpp: just accepts
client’s connection then continuously receives messages from and sends
messages to char buf[BUFFERSIZE].
*/
/* identical part to sclient.cpp omitted */
... ... ...
... ... ...
#include "Timer.cc" // class provided by Dr. S Manoharan
#define BUFFERSIZE 1000
int main(int argc, char *argv[])
int times=1;
char txBuf[BUFFERSIZE];
/* identical part to sclient.cpp omitted */
..........
int i;
for (i = 0; i < BUFFERSIZE; i++ ) {
txBuf[i] = 'd';
}
- 52 -
txBuf[i]='\0';
Timer timer;
char rxBuf[BUFFERSIZE];
int n = 0;
aa:
long bytesRecd = 0;
long bytesSent = 0;
timer.start();
for (; ; ) {
if( bytesSent != BUFFERSIZE * times ) {
send(sockfd, txBuf, strlen(txBuf), 0);
bytesSent += strlen(txBuf);
}
if( bytesRecd != BUFFERSIZE * times) {
int nbytes = recv(sockfd, rxBuf, sizeof(rxBuf), 0);
if ( nbytes <= 0 ) break;
bytesRecd += nbytes;
}
if( bytesRecd == bytesSent && bytesSent == BUFFERSIZE * times) break;
}
timer.stop();
cout << "Time Used to send " << bytesSent/1000000.0 << " MB and receive "
<< bytesRecd/1000000.0 << " MB: "<< timer.elapsedTime() << endl;
times += 1000; //increase data to 1000 times greater
n ++;
if (n <21) goto aa;
delete rxBuf;
close(sockfd);
return 0;
} // end of file “testClient.cpp”
/* File “testMqClient.cpp: testing performance of message queue.
testMqServer.cpp has not much difference with mqserver.cpp: just open/create
queues then receiving and sending messages continuously.
*/
/* identical part to mqclient.cpp omitted */
... ... ...
... ... ...
#include "Timer.cc" // class provided by Dr. S Manoharan
#define BUFFERSIZE 1000
int main() {
/* Buffer to receive msg into */
char txBuf[BUFFERSIZE];
int num_bytes_to_send = BUFFERSIZE;
/* identical part to mqclient.cpp omitted */
... ... ...
... ... ...
mq_attr.mq_maxmsg = 10
int times=1;
int i;
for (i = 0; i < BUFFERSIZE; i++ ) {
txBuf[i] = 'd';
}
- 53 -
txBuf[i]='\0';
char rxBuf[BUFFERSIZE];
int n = 0;
Timer timer;
/* Perform the continuous sending and receiving */
timer.start();
aa:
int bytesSent = 0;
int bytesRecd = 0;
for (i=0; i<times; i++) {
//send the query to the cmq
status = mq_send(mqfd3, txBuf, sizeof(txBuf), priority_of_msg);
if (status == -1) {
perror("mq_send failure on mqfd3");
exit(1);
}
bytesSent += sizeof(txBuf);
//get the response from "smq"
num_bytes_received = mq_receive(mqfd1,rxBuf,BUFFERSIZE,0);
if (num_bytes_received == -1) {
perror("mq_receive failure on mqfd1");
exit(1);
}
bytesRecd += sizeof(rxBuf);
}
timer.stop();
cout << "Time Used to send " << bytesSent/1000000.0 << " MB and receive
"<< bytesRecd/1000000.0 << " MB: "<< timer.elapsedTime() << endl;
times += 1000;
n ++;
if (n <21) goto aa;
/* close the queue */
/* destroy the queues */
printf("Exiting mqclient...\n");
return 0;
} // end of file “testMqClient.cpp”
/* File “Timer.h”: class Timer definition.
Provided by Dr S. Manoharan
*/
#ifndef _Timer_h_
#define _Timer_h_
#endif
#include <assert.h>
#include <iostream.h>
#include <stdlib.h>
class Timer {
friend ostream& operator<<(ostream&, const Timer&);
private:
enum Status { TIMER_ON, TIMER_OFF };
private:
double delta;
// elapsed time in seconds
double startTime;
// time at which timer started
Status status;
protected:
- 54 -
protected:
virtual void cleanup();
void copy(const Timer&);
public:
Timer();
Timer(const Timer& c)
virtual ~Timer()
{ copy(c); }
{ cleanup(); }
Timer& operator=(const Timer&);
public:
double elapsedTime();
void reset() { delta = 0; status = TIMER_OFF;
void start();
void stop();
};
}
/* File “Timer.cc”: define the member functions of class Timer.
Provided by Dr S. Manoharan */
#ifdef GETRUSAGE_TIME
#include <sys/time.h>
#include <sys/resource.h>
#if defined(__sparc__)
extern "C" int getrusage(int who, struct rusage *rusage);
#endif
#endif // GETRUSAGE_TIME
#include <stdlib.h>
#include "Timer.h"
ostream&
operator<<(ostream& os, const Timer&)
{
return os;
}
Timer::Timer()
{
reset();
}
void
Timer::cleanup()
{
}
void
Timer::copy(const Timer& c)
{
delta = c.delta;
startTime = c.startTime;
status = c.status;
}
Timer&
Timer::operator=(const Timer& c)
{
- 55 -
if ( this != &c ) {
cleanup(); copy(c);
}
// not self
return *this;
}
double
Timer::elapsedTime()
{
double rval;
switch (status) {
case TIMER_OFF
:
rval = delta;
break;
case TIMER_ON
:
stop();
rval = delta;
start();
break;
default
:
cerr << "invalid case in Timer::elapsedTime" <<
endl;
exit(-1);
break;
}
return rval;
}
void
Timer::start()
{
status = TIMER_ON;
#
#
#
#
ifdef GETRUSAGE_TIME
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
if defined(__alpha) || defined(__sparc__) || defined(__sgi)
startTime = (usage.ru_utime.tv_sec
+ 1e-6 * usage.ru_utime.tv_usec);
endif
endif // GETRUSAGE_TIME
return;
}
void
Timer::stop()
{
status = TIMER_OFF;
#
#
#
#
ifdef GETRUSAGE_TIME
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
if defined(__alpha) || defined(__sparc__) || defined(__sgi)
delta += (usage.ru_utime.tv_sec
+ 1e-6 * usage.ru_utime.tv_usec) - startTime;
endif
endif // GETRUSAGE_TIME
- 56 -
return;
}
/* iRmiTest.java: The interface declaration for a remote object for testing RMI.
*/
import java.rmi.Remote;
public interface iRmiTest extends Remote {
public void receive(String clientMsg) throws RemoteException;
public String send() throws RemoteException;
} //end of file “iRmiTest.java”
/* testRMIClient.java: testing performance of Java RMI.
Without using JNI.
*/
import java.rmi.*;
public class testRMIClient {
iRmiTest server;
//host can be any host where our RMI server is running
String host="wedge.tcs.auckland.ac.nz:1099";
static final int MSGSIZE=62500; // string length
public testRMIClient() {}
public void connect(){
try{
server = (iRmiTest)Naming.lookup("rmi://"+host+"/ServerEliza");
} catch (Exception e) { e.printStackTrace(); }
}
public void send(String clientMsg){
try{
server.receive(clientMsg);
} catch (Exception e) { e.printStackTrace(); }
}
public String receive(){
try{
return server.send();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
return null;
}
public static void main(String args[]) {
testRMIClient me=new testRMIClient();
int times=0;
char temp[]=new char[MSGSIZE];
//initialise the char array
- 57 -
for(int i=0; i< MSGSIZE; i++){
temp[i] = 'd';
}
String msg = new String(temp);
System.out.println("msg length: "+msg.length()/1000000.0 +" MB");
me.connect();
for(int n=0; n<21; n++){ //20 times
long bytesSend = 0;
long bytesRecd = 0;
long startTime = System.currentTimeMillis();
if(n==0) {
me.send("");
me.receive();
}else
for(int i=0; i< times; i++){
startTime = System.currentTimeMillis();
me.send(msg);
bytesSend += MSGSIZE;
startTime = System.currentTimeMillis();
msg = me.receive();
bytesRecd += MSGSIZE;
}
System.out.println("Time Used to send and receive " + bytesSend /
1000000.0 +" MB: "+ (System.currentTimeMillis()-startTime)/1000.0 + " sec" );
System.out.println((System.currentTimeMillis()-startTime)/1000.0);
times += 16;
}
}
} //end of file “testRMIClient.java”
/* testRMIServer.java: server for testing performance of Java RMI.
Without using JNI.
*/
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
public class testRMIServer extends UnicastRemoteObject implements iRmiTest {
String name;
String msg=new String();
// constructor
public testRMIServer(String name) throws RemoteException {
super();
this.name = name;
}
public void receive(String clientMsg) throws RemoteException {
msg = clientMsg;
}
public String send() throws RemoteException{
return msg;
- 58 -
}
public static void main(String args[]) {
try {
LocateRegistry.createRegistry(1099); //default RMI port
String myName = "ServerEliza";
testRMIServer elz = new testRMIServer(myName);
Naming.bind("rmi:///"+myName, elz);
System.out.println("Ready for RMI's");
} catch (Exception e) {
e.printStackTrace();
}
}
} // end of file “testRMIServer.java”
End of Testing Performance files
/* File “eliza.h”: For use of library libsock.a and libmq.a
*/
#include <stdio.h>
#include <ctype.h>
#include <iostream.h>
#include <stdlib.h>
#include <strings.h>
#ifndef MAXSIZE
#define MAXSIZE 256
#endif
extern char *initDialog();
extern char *eliza(char inputline[]);
extern char *clienteliza(char resp[]);
extern char *tolowercase(char line[]);
extern char *substring(char line[], int start, int length);
extern int isrepeat(int newpos[], int foundkeys, int j);
extern void replace(char line[], char *keys1[], char *keys2[], int pos);
extern void toappend(char line[], int pos);
extern int getcatnum(char line[], int side);
extern int hasstar( char *resp);
extern void insert(char resp[], char chars[], int pos);
extern char *getFirstWord(char *line);
extern int contains(char *ask[], char *word);
extern void trim(char str[]);
extern void getInput(char buf[]);
// end of "eliza.h"
/* File “GllibSocket.cpp”: source for GllibSocket.o
GllibSocket.o and eliza.o are used to create library libsock.a.
Commands:
cxx –c GllibSocket.cpp
ar –q libsock.a GllibSocket.o eliza.o
ranlib libsock.a
*/
#include <netinet/in.h>
#include <iostream.h>
#include <arpa/inet.h>
- 59 -
#include <netdb.h>
#include <stdlib.h>
int GLServerSocket(int portNumber){
struct sockaddr_in server;
int sockfd;
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){
cout << "Connot create server socket\n";
return sockfd;
}
server.sin_family = AF_INET;
server.sin_port = htons(portNumber);
server.sin_addr.s_addr = INADDR_ANY;
cout << "serverIP: " << inet_ntoa(server.sin_addr) << endl;
if( bind(sockfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) < 0 ){
cout << "Cannot bind to port " << portNumber << endl;
return -1;
}
if( listen(sockfd, 20) < 0 ) {
cout << "Cannot listen to port " << portNumber << endl;
return -1;
}
return sockfd;
}
int GLAccept(int sockfd){
int nsockfd;
int addrsLen = sizeof(struct sockaddr_in);
struct sockaddr_in client;
if( (nsockfd = accept(sockfd, (struct sockaddr *)&client, &addrsLen) ) < 0){
cout << "error accepting connection\n";
return nsockfd;
}
printf("Connection from client: %s\n", inet_ntoa(client.sin_addr) );
return nsockfd;
}
int GLSend(int nsockfd, char *buf, int size){
int nbytes;
if( (nbytes = send(nsockfd, buf, size, 0) ) < 0 )
printf("error sending message to: %d\n", nsockfd);
return nbytes;
}
int GLReceive(int nsockfd, char *buf, int size){
int nbytes;
if( (nbytes = recv(nsockfd, buf, size-1, 0) ) < 0) {
printf("error receiving message from: %d\n", nsockfd);
return nbytes;
}
buf[nbytes]='\0';
return nbytes;
}
//used for client connection
int GLConnect(char *hostname, int portNumber){
struct sockaddr_in server;
- 60 -
struct hostent *hostentry = gethostbyname(hostname);
if ( hostentry == 0 ) {
cout << "DNS lookup for " << hostname << " failed" << endl;
exit(1);
}
char *hostIP = inet_ntoa(*((struct in_addr *)hostentry->h_addr));
int rsockfd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_port = htons(portNumber);
server.sin_addr.s_addr = inet_addr(hostIP);
int rstatus = connect(rsockfd, (struct sockaddr *)&server, sizeof(struct
sockaddr));
if ( rstatus == -1 ) {
printf("Connection to %s at port %d failed\n", hostname, portNumber);
exit(1);
}
return rsockfd;
} //end of file “GllibSocket.cpp”
/* File “sock.h”: header file for socket client/server programs
*/
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <strings.h>
#include "eliza.h"
#ifndef MAXSIZE
#define MAXSIZE 256
#endif
// functions defined in libsock.a
extern int GLServerSocket(int portNumber);
extern int GLAccept(int sockfd);
extern int GLSend(int nsockfd, char *buf, int size);
extern int GLReceive(int nsockfd, char *buf, int size);
extern int GLConnect(char *hostname, int portNumber);
//super class for serverClass and clientClass
class sockClass {
protected:
int portNumber;
public:
char buf[MAXSIZE];
sockClass() {}
int Receive(int nsockfd, char *buf, int size){
return GLReceive(nsockfd, buf, size);
}
int Send(int nsockfd, char *buf, int size){
- 61 -
return GLSend(nsockfd, buf, size);
}
void run();
virtual ~sockClass() {
delete buf;
}
};
class serverClass : public sockClass {
protected:
int sockfd;
int nsockfd;
public:
serverClass() {};
serverClass(int portNumber){ this-> portNumber= portNumber;}
int ServerSocket(){
return sockfd = GLServerSocket(portNumber);
}
int Accept(){ return GLAccept(sockfd); }
void run() {
printf("Starting Eliza Server...\n");
if( (nsockfd = Accept() ) <0 ) goto end;
strcpy(buf, initDialog());
if (Send(nsockfd, buf, strlen(buf)) < 0 ) goto end;
int n;
for(n=0; n<10;n++) {
if( Receive(nsockfd, buf, sizeof(buf)) < 0) break;
strcpy(buf, eliza(buf)); //generate answer
//pay attention of using sizeof
if (Send(nsockfd, buf, strlen(buf)) < 0 ) break;
}
sleep(2);
close(nsockfd);
end:
printf("Stopping Eliza Server...\n");
close(sockfd);
return;
}
};
class clientClass : public sockClass {
protected:
char *hostname;
int nsockfd;
public:
clientClass(const char *hostname, int portNumber){
this -> portNumber= portNumber;
this -> hostname = new char[strlen(hostname)+1];
strcpy(this -> hostname, hostname);
}
int ClientConnect(){
return nsockfd = GLConnect(hostname, portNumber);
}
void run(){
printf("Starting Eliza Client...\n");
printf("Connecting to eliza server: %s at port %d\n",
- 62 -
hostname,portNumber);
clientClass clientToGUI("localhost", 4567);
int csockfd; //connection to GUI server
if( ( csockfd = clientToGUI.ClientConnect()) <= 0) goto end;
//first receive the prompt
if( Receive(nsockfd, buf, sizeof(buf) ) <= 0) goto end;
//also send the prompt to GUI server
char tmp[MAXSIZE];
strcpy(tmp, buf);
strcat(tmp, "PROMPT");
clientToGUI.Send(csockfd, tmp, strlen(tmp));
char confirm[MAXSIZE];
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
printf("con %s\n", confirm);
//get input from GUI server to start eliza
clientToGUI.Receive(csockfd, buf, sizeof(buf));//the initial query
printf("init %s\n", buf);
if( Send(nsockfd, buf, strlen(buf)) < 0 ) goto end;//send it to eliza
int n=0;
while(1){
if( Receive(nsockfd, buf, sizeof(buf) ) < 0 ) break;
cout << "A> " << buf << endl;
//also send the msg to GUI server
strcpy(tmp, buf);
strcat(tmp, "SERVER");
clientToGUI. Send(csockfd, tmp, strlen(tmp));
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
if(n==9) break;
n++;
strcpy(buf, clienteliza(buf));
//send it to GUI server too
strcpy(tmp, buf);
strcat(tmp, "CLIENT");
clientToGUI. Send(csockfd, tmp, strlen(tmp));
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
cout << "Q> " << buf <<endl;
if( Send(nsockfd, buf, strlen(buf)) < 0 ) break;
sleep(2);
}
clientToGUI. Send(csockfd, "END", strlen("END"));
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
end:
printf("Stopping Eliza Client...\n");
close(nsockfd);
return;
}
virtual ~clientClass(){
delete hostname;
}
}; //end of file “sock.h”
/*
File "sockserver.cpp": modified version of "sserver.cpp".
Using functions defined in library libsock.
To compile: cxx sockserver.cpp -o sockserver -L. -lsock
To execute: ./sockserver
- 63 -
*/
#include "sock.h"
#include "eliza.h"
int main(int argc, char *argv[]) {
int sockfd;
int nsockfd;
int portNumber = 2888;
char buf[MAXSIZE];
int nbytes;
if( (sockfd = GLServerSocket(portNumber) ) < 0 ) exit(1);
//accept client connections
for(;;) {
if( (nsockfd = GLAccept(sockfd) ) <0 ) {
close(nsockfd);
continue;
}
strcpy(buf,initDialog()); //get prompt message
if (GLSend(nsockfd, buf, strlen(buf)) < 0 ) break;
// continuous conversation
while(1) {
if( GLReceive(nsockfd, buf, sizeof(buf)) < 0) break;
cout << "received: " << buf << endl;
strcpy(buf, eliza(buf)); //generate next response
if (GLSend(nsockfd, buf, strlen(buf)) < 0 ) break;
}
close(nsockfd);
}
close(sockfd);
return 0;
} // end of file "sockserver.cpp"
/*
File "sockclient.cpp": modified version of "sclient.cpp".
Using functions defined in library libsock.
To compile: cxx sockclient.cpp -o sockclient -L. -lsock
To execute: sockclient <hostname>
*/
#include "sock.h"
#include "eliza.h"
int main(int argc, char *argv[]) {
char *hostname = "localhost";
int portNumber = 2888;
char buf[MAXSIZE];
int rsockfd, nbytes;
int n=0; // times of conversation
if ( argc > 1 ) {
hostname = argv[1];
cout << "connecting to: " << hostname << endl;
} else printf("usage: ./sockclient <hostname>\n");
rsockfd = GLConnect(hostname, portNumber);
- 64 -
if( (nbytes = GLReceive(rsockfd, buf, sizeof(buf))) < 0) goto end;
buf[nbytes] ='\0'; // terminates char array
printf("%s", buf);
getInput(buf); //accept user entry
if( GLSend(rsockfd, buf, strlen(buf)) < 0 ) goto end;
// continuous
for( ; ; ) {
if( (nbytes
buf[nbytes]
cout << "A>
conversation
= GLReceive(rsockfd, buf, sizeof(buf))) < 0 ) break;
='\0';
" << buf << endl;
if(n==20) break;
strcpy( buf, clienteliza(buf) );
cout << "Q> " << buf << endl;
if( GLSend(rsockfd, buf, strlen(buf)) < 0 ) break;
n++;
}
end:
close(rsockfd);
return 0;
} // end of file "sockclient.cpp"
/* File GLlibMq.cpp: source for GLlibMq.o
GLlibMq.o and eliza.o are used to create library libmq.a.
Commands:
cxx -c GLlibMq.cpp -lrt
ar -q libmq.a GLlibMq.o eliza.o
ranlib libmq.a
*/
#include
#include
#include
#include
<mqueue.h>
<iostream.h>
<sys/fcntl.h> // for "O_WRONLY", "O_CREAT", "O_RDONLY"
<stdlib.h> //for exit()
//file: GLlibMq.cpp
void GLMqCreate(int msgsize, mqd_t& mqfd1, mqd_t& mqfd2, int pmode, const char
*sname, const char *cname){
struct mq_attr attr;
/* Fill in attributes for message queue */
attr.mq_maxmsg = 1;
attr.mq_msgsize = msgsize;
attr.mq_flags
= 0;
/* Open the server sname queue, and create it for client to receive msgs */
mqfd1 = mq_open(sname, O_RDWR|O_CREAT, pmode, &attr);
if (mqfd1 == -1) {
perror("mq_open failure");
exit(0);
};
/* Open the client cname queue, and create it if the client has not created
it. server side will receive queries from "cmq"
- 65 -
*/
mqfd2 = mq_open(cname, O_RDWR|O_CREAT, pmode, &attr);
if (mqfd2 == -1) {
perror("mq_open failure");
exit(0);
}
}
void GLMqSend(int mqfd, char *msg_buffer, int size, unsigned int priority) {
// 0 returned by mq_send() on success
if ( mq_send(mqfd, msg_buffer, size, priority) < 0 ) {
perror("mq_send failure");
exit(1);
}
}
int GLMqReceive(int mqfd, char *msg_buffer, int size, unsigned int priority) {
int nbytes;
if ( (nbytes = mq_receive(mqfd, msg_buffer, size, &priority) ) < 0 ) {
perror("mq_receive failure");
exit(1);
}
return nbytes; // ssize_t size returned on success or -1 on failure
}
void GLMqClose(int mqfd){
if (mq_close(mqfd) == -1)
perror("mq_close failure");
}
void GLMqUnlink(const char *name){
if (mq_unlink(name) == -1)
perror("mq_unlink failure in mqserver");
}// end of file “GLlibMq.cpp”
/* File “mq.h”: header file for message queue eliza programs
*/
#ifndef _MQ_H_
#define _MQ_H_
#endif
#include
#include
#include
#include
#include
#include
#include
<mqueue.h>
<iostream.h>
<sys/fcntl.h> // for "O_WRONLY", "O_CREAT", "O_RDONLY"
<stdlib.h> //for exit(0)
<string.h>
"eliza.h"
"sock.h"
#define NUMTIMES 10 // number of times sending and receiving
#define PMODE 0666
#ifndef MSGSIZE
#define MSGSIZE 255 //this is the max size of a msg
#endif
// functions defined in libmq.a
extern void GLMqCreate(int msgsize, mqd_t& mqfd1, mqd_t& mqfd2, int pmode,
- 66 -
constchar *sname, const char *cname);
extern void GLMqSend(int mqfd, char *buf, int size, unsigned int priority);
extern int GLMqReceive(int mqfd, char *buf, int size, unsigned int priority);
extern void GLMqClose(int mqfd);
extern void GLMqUnlink(const char *name);
class mqServerClass {
private:
char *server_mq;
char *client_mq;
public:
mqServerClass(char *server_mq, char *client_mq) {
this -> server_mq = server_mq;
this -> client_mq = client_mq;
}
void run() {
char buf[MSGSIZE];
int mqfd1=0, mqfd2=0; // mqfd1: client queue, mqfd2: server queue
const char *server_mq = "smq";
const char *client_mq = "cmq";
unsigned priority = 1;
printf("Starting the mqserver...\n");
GLMqCreate(MSGSIZE, mqfd1, mqfd2, PMODE, server_mq, client_mq);
strcpy(buf,initDialog());
//send the initial prompt for the client to type in first query
GLMqSend(mqfd1, buf, MSGSIZE, priority);
//receive the first query from the client queue
GLMqReceive(mqfd2, buf, MSGSIZE, priority);
/* Perform the continuous sending and receiving */
for (int i=0; i<NUMTIMES+1; i++) {
//imitate server, generate reply
strcpy(buf, eliza(buf));
GLMqSend(mqfd1, buf, MSGSIZE, priority);
//receive the next query from the client queue
if(i != NUMTIMES) // more to receive
GLMqReceive(mqfd2, buf, MSGSIZE, priority);
}
end:
/* End of receiving from the client queue, so close it */
GLMqClose(mqfd2);
/* destroy the client queue */
GLMqUnlink(client_mq);
printf("\nExiting the mqserver...\n");
return;
}
};
class mqClientClass {
private:
char *server_mq;
char *client_mq;
public:
mqClientClass(char *server_mq, char *client_mq) {
this -> server_mq = server_mq;
this -> client_mq = client_mq;
}
void run(){
char buf[MSGSIZE];
int mqfd1=0, mqfd2=0; // mqfd1: client queue, mqfd2: server queue
- 67 -
const char *server_mq = "smq";
const char *client_mq = "cmq";
unsigned priority = 1;
printf("Starting the mqclient...\n");
clientClass clientToGUI("localhost", 4567);
int csockfd;
if( ( csockfd = clientToGUI.ClientConnect()) < 0) goto end;
GLMqCreate(MSGSIZE, mqfd1, mqfd2, PMODE, server_mq, client_mq);
//receive the initial prompt from "smq"
GLMqReceive(mqfd1, buf, MSGSIZE, priority);
//also send the prompt to GUI server
char tmp[MAXSIZE];
strcpy(tmp, buf);
strcat(tmp, "PROMPT");
clientToGUI.Send(csockfd, tmp, strlen(tmp));
char confirm[MAXSIZE];
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
//get input from GUI server to start eliza
clientToGUI.Receive(csockfd, buf, sizeof(buf));//the initial query
//send the query to the cmq
GLMqSend(mqfd2, buf, MSGSIZE, priority); //getInput(buf);
for(int n=0; n < NUMTIMES; n++) {
/* Perform the continuous sending and receiving */
if(n) { //if not the first query string from user
//generate next query
strcpy(buf, clienteliza(buf));
cout << "Q> " << buf << endl;
//send the query to the cmq
GLMqSend(mqfd2, buf, MSGSIZE, priority);
//send it to GUI server too
strcpy(tmp, buf);
strcat(tmp, "CLIENT");
clientToGUI. Send(csockfd, tmp, strlen(tmp));
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
}
sleep(2);
//get the response from "smq"
GLMqReceive(mqfd1, buf, MSGSIZE, priority);
//also send the msg to GUI server
strcpy(tmp, buf);
strcat(tmp, "SERVER");
clientToGUI. Send(csockfd, tmp, strlen(tmp));
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
cout << "A> " << buf << endl;
}
clientToGUI.Send(csockfd, "END", strlen("END"));
clientToGUI.Receive( csockfd, confirm, sizeof(confirm)); //confirm
end:
/* close the server queue */
GLMqClose(mqfd1);
/* destroy the server queue */
GLMqUnlink(server_mq);
printf("Exiting mqclient...\n");
return;
}
}; //end of file “mq.h”
- 68 -
/* File mqueueserver.cpp: modified version of "mqserver.cpp".
Using functions defined in library libmq.
To compile: cxx mqueueserver.cpp -o mqueueserver -L. –lmq -lrt
To execute: ./mqueueserver
*/
#include "mq.h"
#include "eliza.h"
int main() {
int mqfd1=0, mqfd2=0; // mqfd1: sending to, mqfd2: receiving from
char msg_buffer[MSGSIZE];
unsigned int priority_of_msg = 1;
const char *server_mq = "smq";
const char *client_mq = "cmq";
GLMqCreate(MSGSIZE, mqfd1, mqfd2, PMODE, server_mq, client_mq);
printf("Starting the mqserver...\n");
strcpy(msg_buffer,initDialog());
//send the initial prompt for the client to type in first query
GLMqSend(mqfd1, msg_buffer, MSGSIZE, priority_of_msg);
//receive the first query from the client queue
GLMqReceive(mqfd2, msg_buffer, MSGSIZE, priority_of_msg);
/* Perform the continuous sending and receiving */
for (int i=0; i<NUMTIMES; i++) {
strcpy(msg_buffer, eliza(msg_buffer)); //mimic server, generate reply
GLMqSend(mqfd1, msg_buffer, MSGSIZE, priority_of_msg);
//receive the next query from the client queue
if(i != NUMTIMES -1 ) // more to receive
GLMqReceive(mqfd2, msg_buffer, MSGSIZE, priority_of_msg);
}
/* End of receiving from the client queue, so close it */
GLMqClose(mqfd2);
/* destroy the client queue */
GLMqUnlink(client_mq);
printf("\nExiting the mqserver...\n");
return 0;
} // end of file “mqueueserver.cpp”
/* File "Controller.cpp": A server program to interface between GUI Client and
eliza client/server programs.
To compile: cxx Controller.cpp -o ./con -L. –lsock –lmq -lrt
To execute: ./con
*/
#include "mq.h"
serverClass GUI_server(5153);
int main(int argc, char *argv[]) {
int sockfd, csockfd;
char buf[MAXSIZE];
char buff[MAXSIZE];
- 69 -
int ssockfd=0; // for GUI client connection
bool rmiConn = false;
int pidsr=0, pidcl=0;
// create a socket for GUI connection
cout << "Setting up GUI server...\n";
if( (sockfd = GUI_server.ServerSocket() ) < 0 ) exit(1);
//socket deal with eliza client connection
cout << "Setting up eliza c/s server...\n";
serverClass ceserver(4567); //client eliza server
if(ceserver.ServerSocket() < 0) exit(1);
accept:
printf("accepting connection from GUI client\n");
if( (ssockfd = GUI_server.Accept() ) < 0 ) exit(1);
again:
printf("receiving messages from GUI client\n");
rmiConn = false;
if( GUI_server.Receive(ssockfd, buf, sizeof(buf)) <= 0) goto accept;
printf("gui received: %s ",buf);
if(buf[strlen(buf)-1]==10 && buf[strlen(buf)-2]==13)
buf[strlen(buf)-2]='\0';
if( !strcmp(buf, "SOCKET") ){ // require running socket programs
int pids = fork();
if( pids == 0 ) {
sockServerClass *sockserver = new sockServerClass(5566);
if (sockserver->ServerSocket() < 0) exit(1);
sockserver->run();
} else {
pidsr= pids;
sleep(1); // waiting for server setting up
int pidc = fork();
if( pidc == 0 ) {
sockClientClass *sockclient = new sockClientClass("localhost", 5566);
if(sockclient->ClientConnect() < 0) exit(1);
sockclient->run();
} else
pidcl = pidc;
csockfd = ceserver.Accept();
rmiConn = false;
}
} else if( !strcmp(buf, "MQ") ) { //require running message queue
int pids = fork();
if( pids == 0 ) {
mqServerClass mqserver;
mqserver.run();
} else {
pidsr= pids;
sleep(1); // waiting for server setting up
int pidc = fork();
if( pidc == 0 ) {
mqClientClass mqclient;
mqclient.run();
} else pidcl= pidc;
csockfd = ceserver.Accept();
rmiConn = false;
}
} else if( !strcmp(buf, "RMI") ) { // require running RMI
int pids = fork();
if( pids == 0 ) if(execl("/usr/bin/java", "java", "RmiElizaServer", 0)<0)
goto fail;
else {
- 70 -
pidsr= pids;
sleep(1); // waiting for server setting up
int pidc = fork();
if( pidc == 0 ) if(execl("/usr/bin/java","java","RmiElizaClient",0)<0) )
goto fail;
else
pidcl= pidc;
csockfd = ceserver.Accept();
rmiConn=true;
}
} //end if
//receive prompt from eliza client
// if Receive or Send error, eliza client/server session ends
if(ceserver.Receive(csockfd, buf, sizeof(buf)) <= 0) goto fail;
if( ceserver.Send(csockfd, "confirm\n", 10 ) < 0 ) goto fail;
int x;
if( rmiConn ) buf[strlen(buf)-1] = '\0';
for(x=0;x< strlen(buf);x++)
if(buf[x]==10) buf[x] = '~';
//must add a '\n' for Java GUI Client to receive the line
strcat(buf, "\n");
if( GUI_server.Send(ssockfd, buf, strlen(buf) ) < 0 ) goto fail;
// receive one start up query from GUI client
if(GUI_server.Receive(ssockfd, buf, strlen(buf)) <=0) goto fail;
strcpy(buf, substring(buf, 6, strlen(buf)) ); //prompt entry from GUI
ceserver.Send(csockfd, buf, strlen(buf));
sleep(1);
while(1){
//receive from eliza client
if(ceserver.Receive(csockfd, buf, sizeof(buf))<=0) goto
if( ceserver.Send(csockfd, "confirm\n", 20 ) < 0 ) goto
if( rmiConn ){ //from java socket
if( GUI_server.Send(ssockfd, buf, strlen(buf) ) < 0 )
}else { //from C socket or MQ, need adding a '\n'
strcat(buf, "\n"); // for Java socket receipt
if( GUI_server.Send(ssockfd, buf, strlen(buf) ) < 0 )
}
if(! strcmp(buf, "END\n") ) break;
}//end while
fail;
fail;
goto fail;
goto fail;
close(csockfd);
sigsend(P_PID, pidsr, SIGKILL);
sigsend(P_PID, pidcl, SIGKILL);
goto again;
fail:
sigsend(P_PID, pidsr, SIGKILL);
sigsend(P_PID, pidcl, SIGKILL);
close(csockfd);
close(ssockfd);
goto accept;
end:
return 0;
} // end of file “Controller.cpp”
/* File: “RmiElizaClient2.java”: The modified client definition.
To Compile: javac iRmiEliza.java RmiElizaClient2.java
To run: java RmiElizaClient2
*/
import java.rmi.*;
- 71 -
import java.net.*;
import java.io.*;
public class RmiElizaClient2 {
static
public
public
public
static
RmiElizaClient2 me;
RmiElizaClient2() {}
native String getInput();
native String getReqStr(String request);
{ System.loadLibrary("rmiElizaClient"); }
public static void main(String args[]) {
String host;
me=new RmiElizaClient2();
iRmiEliza remoteElz;
Socket sock;
BufferedReader in;
PrintWriter out;
try {
System.out.println("Creating Socket to GUI Server");
sock = new Socket("localhost", 5482);
in = new BufferedReader(
new InputStreamReader(sock.getInputStream()));
out = new PrintWriter(sock.getOutputStream(),true);
if(args.length==1) host=args[0];
else host = "wedge.tcs.auckland.ac.nz:1099";
remoteElz = (iRmiEliza)Naming.lookup("rmi://"+host+"/ServerEliza");
String line = remoteElz.getInitStr();
System.out.println("from server: " + line); //prompt
out.println(line + "PROMPT");
// to GUI server
if(in.readLine() == null) sock.close(); // receiving confirmation
line= in.readLine(); //get first query
int i=0;
while(i<10) {
if( i != 0 ) { //not the first time received from GUI
line = me.getReqStr(line);
System.out.println("Q> "+line);
out.println(line + "CLIENT");
// to GUI server
if( in.readLine() == null ) sock.close(); // receiving confirmation
}
try{
Thread.sleep(2000);
} catch(InterruptedException e){}
line = remoteElz.getReplytStr(line);
System.out.println("A> "+line);
out.println(line + "SERVER");
// to GUI server
if(in.readLine() == null) sock.close(); // receiving confirmation
i++;
}
out.println("END");
if(in.readLine() == null) sock.close(); // receiving confirmation
} catch (Exception e) {
e.printStackTrace();
}
}
} // end of file “RmiElizaClient2”
- 72 -
/* File: “ElizaClient.java”: the model part of GUI client for the Controller.
*/
import java.io.*;
import java.net.*;
public class ElizaClient {
ElizaClientGUI gui;
protected Socket sock;
PrintWriter out;
boolean connected = false;
boolean promptRecd = false;
static final boolean AUTO_FLUSH=true;
String type="";
String line="";
int scount=1;
int ccount=2;
public static void main(String[] args) {
System.getProperties().put("line.separator","\r\n");
ElizaClient client = new ElizaClient();
}
public ElizaClient() {
gui = new ElizaClientGUI(this);
gui.setSize(900,500);
gui.setTitle("Distributed Eliza");
gui.show();
}
public void connectToServer(String ip, int port) {
try{
//sock is bound to a port number where data to be sent
sock = new Socket(ip, port);
BufferedReader in = new BufferedReader(
new InputStreamReader(sock.getInputStream()));
out = new PrintWriter(sock.getOutputStream(), AUTO_FLUSH);
connected=true;
System.out.println("length "+type.length());
gui.setStatusDisplay( "Connected to Server: " + ip + ", " + port+".
Please Choose an Eliza C/S Type and Click Start.");
// thread continuously receiving
InputReader ir= new InputReader(this, in);
ir.start();
}catch(Exception e){
if(connected) disconnectFromServer();
gui.setStatusDisplay("Disconnected: "+e.toString());
connected=false;
}
}
synchronized public void disconnectFromServer() {
if( ! connected ) return;
try{
sock.close();
}catch(IOException e){ System.out.println(e.toString()); }
scount=1;
ccount=2;
gui.setStatusDisplay("Disconnected");
connected=false;
- 73 -
}
synchronized public void handleLineFromServer(String line) {
String sub=new String();
System.out.println(line);
if( line.length() >= 6 ) {
sub = line.substring(line.length()-6);
System.out.println(sub);
if( sub.equals("PROMPT") ) {
sub = line.substring(0, line.length()-6);
String temp="";
for(int i=0;i<sub.length();i++)
if(sub.charAt(i)=='~') temp+='\n';
else temp+=sub.charAt(i);
gui.clientTextAppend(temp);
gui.clientTextArea.requestFocus();
} else if( sub.equals("CLIENT") ) {
gui.clientTextAppend("Q" + ccount + "> " + line.substring(0,
line.length()-6) + "\n");
ccount ++;
} else if( sub.equals("SERVER") ) {
gui.serverTextAppend("A" + scount + "> " + line.substring(0,
line.length()-6) + "\n" );
scount ++;
}
}else {
scount=1;
ccount=2;
gui.setStatusDisplay("Session Ends. Please Choose an Eliza C/S Type and
click start button.");
}
}
public void eofOnSocket(Exception e){
if( e != null)
gui.setStatusDisplay("Exception: Socket disconnected.");
else
gui.setStatusDisplay("End of File: Disconnected");
}
}
/* class to handle receiving from server */
class InputReader extends Thread {
ElizaClient parent;
BufferedReader in;
IOException e=null;
InputReader(ElizaClient parent, BufferedReader in) {
this.parent=parent;
this.in=in;
}
public void run() {
//Reads from the given input stream, passing lines back to parent,until
//EOF or error/exception.
boolean eof=false;
try{
while( ! eof) {
String line=in.readLine();
if(line==null)
eof=true;
else
parent.handleLineFromServer(line);
- 74 -
}
parent.eofOnSocket(e);
}catch(IOException e){
parent.eofOnSocket(e);
}
parent.disconnectFromServer();
}
}
//end of file “ElizaClient.java”
/* File: “ElizaClientGUI.java”: the view part of GUI client.
*/
import java.awt.*;
import java.awt.event.*;
public class ElizaClientGUI extends Frame implements WindowListener,
ActionListener, KeyListener {
TextArea serverTextArea;
TextArea clientTextArea;
TextField ipField, portField, status;
Button connect, disconnect, start;
Choice mediaChooser = new Choice();
String type = "";
String input=" ";
ElizaClient parent;
ElizaClientGUI me;
Font f=new Font("Monospaced", Font.PLAIN, 12);
String ip;
int port;
public ElizaClientGUI(ElizaClient parent) {
this.parent = parent;
setLayout(new BorderLayout());
serverTextArea = new TextArea(50,60);
serverTextArea.setFont(f);
serverTextArea.setEditable(false);
clientTextArea = new TextArea(50,60);
clientTextArea.setFont(f);
clientTextArea.addKeyListener(this);
add(createClientPanel(), BorderLayout.EAST);
add(createServerPanel(), BorderLayout.WEST);
add(createStatusPanel(), BorderLayout.SOUTH);
add(createControlPanel(), BorderLayout.NORTH);
addWindowListener(this);
status.setFont(f);
status.setForeground(Color.red);
me = this;
}
Panel createServerPanel(){
Panel sp=new Panel(); //server panel
sp.setLayout(new BorderLayout());
Label label=new Label("Eliza Server Message:");
label.setForeground(Color.blue);
sp.add(label, BorderLayout.NORTH);
sp.add(serverTextArea, BorderLayout.CENTER);
return sp;
}
- 75 -
Panel createClientPanel(){
Panel cp=new Panel(); //client panel
cp.setLayout(new BorderLayout());
Label label=new Label("Eliza Client Message:");
label.setForeground(Color.blue);
cp.add(label, BorderLayout.NORTH);
cp.add(clientTextArea, BorderLayout.CENTER);
return cp;
}
Panel createControlPanel(){
Panel p=new Panel();
p.setLayout(new BorderLayout());
Panel p1=new Panel();
p1.setLayout( new BorderLayout() );
p1.add( new Label("IP Address:"), BorderLayout.WEST);
p1.add( ipField = new TextField("wedge.tcs.auckland.ac.nz"),
BorderLayout.CENTER );
Panel p3=new Panel();
p3.setLayout( new BorderLayout() );
p3.add( new Label("
Port:"), BorderLayout.WEST);
p3.add( portField = new TextField("5153",5), BorderLayout.CENTER);
p1.add( p3, BorderLayout.EAST);
p1.add( new Label(""), BorderLayout.SOUTH);
p1.add( new Label(""), BorderLayout.NORTH);
p.add( p1, BorderLayout.WEST);
Panel p4=new Panel();
p4.setLayout( new BorderLayout() );
p4.add(new Label("
Choose Eliza Client/Server Type:"),
BorderLayout.WEST);
mediaChooser.add("SOCKET");
mediaChooser.add("MQ");
mediaChooser.add("RMI");
p4.add(mediaChooser, BorderLayout.CENTER);
p.add( p4, BorderLayout.CENTER);
Panel p2=new Panel();
p2.setLayout( new GridLayout() );
connect = new Button("Connect");
connect.addActionListener(this);
p2.add( new Label("") );
p2.add( connect );
p2.add( new Label("") );
start = new Button("Start");
start.addActionListener(this);
p2.add( start );
p2.add( new Label("") );
disconnect = new Button("Disconnect");
disconnect.addActionListener(this);
p2.add( disconnect );
p2.add( new Label("") );
p.add( p2, BorderLayout.SOUTH);
p.setBackground(new Color(230,230,230));
return p;
}
Panel createStatusPanel(){
Panel p=new Panel();
Label label=new Label("Message:");
label.setForeground(Color.blue);
p.add( label, BorderLayout.WEST);
status= new TextField(80);
- 76 -
p.add(status, BorderLayout.CENTER);
return p;
}
public void setClientText(String line){
clientTextArea.setText(line);
}
public void clientTextAppend(String line){
clientTextArea.append(line);
}
public void serverTextAppend(String line){
serverTextArea.append(line);
}
void setStatusDisplay(String text){
status.setText(text);
}
public void actionPerformed(ActionEvent e){
if(e.getSource() == connect ){
if( parent.connected ) return;
setIpAndPort();
parent.connectToServer(ip, port);
} else if(e.getSource()==disconnect){
if( parent.connected )
parent.disconnectFromServer();
} else if( e.getSource() == start ) {
if( ! parent.connected ) {
setStatusDisplay("You have to Connect to the Server First!");
return;
}
String type= mediaChooser.getSelectedItem();
setStatusDisplay( "Connected to Server: " + ip + ", " + port+ ". Eliza
C/S Type: "+type);
clientTextArea.setEditable(true);
parent.out.println(type);
}
clearTextArea();
}
void setIpAndPort(){
if( (ipField.getText()).equals("") ) return;
ip=ipField.getText();
try {
port = Integer.parseInt(portField.getText());
}catch(Exception ex){ return; }
}
void clearTextArea(){
clientTextArea.setText("");
serverTextArea.setText("");
}
public void keyPressed(KeyEvent e) {
char c=e.getKeyChar();
if((int)c == 10) {
String str=new String("PROMPT"+input);
parent.out.println(str);
input="";
clientTextArea.append("\n");
- 77 -
clientTextArea.setEditable(false);
return;
}
input += String.valueOf(c);
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void windowClosing(WindowEvent e){
dispose();
System.exit(0);
}
public void windowDeiconified( WindowEvent evt ) {}
public void windowIconified( WindowEvent evt ) {}
public void windowActivated(WindowEvent evt) {}
public void windowDeactivated(WindowEvent evt) {}
public void windowOpened(WindowEvent evt) {}
public void windowClosed(WindowEvent evt) {}
} // end of file “ElizaClientGUI.java”
- 78 -