Introduction
I have included some code that can make your windows
programs and your java programs communicate
with each other easily.� The emphasis is on the word easily,
because there could be a lot of different ways to do the same thing.�
On the windows side, I wrote an ActiveX
control called XYMessenger
.� On the java
side, I wrote a java class also named XYMessenger
.�
These two components have almost identical methods and they can send each
other messages of arbitrary size.� If the word XYMessenger
sounds familiar to you that is because I previously posted an article
on CodeProject about a client-server development tool package I wrote.�
The package is now free
(for commercial or non-commercial use) but the source code is not provided
to the public at this time.� The code provided here are simplified versions
of the key components in that package.� You can use and modify the
code anyway you wish if you acknowledge in your work that the original
idea is from me :-).
The Ideas
The basic idea is, if you insert the XYMessenger.ocx
control into your VB or MFC
program or other windows program that can
use an ActiveX control, then your program
can act as both client or server to other programs also using XYMessenger
.�
For your java programs, you need only to derive
a class from the XYMessenger
to achieve the
same goal.� A call to the ConnectDirect
method is all you need to join the communication.� The first program
to call ConnectDirect
will be acting as the
root node in a tree called the communication tree.�
The root node needs to use a local port for other programs to connect to.�
Other programs will connect to this root node using the ip address and
the port number (of the root node).� These programs will be the clients
to the root node, however, each of them can also be a server so that more
and more programs can be connected together.
What can you do once you connected your programs
together?� Well, the SendTextMsg
method allows you to send messages of arbitrary size to other connected
programs.� How do you know who to send the message
to?� Each node in the communication tree is identified by a
user identifier of the form Name@XXX,
where Name is the login name used in the call
to ConnectDirect
and XXX
is a digit string dynamically generated by the system to identify the position
of the node in the communication tree.�
The GetUserCount
and GetUser
methods can be used to retrieve user identifiers of all nodes.� For
example, GetUser(0)
will return the
user identifier of the first user in the internal user list, GetUser(-1)
will return the user identifier of the current node.� GetParent
will return the user identifier of the parent node of the current node.
What does a message look like?� A message
has the following attributes:� Sender
(user identifier), Receiver (user identifier),
MsgID (integer), SenderMsgID
(integer), Title, Text,
and AppSpecificCode (integer).� The Sender,
Receiver, Title,
and Text fields are null terminated character
strings in windows programs, and they are
byte arrays in java programs.
Where are incoming messages stored and how to
retrieve and process them?� The incoming messages are stored
in an internal message array.� For windows
programs, an ActiveX event will be fired when
a new message arrives, the developer is supposed to implement an event
handler and process the message there.� For java
programs, the developer is supposed to override the virtual message handler
function in the base class XYMessenger
to
process the message.� In both cases the message handlers will be invoked
by the system automatically.� The GetMsgCount
method returns the total number of messages in the internal message
array.� There is an nMsgID parameter
in the message handler, you can use it to call the GetMsgIndex
method to obtain an index of the message.� This returned index can
be used to call GetMstTitle
, GetMsgText
,
GetMsgAppSpecificCode
, etc.� The RemoveMsg
method destroys the message with given index.
The SendSyncTextMsg
method is used to send
a synchronous message to a node.� It will not return until the receiver
has sent a reply message or time-out occurs.� You can specify the
time-out value (in seconds) with this method.� The method returns
a positive integer which can be used to call GetMsgIndexWithSenderMsgID
to obtain the index of the reply message.� If the receiver does not
reply or the method fails, then the return value will be -1.�
By the way, the message sent by SendSyncTextMsg
has to be replied by calling the ReplySyncTextMsg
method using the nSenderMsgID parameter
of the original message as the first parameter in the call. Please also note that the user implemented message handler will be called before
SendSyncTextMsg
returns. If you delete all incoming messages from within the handler,
SendSyncTextMsg
will return -1 as if the receiver never replied to the sent message.
What if the data I want to send contains embedded
null characters (such a .exe file)?� Well, there is never a
problem if you only use the java component.�
For consistency, I wrote two functions, ReplaceNull
and RestoreNull
in both java
and c++, that can be used to remove null characters
from data before sending the message and restore the null characters after
receiving the message.� The java version
is in class XYAPI, the c++
version is in file XYStatic.cpp.
I am very confident that the code and the basic ideas can be very useful
in general client-server programming, not just being a bridge between java
and windows programs.� In fact, I have
built pretty complicated server programs and have used XYMessenger
in both web clients (embedding XYMessenger
in applet) and web servers easily.� Please check my
web page for details.
How To Use The Sample Programs
For your convenience I have included the binary code in the zip file
(the code size is extremely small).� Here are the steps to install:
-
Unzip the downloaded file.
-
Register XYMessenger.ocx
-
Include the full path of the XYMessenger.jar
file into your
CLASSPATH
environment.
The java sample program is implemented in
the class
XYTester
.� This program tries
to send a message to each connected node and then broadcast a message to
all nodes every 3 seconds.� You can run multiple instances of this
program.� The command line to start the program is:
java XYTester ServerAddress ServerPort LocalPort
Note that if ServerPort is 0,
then the instance will be the root node.� If LocalPort
is 0, then the instance can only be
a client (not accepting any connections).� Here is an example on using
this program:
-
First start the root node with the following command:�
java�
XYTester� ""� 0� 5000
-
Then start one or more clients with the command:�
java�
XYTester� ""� 5000� 0
-
Then start another client which is also a server with the command:�
java� XYTester� ""� 5000�
5001
-
Then start one or more clients of the above client with the command:�
java� XYTester ""� 5001� 0
Note that if the instances of XYTester are
running on different machines, then the
""
string should be replaced by server address (the name of the server machine
or its ip address).
The windows sample program is called XYTreeDemo
.�
It is a dialog based program.� Like XYTester
,
you can use multiple instances of XYTreeDemo
to build the communication tree.� However,
this program has the capability of building multiple communication
trees within a single process.� If you start the program and
click the Create Node button, then a separate
user interface thread will be created with an instance of XYMessenger
embedded in a new dialog.� The Connect
button on the new dialog will call the ConnectDirect
method to create a new node in a communication
tree.� This new node can be a client or a server (or both).�
After successfully connected, the Send button
will, of course, send a message to another node.� You can click the
Create Node button on the main dialog to create
as many nodes as possible.
Now the fun part, see what happens if you connect an instance of XYTreeDemo
to an instance of XYTester
, or connect an
instance of XYTester
to an instance of XYTreeDemo
.
The Implementation
The ActiveX control XYMessenger.ocx
is implemented with VC++ 5.0 using the CSocket
class of MFC.� The java
class XYMessenger
is implemented with JDK
1.2.2 using the Socket and ServerSocket
classes of java.net.
Each instance of XYMessenger.ocx can only
be used in a single thread (apartment model threading).� As demonstrated
by the XYTreeDemo program, you can use multiple
instances of XYMessenger.ocx in multiple threads.�
There is nothing to prevent you from using multiple instances of XYMessenger.ocx
in a single thread.� Even if an instance of XYMessenger.ocx
is waiting for a reply message from another node, the thread is not blocked,
it can still process incoming messages from other nodes.
The java class XYMessenger
is truly multi-threaded in the sense that each connection (between two
nodes) is running in a separate thread.� Therefore, the user implemented
event handlers (virtual functions) have to be thread-safe even if you use
only one instance of XYMessenger
.
The Limitations
The simplified code provided here has a hard coded limit of connecting
at most 10 clients to a server (however, each of the 10 clients can have
10 clients connected to it).� It is not hard to increase the limit
to, say 1000.� Another limitation is, each node in the communication
tree maintains a list of user identifiers of all nodes.� This
can be inefficient if there are hundreds of nodes connected together.�
The free package on my web
page does not have these limitations, of course, and it has a lot more
functionality besides simply connecting programs together.
Thank you for reading this article.� Your input is appreciated.
Updates
14 June 2002
- Fixed a bug in both the java and the ocx version of XYMessenger.
- Modified code so that XYMessenger.ocx can now be used in console applications.
- Added a simple console demo program.
- The unicode version of XYMessenger.ocx is fixed. But it is not yet compatible with the java XYMessenger class.