StrataFrame Forum

Creating a messaging/chat like server

http://forum.strataframe.net/Topic21310.aspx

By Greg McGuffey - 12/22/2008

I'd like to have a server app that provides a messaging service between the clients. There a few different sort of things I'd like to do:



- When certain fields are updated on a certain BO, it changes the application state. Rather than code to errors (because another user attempts something that was legal when it retrieved the state, but the state has now changed), I'd like to be able to just notify the clients so they can get the new state and adjust to it.



- For administration purposes, it would be good to know who is on, in real time. The way chat clients can show a list of friends online is what got me thinking about doing something chat like (though I don't intend to allow users to chat, just the client apps).



- For both administration and security purposes, I'd like to be able to kick off users, send out notifications etc. in real time. This would be used when the app is being upgraded, to make sure no one is on (and give them a 10 min warning). For security, in case we have a particularly naughty user, I could kick them off immediately.



I'm just a total newbie at doing anything like this. I've done a bit of research on using network streams, but don't know where to start. I'm also wondering how I'd do this if I were to use IIS and an ASPX page. I'm not sure that would work because I'd need to have a client talk to the server, which would broadcast to other clients.



Any help/starting tips would greatly appreciated!



Thanks!
By Edhy Rijo - 12/22/2008

Hi Greg,

Even though I have not experience doing this kind of application, it is all based on network Sockets where you will have a Server and multiple Clients to communicate via a TCP port to do all kind of things and have a lot of fun Tongue

This is one of the things I have on my list to do for my applications too, but don't know when I will ever start working on that BigGrin

I goggled ".net socket server" and got several links, here is one very simple.

http://www.eggheadcafe.com/articles/20020323.asp

Also there are some communications libraries like IP Works from www.nsoftware.com which easy this kind of thing, I have not used this library, but I have used other libraries from nSoftware and they are pretty easy and straight forward to work with.  Here you can see a bunch of demos http://www.nsoftware.com/ipworks/demos.aspx available to test their libraries, I would pay attention to these ones:

  • TCP Echo Client/Server
  • UDP Echo Client

I am sure there should also be a Client/Server TCP/Win Socket demo application somewhere using plain .NET.

By Edhy Rijo - 12/22/2008

Here is another one http://www.chilkatsoft.com/p/p_514.asp from the guys at Chilkat Software which also you can test with the demo in the link.
By Trent L. Taylor - 12/23/2008

Well, this is basically what we have done within our medical software.  We have a server that gets deployed with our medical software which does a number of things:

  • Manages concurrent user connections
  • Manages all connection strings for the clients.  This is done via UDP broadcasting so when the client starts up, it looks for the server using a unique GUID (just a unique key in case there is more than one server on the network).  Once found, the server hands the connection string information back to the client.  This prevents the client from ever having to manage or enter a connection string...this has been a very nice benefit.
  • Manages the licensing.  When the client starts up, it gets all of the licensing information from the server (i.e. certain products purchased, etc.)
  • Database management (i.e. backup and restoring of databases)
  • Automated tasks such as checking for past due labs or running reports defined by the user on a scheduled task or time.
  • Allows certain users to be kicked off by right-clicking the user and then saying "log-off."  The client then recognizes the log-off and the logically shuts down.

The screen that is shown above is the server console (not the server) that interacts with the server service.  The server should be running at all times since a lot of logic is wired into that.  The serer console is the mechanism to which the server service can be accessed for interaction.

So this begs the question, how do you do this and what approach should you take.  We took the approach of creating a listener on the server service (TCP socket).  The client communicates to the server on a poll and does not have a listener on the client side to reduce overhead on the client side.  The client's poll on a theaded timer periodically (about every 7-10 seconds) to the server.  

Based on the logic of HTTP response and request objects, we created a proprietary (to our medical software) response and request object that makes it easier in code to work with which then streams the request/response through the socket to the server/client.  This way you work with a request and response type of interface while the base class does all of the TCP work for you....this makes it much easier to work with in code and make changes or add new requests, etc.

So a request can be made from the client at any time and receive an immediate response from the server.  If you want the server to send the client a command (such as log-off) without the client initiating the request, then the server will queue the command and when the client checks in on the next poll (again, around 7-10 seconds) it will receive the queued command and shut down.  So it is basically like writing a web service (but it isn't) for a Windows service.

We have had great luck with this approach and the nice things is that is is far moire scalable that using direct TCP sockets.

By Greg McGuffey - 12/23/2008

Thanks for the links Edhy. The http://www.eggheadcafe.com/articles/20020323.asp link was helpful to understand some of what Trent suggested!



Trent, that was very helpful. This gets me stated in getting this done. One thing I'm unclear on is what the actual data is that is passed back and forth. All the examples I've seen (so far) are just encoding strings. Any recommendations on how to pass the actual data (which I'm assuming would all be handled by the request/response objects you suggested).



Again thanks. This is yet another example of why SF rocks! BigGrin
By Trent L. Taylor - 12/23/2008

Well, the data being passed back and forth would depend on how you create your request and response objects.  You could create a string with pipe delimmiters and then parse it on either side.  Or, you could use a BinaryWriter and BinaryRead to write the data to a byte array (kindof creating your own serialization) and then put it back to gether on the other side.  In either case, you could use a network stream to read and write to and from.

Probably the best way to go would be to use a BinaryFormater (serialization) to serialize the data and then pass it back and forth as a stream (BinaryRead, BinaryWriter).  If you do this, it would be the cleanest and easiest to expand as you move forward and add more and more logic to this server and client.

By Greg McGuffey - 12/23/2008

Thanks Trent. I think that gives me enough info to get started! BigGrin



Have a great Christmas!
By Edhy Rijo - 12/23/2008

You are welcome Greg, and please keep us informed with your progress in this project.

Trent, is the Server Service using a database to hold the status information or you are using an xml file?  Also I assume you can use SF to create the Server Console application, right?

By Trent L. Taylor - 12/23/2008

Trent, is the Server Service using a database to hold the status information or you are using an xml file?

Neither.  An internal collection with custom items.  If the service shuts down, the clients will bail out.  You don't want to store this type of information in any type of database.  If you do, then you will always fight sync and update problems.  What if the server dies, etc.  So this should all be real-time.

Also I assume you can use SF to create the Server Console application, right?

Yup...we did Smile

By Edhy Rijo - 2/6/2009

Hi Greg,

I am about to start a new SF project to handle the Server service features.  Did yo review any of the TCP libraries here that deals with communication between clients and server?

If you have any opnion you can share I will really appreciate since I have to have this done in a week or so.

Thanks!

By Greg McGuffey - 2/6/2009

I did some research/test projects on this, but got derailed. However, here is what my current thinking is:



- .NET has all you need to handle the network communication. A TCPListener/TCPClient I believe. That part seems pretty darned easy.

- Threading will be key, so if you aren't up to speed on threading (asynch especially), read up on that.

- Serialization is also key to how you pass data back and forth. You'd likely just use the binary serializer, but I didn't get that far to know.

- You need to know how to make a Service. Not rocket science and was pretty straight forward. I am using a book (which was pretty marginal, but I still learned the basics). Not sure I'd recommend it, but it is working for me.

http://www.amazon.com/Programming-Windows-Services-Microsoft-Visual/dp/073562433X/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1233971392&sr=8-1



This is of course just the underlying plumbing that allows the client to talk to the server. You'll need the class(s) that would actually manage the business rules. I think what trent was talking about is creating a class, especially within the client, that makes it very easy to handle the communication. This class would wrap the code that actually uses the TCPClient, bundles up any data, sends message and processes result.



Not much, but that's all I got so far...keep us updated!


By Russell Scott Brown - 2/12/2009

It looks like you have the makings of another product.  Have you thought about this?
By Edhy Rijo - 2/12/2009

Greg McGuffey (02/06/2009)
Not much, but that's all I got so far...keep us updated!

Hi Greg,

Sorry for the delayed reply.  Thanks for the info, you are right about .NET having all the tools needed to handle the communications, but sometimes 3rd party libraries will offer easier and faster approach to the solution.

Also thanks for the book recommendation, I will get it, since I have a lot to learn about Windows Services, I have done some work with Services with my VFP applications in the past, but for what I have read with VB.NET is much more easier since there is a special project to create a Service application.

I will keep up posted on my findings.  The more I think about this, the bigger this whole Windows Server/Console Application will be in order to manage a commercial application license and connections.

By Edhy Rijo - 2/16/2009

Hi Trent,

Trent L. Taylor (12/23/2008)
We took the approach of creating a listener on the server service (TCP socket).  The client communicates to the server on a poll and does not have a listener on the client side to reduce overhead on the client side.  The client's poll on a theaded timer periodically (about every 7-10 seconds) to the server.

Why will you go with the polling approach instead of keeping the TCP socket alive all the time for the client?

Also would the Console Server project be based on SF with Security?, so in other words would this program be using the SF security to allow only authenticated users to modify the Service? if so, this will mean that 2 projects (Server and Client) will be sharing the security database, right?

By Greg McGuffey - 2/16/2009

I don't know the definitive answer to why not keep the connection open, but I can take a guess based on my research thus far. I think the first reason would be that you need to consider that the connection might not be available all the time. The internet is not the most reliable, so it is almost a guaranty that you'll need to deal with the connection being broken. And you'd have to deal with this on both sides. I think there is a reason TCP/IP was designed to be a stateless connection.



As to the security question, again I can just offer my thoughts, but I wasn't planning on using security with the server. Using SF security seems like it might be hard. How would you structure the server such that the SecurityBasics.CurrentUser would be switched and available to multiple users? It's shared so there is only going to be one instance. I'm not sure it is thread safe. I was thinking of another method, which would be to have some sort of token identifying the client as legitimate that would identify it to the server (which might simply be the client license token...which your server will validate). Also, don't make any requests of the server that might need a logged in user until the user is logged in. Then you could just pass the user ID to the server, if the user ID needs to be tracked. I.e. do user validation on client side were you have easy access to permissions.



I'm probably missing something here, but those are my thoughts.



Thanks for the updates Edhy!
By Edhy Rijo - 2/16/2009

Greg McGuffey (02/16/2009)
...The internet is not the most reliable, so it is almost a guaranty that you'll need to deal with the connection being broken. And you'd have to deal with this on both sides. I think there is a reason TCP/IP was designed to be a stateless connection.!

Agree with your statement, but in my case I see the Server Service as a Licensing Manager in which the clients must or should be connected all the time in order to communicate with the database. Here are more details of my thinking:

  1. Server Console is a regular SF project which will use the Connection Manager to get the database connection info to be passed over the Client.
  2. Server Console will be protected by my security software to be properly licensed to the end user.  This has nothing to do with SF except that this program will be wrapped with this protection for me to control the licenses.
  3. Only clients under the same local network (Intranet) will be able to communicate with the Server to get the database connection string and other stuff in order to operate properly.  Of course the client is a regular SF application with modifications to connect to a Server first instead of the database.
  4. So my plan was to have the client connection alive during the client session, so if the connection with the Server is broken, then the connection with the database will also be broken providing a minute or two to the end user to save any pending changes back to the database.


I was thinking of another method, which would be to have some sort of token identifying the client as legitimate that would identify it to the server (which might simply be the client license token...which your server will validate).

Yes this is the case, my security/protection library will enforce the License on the Server instead of dealing with all possible Client workstations, so at this point the customer can have a Local Network with x number of workstations and I will need to worry about the Server Service and Server Console application to be properly protected and licensed. 

In regards to using the SF security, what I meant was that since the Server Console is also a SF application with access to the database, I can implement the SF security so only users with specific role can connect to the Server Console after it has been setup by the administrator or initial default user when installed.

Greg, in regards to your original question I found this sample at the nSoftware site: http://www.nsoftware.com/kb/tutorials/jabber.aspx I have not test it yet, but it looks pretty easy to implement.

This topic is getting better and better! Hehe

By Edhy Rijo - 3/2/2009

Greg McGuffey (02/06/2009)
...You need to know how to make a Service. Not rocket science and was pretty straight forward. I am using a book (which was pretty marginal, but I still learned the basics). Not sure I'd recommend it, but it is working for me.

Greg,

Just want to thank you on the recommendation of this book "Programming Windows Services with MS VB 2008" so far it has been pretty good to me in getting started with all the understanding about Windows Services.  Oh, btw, I got it new from Hightech Computer at amazon.com for $13.99 includind shipping charges.

By Greg McGuffey - 3/2/2009

Sweet! w00t



It's not a great book. Lots of omission and typos. But it did help me a lot understanding how to do a service!



Glad your project is moving right along! I'm jealous. I've been pulled in yet another direction for the time being Pinch
By Edhy Rijo - 3/10/2009

Trent, I have a question about the Service and Server Console applications.

I am doing the following, in the Service application I have a BO which will hold the list of all connected workstations and this BO will be updated OnConnect and OnDisconnect to add/remove the records, now in the Server Console application I have a list view in which I want to show the data in the Service BO, I am still not clear how to accomplish this, but here are some ideas:

  1. The Server Console will also connect to the Service application with a different parameter to let it know that it is the Server Console and the service will pass a serialized BO to the Server Console to show the connected clients in the listview. Still I don't know how to do the serialization process but guess it should not be hard.
  2. The Server Console can create an XML file with the connected clients and the Server Console can read this file, etc.  This approach could be more difficult to handle since I will need to have this XML file in an specific folder and also would need a timer to keep reading from each and it would be a lot of IO traffic.
  3. From the Service application I could create an Instance of the Server Console application, but that would be also a bit complicated.

So, from all 3 above I would go with option 1, but before I continue further could you or somebody else comment on this or any other recommendation to handle this?

Thanks!

By Trent L. Taylor - 3/11/2009

Yeah, based on your options there 1 would be best.  How you set this up will be very important in regards to scalability and growth within your application because it won't be long before you need to add some new functionality.

You will want to have a "service port" that is exposed on your service application to which the server console will connect to administrate and retrieve all information.  You can still use sockets on this, but for arguments sake, let assume that we will use port 20010 as the administration port on the service application.  When the server console fires up, it will communicated with the service on that point to obtain information...but it should not "crash" if the service is down.  In fact, if run on the same machine as the service, it should be able to start, stop, and restart he service from within the server console:

This just makes it easier for the end-user as well...especially in service call type of scenarios.  But through this port, you can do whatever you need...send serialized BOs back and forth, etc.  In all actuallity, I would probably not send a serialized BO back and forth in this case, but rather create a query type of situation where I send a request and a response, not unlike web services.  This way you can create a NetRequest class and a NetResponse class (or whatever you want to call them) that wrap this logic for you and know how to send and receive the NetStream data on both sides...so if you WANTED to send over a serialized BO you could.  For populating a list with the connected users or something that would probably be fine.  But these are just some ideas of how we designed our server service and console that might help.

By Greg McGuffey - 3/11/2009

Trent,



How does the server console figure out the status of the service? Also, how do you start/stop/pause the server from the server console?


By Trent L. Taylor - 3/11/2009

Using the ServiceController class.  It makes it really easy!

System.ServiceProcess.ServiceController
By Edhy Rijo - 3/11/2009

Trent,

Thanks for the info.  Now back to serialization, I am trying to use the SerializeToByteArray but it is not working for me, I have the feeling my code is not correct, here is  my approach:

' Serialize the BO into a ByteArray

Dim pszOutByte As System.Byte()

pszOutByte = Me.bizConnectedClients1.SerializeToByteArray

' DeSerialize the BO from a ByteArray

Dim loBO As New bizConnectedClients

BusinessLayer.DeserializeBusinessObject(e.TextB)

Me.BizConnectedClients1.CopyDataFrom(loBO, BusinessCloneDataType.ClearAndFillFromDefaultView)

loBO.Dispose()

In above code e.TextB contain the serialized BO byte stream. When testing this I get the following error when trying to Deserialize the BO:

SerializationException
  The input stream is not a valid binary format. The starting contents (in bytes) are: 34-31-36-32-32-34-36-33-0D-0A-54-65-73-74-20-4F-6E ...

Source     : System.Windows.Forms

Stack Trace:
   at System.Windows.Forms.Control.EndInvoke(IAsyncResult asyncResult)
   at nsoftware.IPWorks.Ipport.nsoftware.core.IIpportEvents.FireDataIn(Byte[] text, Boolean EOL)

What is it that I am missing here?

By Trent L. Taylor - 3/11/2009

I am not sure what e.TextB is, but assuming it is the serizlized byte array, you are not placing the deserialized BO into a variable:

loBo = BusinessLayer.DeserializeBusinessObject(e.TextB)

And you will need to type is as well.  So it would look more like this:

Dim loBO As bizConnectedClients
loBO = DirectCast(BusinessLayer.DeserializeBusinessObject(e.TextB), bizConnectedClients)

By Greg McGuffey - 3/11/2009

Trent L. Taylor (03/11/2009)
Using the ServiceController class. It makes it really easy!




Cool! I'll check that out.
By Edhy Rijo - 3/19/2009

Hi Trent/Greg,

Just a note to say thanks again.  So far I was able to create my version of a Server Console and a Server Service using the IP*Works library from www.nsoftware.com and the book for Windows Services suggested by Greg in this thread.

This has been a great experience working outside of just the database process, using secured XML files managed by the SF Business Object to store the Server Settings information and using a ListView with all the goodies of the SF BO to show connected clients without saving any data to a database, this whole process shows once again the power in the SF framework.

On a side note, I was having a lot of trouble with the IP*Works and sending the BO as a byte array, despite Trent's support replies I could not make it work to a satisfactory level, so I decided to simply use a predefined string which I then parsed and it worked just great.

Thanks guys! Tongue

By Greg McGuffey - 3/19/2009

Awesome Edhy!



I'm jonesing to work on a service like this myself...alas, no time at the moment. Crying



I'll be referring back to this thread when I do though!
By Edhy Rijo - 3/23/2009

Trent,



One more question, for the client do you allow the end user to type in the IP address of the Server to connect to or is there a way for the client to discover the Server?



In my case, I would like to keep the client locked down to the network segment they are installed on, this way a customer with different branches will not be able to connect to the Server Service from remote locations.



Any suggestion on how to handle this logistic situation? Hehe
By Trent L. Taylor - 3/24/2009

One more question, for the client do you allow the end user to type in the IP address of the Server to connect to or is there a way for the client to discover the Server?

We do allow it if the server cannot be automatically located.  We use a UDP boradcast from the client if it cannot connect t the server.  The server is listening on that UDP port and sends out a UDP broadcast of its location to which the client will retrieve the servers location and login.  You can find a lot of samples on the internet about this.  I did a quick serach and found some CodeProject samples.  Here is a chart sample that uses a UDP broadcast.

http://www.codeproject.com/KB/IP/socketsincs.aspx?fid=2956&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=1070795

By Edhy Rijo - 7/23/2009

Hi Trent,

I am trying to implement my version of Service Console to send the connection information to a SF application. In the AppMain.vb I need to communicate with the Service and get the connection, so far so good, I am doing this in the SetDataSources(), but if something is wrong and I need to close the application how do I do that?



I tried something like:



System.Windows.Forms.Application.Exit()

Exit sub





But the flow of the StrataFrameApplication.RunApplication() continues instead of exiting. Any recommendation on how to stop the application if I don't get the connection string from my Service?
By Trent L. Taylor - 7/24/2009

In VB you can just call "End":



End




But the Exit should have worked. I use this at times to provide an exit code so I can tie into other applications (or shells ) and take logic if a certain exit code occurs.
By Edhy Rijo - 7/24/2009

Thanks Trent,



"End" did the trick. I knew it should be something really simple BigGrin