Sample Server - Services

Now the server can accept remote connection, but it is still completely useless because there is nothing that can process these incoming requests.

In this chapter 2 services will be defined:

LoginService that will allow user to log in/log out.

ChatService that will allow to send messages from client to server.

Login Service

Add a new class named LoginService to the project:

using System; 
using RemObjects.SDK; 
using RemObjects.SDK.Server; 
 
namespace ChatServer 
{ 
    [Service] 
    public class LoginService : RemObjects.SDK.Server.Service 
    { 
        [ServiceMethod] 
        public bool LogIn([StreamAs(StreamingFormat.Utf8String)]string loginEx) 
        { 
            throw new NotImplementedException(); 
        } 
     
        [ServiceMethod] 
        public void LogOut() 
        { 
            throw new NotImplementedException(); 
        } 
    } 
} 

What happens here?

The LoginService class is inherited from the RemObjects.SDK.Server.Service class and is marked with the [Service] attribute.

This tells the Code-First infrastructure of the Remoting SDK that this class represents a service that should be published for remote access. The RemObjects.SDK.Server.Service base class provides a number of methods, properties and events required by the Remoting SDK infrastructure, like service activation and deactivation methods.

Then two public methods are defined that are marked with the [ServiceMethod] attribute. This means that these methods will be published for remote access.

Then a [StreamAs] attribute is applied to the String parameter of the LogIn method. Remoting SDK can send strings over the wire using several different encodings (UTF8, Unicode and ANSI). UTF8 is used by default, other two encodings might be used for backwards compatibility reasons.

Method definitions

public bool LogIn([StreamAs(StreamingFormat.Utf8String)]string loginEx)

and

public bool LogIn(string loginEx) 

are equal. There is no need to explicitly specify the [StreamAs] attribute if the UTF8 encoding should be used.

The purpose of this service is to authenticate the user, so let's add some code.

Note that the LogIn method accepts only one string parameter instead of more expected pair username/password. The approach to pass the login information in a single string like 'User ID=SYSDBA;Password=masterkey' (very similar to database connection strings) allows more flexibility later. More additional login parameters can be passed via this string without the need to change the published service interface.

[ServiceMethod] 
public bool LogIn([StreamAs(StreamingFormat.Utf8String)]string loginEx) 
{ 
    try 
    { 
        SemicolonSeparatedString login = new SemicolonSeparatedString(loginEx); 
     
        if (string.IsNullOrEmpty(login["User ID"]) || (login["User ID"] != login["Password"])) 
        { 
            this.DestroySession(); 
            return false; 
        } 
 
        this.Session["User ID"] = login["User ID"]; 
        this.Session["Login Time"] = DateTime.UtcNow; 
 
        return true; 
    } 
    catch (Exception) 
    { 
        this.DestroySession(); 
        throw; 
    } 
} 

The code above is rather simple. First the provided string is parsed into a set of key-value pairs. Then a check is performed to ensure that the user name was provided at all and that the provided user name is equal to the password provided. In a real-world application here would be a real password check. Then if the check is not passed the user session is destroyed and the method returns false to indicate that the login process was not successful. After that method initialized the user session by writing there some information and returns true.

Note: For performance reasons the session object is not initialized by the service instance until it is first accessed. So Sesssion should be accessed at least once to ensure that session manager will create and keep it.

The code is wrapped with try/catch clause to ensure that in the case of any errors occurred the user session will be deleted instead of possibly leaving it in inconsistent state.

The logout process is merely destroys the user session:

[ServiceMethod] 
public void LogOut() 
{ 
    this.DestroySession(); 
} 

Chat service

Add a new class ChatService to the project:

using RemObjects.SDK.Server; 
 
namespace ChatServer 
{ 
    [Service] 
    [ServiceRequiresLogin] 
    public class ChatService : Service 
    { 
        [ServiceMethod] 
        public void SendMessage(string message) 
        { 
            System.Diagnostics.Debug.WriteLine(Session["User ID"] + " - " + message); 
        } 
    } 
} 

It is quite primitive at this point. As we don't know how to relay the received chat message to all logged users we will just display it in the debug pane.

The only point of interest here is the [ServiceRequiresLogin] attribute applied to the service class. It ensures that upon activation the service will require that the session for the current user already exists.

This means that the client app will have to first authenticate via the LoginService before attempting to send any messages to the chat.

The next chapter will cover developing client application for this server.

The server code itself can be downloaded here.

Start the server app and move to the next chapter to create a simple client application.