This guide explains how a developer would integrate the HOOPS/Net Server within their application to add collaborative and/or client-server functionality. The server is completely independent of both the HOOPS Graphics Subsystem and the Application Framework, thus, it can be integrated into an existing application without requiring any additional technology components from TSA.
The HOOPS/Net Server consists of a client side library, and an executable which resides on your server.
The HOOPS/Net client export library (for Windows) and libraries/dlls/executable are located in the /dev_tools/hoops_net subdirectories.
In this release TSA supports both the HTTP and TCP/IP Network protocols. In the case of HTTP we do not provide a web server to broker but instead rely on a separate web server to send messages through to our server.
The server is distributed in the form of an executable. To launch the server you should simply run the hnet_server executable located in the /dev_tools/hoops_net/bin directory of your HOOPS/3dAF installation. On startup the server reads that hnet_server.conf file for initial server settings. Within this file you can set initial settings such as the administrator's password and the port on which the server should listen for initial client connections.
The hnet_server.conf file is an ASCII text file within which the administrator sets up the initial server configurations . The different options are:
server_name = <string>: Since multiple servers can be run simultaneously on the same machine, you can name each server differently.
server_admin_password = <string>: This is the password which must be provided for administrator access to that particular server.
server_access_password = <string>: This is the password which must be provided for client to connect/access to that particular server.
server_debug_log_file_name = <stderr/stdout/null/string>: This is the output device/filename for the server log
server_file_sessions_enabled = <yes/no> : When this flag is set the file sessions hosting capabilities of server be enabled.
server_file_sessions_directory = <string> : Directory path where all the file sessions reside. Each subdirectory under this directory indicates a file session.
server_file_sessions = <string>: A space separated list of all the file sessions. For each entry in this list, a corresponding directory and configuration file must exist under server_file_sessions_directory.
server_port = <string>: The port on which the server listens for initial client connections. Note, after a connection is made the server opens and maintains a separate connection for that client over a separate port.
control_token_policy = <none/one/fair>: This flag governs the control token queue. Once a user requests control they cannot then remove their name from the control queue unless they quit the session. The meaning of the different control policy flags are:
none: Server does not keep track of requests for control. Clients can only get control when nobody else has control
one: Server keeps track of the first person to request control. All other request for control are ignored.
fair: Server keeps an ordered list of the people as they request control. Control is passed to clients in the order in which they are in the list. No one client can have more than one entry in the list.
log_standard_messages_enabled = <yes/no> : When this flag is set the server will log all messages into a message log file.
log_standard_message_file_location = <string> : Specifies the location the message log file should be written to.
log_unique_standard_messages = <string>: A space separated list of all the messages which should only have their last entry stored in the message log. Camera position entries would be a typical example of a unique standard message entry.
log_never_standard_messages message names = <string>: A space separated list of all the messages which should not be put into the message log
log_reset_standard_messages= <string>: The message identifier which forces all entries in the message log to be cleared out. This might be needed when there is a new file load.
session_inactivity_timeout = <string>: Time, in seconds, of inactivity that causes the session to shutdown.
client_inactivity_timeout = <string>: Time, in seconds, of inactivity of a certain user before they are expelled from a session.
Below is an example of a hnet_server.conf file:
# strings must be quoted if longer that one word
server_name = "MCAD Server"
server_admin_password = "rOMULUS9"
server_port = "11111"
control_token_queue_policy = "fair"
log_standard_messages_enabled = "yes"
log_standard_message_file_location = "./logs"
log_unique_standard_messages = "H_SET_CAMERA H_SET_RENDERMODE H_SET_MODELLING_MATRIX H_SET_WINDOW_COLOR"
log_never_standard_messages = "H_DEV_NULL_DATA"
server_file_sessions_enabled= "yes"
server_file_sessions_directory = "file_sessions"
server_file_sessions = "tsa_files"
session_inactivity_timeout = "3600"
client_inactivity_timeout = "0"
The <file_session_name>.conf file is an ASCII text file within which the administrator sets up the initial configurations for the given file session. The different options are:
name = <string>: The name of the file session. This must match with the one listed in hnet_server.conf under server_file_sessions.
password = <string>: This is the password which must be provided for client to access to that particular file session.
file_list = <string>: A space separated list of all the files in this file session. For each entry in this list, a corresponding file must exist.
Below is an example of a tsa_files.conf file:
# strings must be quoted if longer that one word
name = "tsa_files"
password = "SimSim8"
file_list = "foo.hsf bar.hsf"
After the configuration file has been initialized with the appropriate values and you have launched the server, any application with access to the server is able to connect to the server and start a session. The work of creating and managing all active sessions from a single application is done via the HNet class. To create or join a session on the server you must first create a HNet object within your application and then connect this HNet object to an active server. In most applications you will only have one HNet object.
Let's see what's involved in creating a HNet object, connecting it to an active server and creating a session:
// first let's create a HNet object HNet * m_pServerConnection = new HNet(); char *ServerIP = "192.168.1.224"; char *ServerPort = "11111"; char *ServerPassword = "BushwickBill"; // now let's connect this HNet object to our active server // Note: a connection will not be made unless both the password // and server port match what was in the hnet_server.conf file if(m_pServerConnection->ConnectToServer(ServerIP, ServerPort, ServerPassword)) m_bServerConnection = true; // Once a valid HNet object has been created and connected to the server // we then use it to create a session. Other clients can then connect to // that session on the server. Let's create a session via our HNet object char *SessionName = "MCAD"; char *SessionPassword = "Detr0it"; char *ServerPassword = "BushwickBill"; if(m_pServerConnection->CreateSession(SessionName, SessionPassword, ServerPassword)) m_bSessionCreated = true;
At this point a session identified by "MCAD" is now running on the server and anybody else who can connect to the server is able to joins the session.
Via HNet::SetStatusNoticeFunction the user can register a function which the server will call when there is a change in the Server's connection status. There are three different connection states HNET_STATUS_LINK_DOWN, HNET_STATUS_LINK_UP and HNET_STATUS_UNCONNECTED. Below is an example of the type of function you may register:
// here is where the function is registered m_pHNet->SetStatusNoticeFunction(hnet_status_notice_function, (void*)this); // the registered function void HQNetworkDlg::hnet_status_notice_function(unsigned int status,void * user_data) { HQNetworkDlg * self = (HQNetworkDlg *) user_data; if(status == HNET_STATUS_LINK_DOWN) QMessageBox::information( self, "qthoopsrefapp\n", "Connection to server down"); if(status == HNET_STATUS_LINK_UP) QMessageBox::information( self, "qthoopsrefapp\n", "Connection to server up"); if(status == HNET_STATUS_UNCONNECTED) QMessageBox::information( self, "qthoopsrefapp\n", "Server unconnected"); }
In a similar fashion HNet::EnumerateServerSessions allows you to register a function which will be called with the session name of every active session on the server.
The above example shows a way to connect to an active Server using TCP/IP connection. If you wish to connect to the server using HTTP connection, you should can use the following example as a reference:
// first let's create a HNet object m_pServerConnection = new HNet(); // Note the difference in ServerIP and ServerPort char *ServerIP = "http://www.foobar.com:80"; char *ServerPort = "/scripts/hnet_connect.cgi"; // If you need a proxy server // ServerIP = "http://www.foobar.com:80 proxy://my.proxy.com:3112" char *ServerPassword = "BushwickBill"; // now let's connect this HNet object to our active server // Note: a connection will not be made unless both the password // and server port match what was in the hnet_server.conf file if(m_pServerConnection->ConnectToServer( ServerIP, ServerPort, ServerPassword)) m_bServerConnection = true;
Before the connection gets established, the client might be required to enter the username and password to get accross the proxy server. Via HNet::SetUserPasswordFunction the user can register a function which the server will call when there is a request from the proxy server for the client to enter the user and password.
// here is where the function is registered m_pHNet->SetUserPasswordFunction(hnet_user_password_function, (void*)this); // the registered function void HQNetworkDlg::hnet_user_password_function(const char * realm, void * opaque_data, void * user_data) { HQNetworkDlg * self = (HQNetworkDlg *) user_data; HQUserPasswordPrompt * prompt = new HQUserPasswordPrompt(self, realm, "qthoopsrefapp"); prompt->exec(); if(prompt->result() == QDialog::Accepted) m_pHNet->ReportUserPassword(prompt->user_field->text().latin1(), prompt->password_field->text().latin1(), opaque_data); delete prompt; }
Windows Users: If you wish your HTTP connection to use your System's Internet Settings simply prepend the Server string with 'wininet'. In the example above this would make the ServerIP string be 'wininet http://www.foobar.com:80'.
It will be discussed further below but as you create clients and these clients start sending messages you use HNet::Run to transmit and receive these messages to and from the server. HNetClient::Send does not actually send messages rather it buffers all outgoing messages and when HNet::Run is called all outgoing messages are sent out and any messages on the server for that specific client are transmitted down from the server. In most applications users will create a separate timer event which calls the HNet::Run method approximately 30 times a second for TCP/IP connections and approximately 5 times a second for HTTP connections. Note, if your HNet object's Run method is never called then you will never be able to send or receive messages from the server.
The HNetClient class is used for the majority of server communication within a session. In most cases the majority of data associated with a session will be graphical information and so developers will often associate a window with each client. However, as the sever is completely independent of the data it's transmitting, this is not a necessary requirement.
To create a client and connect it to an active session you again use your HNet object. Once a client is created we block it from sending any messages until it registers its message handlers. Messages will be discussed in greater detail below but immediately after creating the HNetClient object we must register its message handlers so that any messages it receives will be routed to the appropriate functions in the application. The HOOPS/MVO HNetMessageRegistrar class provides the HNetMessageRegistrar::SetHandlers method which does the work of registering all the message handlers for HOOPS/MVO objects. Once we have registered this client's message handlers it is ready to receive messages and thus message blocking is turned off:
char ClientName[] = "Sean"; HNetClient * client = m_pHNet->JoinSession(SessionName, SessionPassword, ClientName); // HNetMessageRegistrar is an MVO class. If you are not using MVO // would create an analogous class in your application which does // the work of registering all the message handlers. HNetMessageRegistrar * pMsgRegistrar = new HNetMessageRegistrar(m_pHView, m_pHNetClient); HNetMsgRegistrar->SetHandlers(); // turn off message blocking client->BlockMessageProcessing(false);
Once a client is has registered its message handlers and is no longer blocking messages messages generated by any other members of the sessions will be automatically received and routed to the appropriate functions in the client's application. If log_standard_messages is enabled the client will initially receive all the registered messages transmitted since the session initiated. Note, it is the sole responsibility of the client to encode, decode and interpret all messages .
Messages will be discussed in greater detail below but for non-chat messages only one member of the session can transmit messages at any one time. For a specific member to send messages they must first take control of the session. A client requests control by calling HNetClient::RequestControlToken and if nobody else holds the control token the client will automatically receive control. If somebody else has control and the control token policy is one or fair the user will be put into a queue waiting for the current master to release control. The current holder of control releases control via HNetClient::ReleaseControltoken.
To facilitate control token notification notification requests HNetClient::SetControlTokenNoticeFunction is provided. This allows a client to register a function which will be called anytime a member of the session requests control. This can be used to tell the current master that someone has requested control. Here is an example of the control notice function that we use in our Reference Application.
void HQNetworkClientDlg::control_token_notice_function(bool has_control, void * user_data) { // this is our Network dialog HQNetworkClientDlg * self = (HQNetworkClientDlg*) user_data; // if we have control pop a dialog box asking for user to release control if(self->m_pHNetClient->HasContolToken()) QMessageBox::information( self, "qthoopsrefapp\n", "A request was made for you to release control"); else { if(has_control) self->control_button->setText("Release Control"); else self->control_button->setText("Request Control"); self->EmitControlChanged(has_control); } }
Once you are connected to a session and you have the ability to pass control between members of the session you are now ready to send and receive messages from the HOOPS/Net Server.
HOOPS/Net server can be used as a file server. The first step is to have the configuration file setup so that it has server_file_sessions_enabled = yes and appropriate File Session Configuration file in place. For more details, please refer back to Chapter 2. of this programming guide.
Here's how you can access the list of directories on the server.
The server calls the enumerate_dir_helper function for each of server sessions including the directories.m_pServerConnection->EnumerateServerSessions(enumerate_dir_helper, (void*)m_pServerConnection);
void enumerate_dir_helper(const char * name, void * user_data) { HNet * pServerConnection = (HNet *) user_data; if (pServerConnection->IsDirectory(name)) directory_list_box->AddString(name); }
The controlling client can now associate a directory with the active session.
Note that the client making this call must in control and must also provide the password to access the directory. This password is set in the File Session Configuration file.bool ret = m_pHNetClient->SetSessionDirectory(directory_name, directory_pwd);
Having the directory associated with the session, you might want to have the files in the directory listed. The following example shows how to access the list of files that are associated with the directory.
void HQNetworkClientDlg::OnRefreshFiles() { file_list_box->clear(); selected_file_label->setText(QString("")); m_pHNetClient->EnumerateSessionProperty("files", enumerate_file_helper, (void*)this->file_list_box); }
enumerate_file_helper is a callback function which will be called by the server once for each file in the session with the file details. Following is the sample implementation of such a callback function which you would be required to implement.
static void enumerate_file_helper(const char* property, const char* value, void* user_data) { char name[4096]; sscanf(value,"name=%s", name); ((QListBox *)user_data)->insertItem(QString(name)); }
A client would connect to the server and join a particular session in the regular way. Then it would associate the active session with a directory as described in previous sections.Once the session has an associated directory, the client can request file data using HNet apis. The files in the directory can be of any format. The server provides capability for clients to randomly access these files.
Before the client starts sending data requests to the server, it needs to register a callback function with the server. Upon the client's request for file data, the server will call this function with the requested data and other details. Following is a sample implementation of the file data input callback function.
void HQNetworkClientDlg::file_data_input_function(const char* message, unsigned int message_length, unsigned int file_data_id, void * user_data) { HQNetworkClientDlg * self = (HQNetworkClientDlg*)user_data; QMessageBox::information( self, "qthoopsrefapp\n", "File data recieved from the server"); }
The client must now register the above function with the server as shown below.
m_pHNetClient->SetFileDataInputFunction(file_data_input_function, (void *)your_data);
The client is now ready to randomly access any file in the file session. Following is an example to access 1000 bytes beginging at offset of 25 from start of the file "foo.hsf"
m_pHNetClient->SendFileDataRequest("foo.hsf", 0, 25, 1000);
Once the data request is sent to the server, the client will start receiving data by server calls to the registered file data input function.
There are primarily two classes of messages that are sent between clients involved in HOOPS/Net Server session. The first are simple text messages that can be sent between members of a server session, these can be sent at any point, by anybody and to anybody regardless of who currently has the control token. The second are the messages which conform to the master/slave rule and can be only sent by the current holder of the control token. Everybody connected to the session receives these messages.
These messages can be sent on a one-to-one basis or a one-to-many basis and can also be sent at any time, by anybody regardless of who has the control token.
Sending one of these text messages is done by using the HNetClient::SendPointCast :
char *PeerList = "Sean Sandra Peter Ivan", char *MessageBuffer = "This is not the correct model"; m_pHNetClient->SendPointCast(PeerList, MessageBuffer);
The PeerList is a space separated list of all the people in the session as identified by the UserName they used when they joined the session. When received the message is pre-pended with the Username of the person that sent the message. There is an additional HNetClient::SendBroadcast utility function which sends a text message to everybody in the session, using this avoids the need to provide a PeerList.
The second kind of messages are messages which can be sent by the current master of the session. The messages are pre-pended by a message identifier which is used to identify and register that message with the server. Messages can consist of either binary or ASCII data however they must all begin with an ASCII message identifier which is separated from the body of the message by a space. The following are examples of messages which are used in the our Reference Applications:
H_SET_WINDOW_COLOR 0.5 0.5 0.5
This corresponds to a message to set the window color of the viewing client to an RGB value of (0.5,0.5,0.5)
H_INSERT_HSF_DATA <......binary data.......>
After constructing a message the HNetClient object you call different versions of HNetClient::Send depending on whether you are sending binary or ASCII data. When you are sending binary data the client must tell the server the length of the binary data it wants to transmit. The HOOPS/Net Server will then use this length to binary transmit that many bytes of data. When you don't provide a length in the Send method the toolkit will assume that the data is a null terminated string.
Here is an example of an ASCII and a binary message. The first is sending a new camera position to members of a session while the second is sending a HOOPS Stream File (HSF) which contains 3D data to members of the session.
// firstly here is the function which sends the message void emit_message_function(const char * message, unsigned int length, void * user_data) { MyHQWidget * self = (MyHQWidget*)user_data; if(!length) self->m_pHNetClient->Send(message); else self->m_pHNetClient->Send(message, length); } // this function queries the database for the current camera setting // and then creates a valid HOOPS/Net message containing the information void HBaseView::SetCamera() { char message[4096]; HPoint position, target, up; float width, height; char projection[32]; // query our gfx database for the current camera settings HC_Open_Segment_By_Key(GetSceneKey()); HC_Show_Camera(&position, &target, &up, &width, &height, projection); HC_Close_Segment(); sprintf(message, "H_SET_CAMERA %f %f %f %f %f %f %f %f %f %f %f %s", position.x, position.y, position.z, target.x, target.y, target.z, up.x, up.y, up.z, width, height, projection); emit_message_function((const char *)message, 0, emit_message_function_user_data); } // this function adds the header to the binary file data as well as // the location in the segment tree it needs to be placed void HBaseView::EmitHSFData(const char *segment, const char *in_data, unsigned int data_length) { if (emit_message_function) { char encoded_segment_name[4096]; // we need to URI Encode the node location name so that it does // not include any special characters (particularly spaces) HUtility::URIencode(segment, strlen(segment), encoded_segment_name); int length = strlen(encoded_segment_name) + 32 + data_length; char *message; message = new char [length]; sprintf(message, "H_INSERT_HSF_DATA %s ", encoded_segment_name); int header_length = strlen(message); memcpy(&message[header_length], in_data, data_length); emit_message_function((const char *)message, data_length+header_length, emit_message_function_user_data); delete []message; } }
Note that the emit_message_function_user_data is the function which is used to register which client is currently posting messages.
Before we can send the messages we first need to register the messages with the server so that it can send the message body to the appropriate function in the client. You do this by using HNetClient::SetHandler.
void SetMessageHandler(HNetMessageHandler * handler, const char * msg)
When the server receives a message it removes the identifier and passes the body of the message to the HNetMessageHandler::ProcessMessage function. The ProcessMessage function then sends this data to the appropriate functions within your application. Since at this point you are directing messages towards application specific functions you will see references to both HOOPS/MVO and HOOPS/3dGS in the code example below. Though both help considerably with building an application neither is required to use the HOOPS/Net Server.
So let's create a message handler which deals with a H_SET_CAMERA message as is outlined above.
// first let's create a Camera Message Handler which will map the data in a // H_SET_CAMERA message to our SetCameraFromMessage function class SetCameraMessageHandler: public HNetMessageHandler { public: SetCameraMessageHandler(HBaseView * hbaseview){ m_pHBaseView = hbaseview; }; void ProcessMessage( const char * message_type, const char * data, unsigned int data_length) { m_pHBaseView->SetCameraFromMessage(data, data_length); m_pHBaseView->Update(); }; private: HBaseView * m_pHBaseView; }; // now let's register our Camera Message Handler with the server so that messages // identified with H_SET_CAMERA will be directed to our Camera Message Handler client->SetMessageHandler(new SetCameraMessageHandler(hbaseview), "H_SET_CAMERA"); // here is the function within our application which can // receive and interpret the data from a camera message void HBaseView::SetCameraFromMessage() { char data[8192]; HPoint position, target, up; float width, height; char projection[4096]; memcpy(data, in_data, data_length); data[data_length] = '\0'; sscanf(data, "%f %f %f %f %f %f %f %f %f %f %f %s", &position.x, &position.y, &position.z, &target.x, &target.y, &target.z, &up.x, &up.y, &up.z, &width, &height, projection); HC_Open_Segment_By_Key(GetSceneKey()); HC_Set_Camera(&position, &target, &up, width, height, projection); HC_Close_Segment(); }
To facilitate the registration of multiple message handlers a HNetMessageRegistrar class is included in the the HOOPS/Net Server source files. This is not used directly by the server but rather by the HOOPS/MVO class library to register some of MVO's standard messages. The HNetMessageRegistrar has two member variables which are the HBaseView object and it's associated HNetClient and one virtual method SetHandlers() within which all pre-created MVO messages are registered. If you are using the HOOPS/3dAF it may be easiest for you to derive from our HNetMessageRegistrar class and register all your handlers through that one helper class.
In many cases the user will want to transmit information pertaining to changes in a graphics scene graph without requiring every client in the session to have the tools /libraries available to recreate the operations. Examples of this would include where users are involved in a collaborative modeling session where one user is modifying a CAD model and other people in the session are viewing the session through a lightweight application which does not have a modular present. Or, maybe it's not a collaborative session at all, and the user has a lightweight application which is simply used to view the analysis results from a back end server. To enable this kind of functionality we use a combination of the HOOPS/Net Server to transmit lightweight facetted data to multiple clients in a session and the HOOPS/Stream toolkit to dynamically compress modifications to an existing 2D or 3D scene graph. The HOOPS/Stream toolkit is extremely powerful as it is a compressed, stream enabled form of a HOOPS/3dGS scene graph. The Stream Toolkit in conjunction with the core graphics subsystem has persistence entities and so it is relatively easy to allow users on disparate systems to select and highlight entities across sessions.
Let's firstly look at the example of how we would load a file into a client and how that file would then be transmitted to other clients in the session.
// let's read and transmit the data in 8K buffers #define HSF_BUFFER_CHUNK 8192 void HMyView::Load(const char *filename) { char buffer[HSF_BUFFER_CHUNK]; int length; FILE *fp; fp = fopen(filename, "rb"); // Open the node in the HOOPS/3dGS scene graph where // the file should be read in to char *segment = m_pHView->GetModel()->GetModelSegmentName(); HC_Open_Segment(segment); while (!feof(fp)) { length = fread(buffer,1,HSF_BUFFER_CHUNK,fp); // true indicates whether or not you want the // HSF data to be transmitted to other members of // the session m_pHView->InsertHSFData(segment, buffer, length, true); } HC_Close_Segment(); m_pHView->Update(); } void HBaseView::InsertHSFData(const char *segment, const char *in_data, unsigned int data_length, bool emit_message) { if (emit_message) EmitHSFData(segment,in_data,data_length); // this does the work of putting the geometry into the // controlling client's scene graph HStreamFileToolkit *tk = GetModel()->GetStreamFileTK(); HC_Open_Segment_By_Key(GetModel()->GetModelKey()); HC_Open_Segment(segment); TK_Status status = tk->ParseBuffer(in_data, data_length); HC_Close_Segment(); HC_Close_Segment(); } void HBaseView::EmitHSFData(const char *segment, const char *in_data, unsigned int data_length) { if(emit_message_function) { // first we need to encode the segment name so that it // will contain no blanks or special characters that may // cause problems for the sscanf char encoded_segment_name[4096]; HUtility::URIencode(segment, strlen(segment), encoded_segment_name); int length = strlen(encoded_segment_name) + 32 + data_length; char *message; message = new char [length]; sprintf(message, "H_INSERT_HSF_DATA %s ", encoded_segment_name); // append the message with the data from the HSF int header_length = strlen(message); memcpy(&message[header_length], in_data, data_length); // now send the data with a specific lenght. By doing this the // client and server will not assume that it is a null terminated // string message and consequently do a binary transmission of the // message body emit_message_function((const char *)message, data_length+header_length, emit_message_function_user_data); delete []message; } }
Now H_INSERT_HSF_DATA messages are mapped to HBaseView::InsertHsfDataFromMessage which looks like this:
void HBaseView::InsertHSFDataFromMessage(const char *in_data, unsigned int data_length) { char *data; data = new char [data_length+1]; char segment[2048]; char encoded_segment_name[4096]; char *encoded_hsf_data; char *decoded_hsf_data; encoded_hsf_data = new char [data_length]; decoded_hsf_data = new char [data_length]; // first let's copy the data locally memcpy(data, in_data, data_length); data[data_length] = '\0'; // now extract and decode the segment name where the // data should reside in the clients scene graph sscanf(data, "%s", encoded_segment_name); unsigned long n; unsigned long seg_length = strlen(encoded_segment_name); unsigned long hsf_data_length = data_length - seg_length - 1; HUtility::URIdecode(encoded_segment_name, segment, &n); segment[n] = '\0'; // now open the node in the scene graph and use the HOOPS/Stream // toolkit to read the data and insert it into the open node TK_Status status; HStreamFileToolKit *tk = GetModel()->m_pHFile; HC_Open_Segment_By_Key(GetModel()->GetModelKey()); HC_Open_Segment(segment); status = tk->ParseBuffer(in_data+seg_length+1, hsf_data_length); HC_Close_Segment(); HC_Close_Segment(); if (status == TK_Complete) // we've finished reading the file GetModel()->m_pHFile->Restart(); delete [] encoded_hsf_data; delete [] decoded_hsf_data; delete [] data; }
We may also want to transmit data which is being created dynamically from a modeling session. In this specific case the only difference here is that you need to create the HSF data dynamically rather than from a static file and the stream toolkit provides HStreamFileToolkit::GenerateBuffer to help with this. The following HBaseView::EmitSegment utility function is included in MVO which does all the necessary work.
void HBaseView::EmitSegment(long key) { HStreamFileToolkit tk; char buffer[8192]; int bytes_written; TK_Status status; HC_Open_Segment_By_Key(key); do { status =tk.GenerateBuffer(buffer, 8192, bytes_written); EmitHSFData(".", buffer, bytes_written); }while (status != TK_Complete); HC_Close_Segment(); }
The HNet Server encodes all messages that are sent to/from the HNet Server. Additionally the HNet object now provides an API to users to allow them to decide which encoders to use on their data. Via this API Developers can encrypt their data to ensure secure transmission of their data. In addition to having a number of in-built encoding schemes the HNet Server allows Developers to register and use their own encoders.
For secure transmission the HNet Server uses BlowFish and allows developers to provide keys of up to 56 bits in length. Though not exposed directly supporting keys larger than 56 bits is relatively straight-forward and any Developers wishing to do so should contact support@techsoft3d.com.
To tell the HNet Server to encode your messages with a specific encoder is straight-forward and done by calling:
void HNet::SetEncoderIds(const char *encoder)
'encoder' is a string containing the identifier of an encoding scheme. HNet has in-built support for the following encoding schemes:
If you have set BlowFish as your encoding scheme then you can set the key it uses for encrypting the data via the set_key() method of the Encoder object
Example:
In this simple example we create a HNet object and turn on BlowFish encoding with a key of 'Tejada'. Since none of the other encoders require additional parameters set you can simply call SetEncoderIds with Encoder id to turn on a specific encoder.
// first let's create a HNet object m_pHNet = new HNet(); // create an encoder object by asking HNet for a pointer to it BlowFish object struct hpi_encoder_s * encoder = hpi_encoder_lookup('F'); // now let's set a key on the BlowFish encoder encoder->set_key(encoder, (unsigned char*) "Tejada", strlen("Tejada")); // now let's specify that we want to use the BlowFish encoder m_pHNet->SetEncoderIds("F"); // at this point we use this HNet object to create to the HNet Server and create a session. m_pHNet->ConnectToServer(...);