Massimo, Except from the threading part of this, it will be easier under (x)Harbour to implement a special RDD for this. Also if I remember correctly Phil asked for xBase++ RDD documentation but it was too complex to implement a real RDD for xBase++.
Regards Mike Evans -----Original Message----- From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf Of Massimo Belgrano Sent: Thursday, January 24, 2008 2:38 PM To: Harbour Project Main Developer List. Subject: RE: [Harbour] Re: Licence question DBFSERVER DBF Client/Server Suite for Xbase++Server SoftwareClient Connectivity Library This document as a whole is copyrighted © 2001 by Phil Ide. The software package (comprising dbfServer.exe, dbSocketc.dll, header [.ch] files, sample programs, source code) is copyright © Bjørn Kaarigstad & Phil Ide, 2002. All Rights Reserved. The package is released under the terms of the GNU GENERAL PUBLIC LICENCE Disclaimer This software is supplied free of charge, and so there is no warranty for the software or accompanying documentation (herinafter called the "package"), to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the package "as is," without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality of this package is with you. In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the package as permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of the use or inability to use this package (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties). Any contributions made by others to this package have been acknowledged where possible. Commercial products, retailers, or distributors mentioned here are not necessarily endorsed by me or any other particular party. Authors Phil Ide code and documentation Bjørn Kaarigstad concept, ideas, inspiration Acknowledgements Many thanks to those who tested and broke everything. Special thanks goes to the following people who made particularly important contributions. Edgar Borger Jan Dirk Schuitmaker Mike Evans Mike Grace Jose Luis Otermin Thanks to everyone else who made comments, gave encouragement etc. Documentation Description The package consists of a server program which accepts requests via TCP/IP to perform operations on .DBF database tables. All Xbase++ operations are supported with the exception of dacSession (which includes XbpQuickBrowse) and compound legacy UI superfunctions (e.g. dbEdit()). The results of these operations are returned to the client program. There are many reasons why you might want to remove database operations to a remote program, but in Xbase++ probably the most compelling reason is the way that DBF access times deteriorate rapidly as more and more users connect to the database in a multi-user environment under Windows. dbfServer still opens the table once for each client connection, spawning a new thread to service each connecting client. Within Xbase++, each thread maintains a unique workspace, which has it's own set of unique workareas. The thread remains active for as along as the server is running and the client is connected. Client programs can be forced to use the server by linking in a DLL which handles all the communication between server and client. A #include file is supplied which translates all standard database operations to use the functions in the DLL. For many programs, particularly those generated by the Form Designer, this is sufficient. However, this is largely dependant upon coding style, as there are some elements the pre-processor cannot handle, and so some additional changes are likely to be required. New code can be written to take advantage of the fact that operations are performed through the server. Converted legacy code will suffer from a certain amount of lag since the code was written in the standard 'local connection' format. Understanding how to reduce lag by caching commands is fundamental to getting the best performance from the server. Using code optimised for best performance with regard to the server will produce fast responsive programs and reduce network traffic immensely thereby making the network itself faster and more responsive to the benefit of all users and applications regardless of whether or not they are utilising dbfServer. There are several other distinct advantages to using this package: Want to lock everybody out of the system? Shut down the server program and everyone will be unable to access the tables! Using the server program console, you can see exactly who is connected, which machine they are using, which socket number they are connected through and which files they have open (both tables and indexes). Do you have a time-consuming process which you know would run much faster on the server? dbfServer has the ability to run such procedures for you. Not only will the process run much faster, but again network traffic will be vastly reduced to the benefit of all network users. It should be pointed out that any DBF table can be accessed by any client program, provided it is visible to dbfServer. Converting Existing Programs To link to the dbfServer in your own application, you need to do the following: In each source file where database activity takes place, you need to add this directive: #include "dbfsocket.ch" In your project make file, you need to add the following statement to your executable's list of files: DBFSOCKETC.LIB In the same directory as your executable resides, and with the same name but with an .ini extension you need to create an ini file. This file should contain the following code: [SOCKETS] port=1042 server=localhost You should change the port number to match the port dbfServer is listening on, and the server name should be the IP name (or dotted octet address) of the machine where dbfServer can be found. There are three types of code that dbfServer cannot handle: Whilst it handles: (cExpr)->fname (cExpr)->(fname) it cannot handle: alias->fname alias->(fname) This is due to limitations in the preprocessor. You should change all such code to enclose the ALIAS in parenthesis. Expressions generated as macro's from strings will not be parsed properly, and will still expect local workarea connectivity. Fieldnames without aliased expressions cannot be pre- determined by the pre-processor. You must alias them. For example, if you have a table with the field NAME in it, you cannot do this: cName := NAME Instead, you must do this: cName := (Alias())->NAME // or cName := (cAlias)->NAME Remember that the path you supply to the program for opening tables is FROM THE SERVER not the client! That's all there is to it! Optimising The Form Designer when used to connect to a database, adds database access controls to the ::editControls array, which is then evaluated when the dialog is created or the record pointer is moved (via dialog controls). In a 'local database connection' e.g. a USE statement, this is ok. In a remote connection scenario, this becomes innefficient because to refresh 20 controls, you need to send at least 60 instructions to the server. In large data-intensive dialog's, you should consider another strategy. One method is to perform dbfScatter()/dbfGather() to/from an array, and point your dialog controls at the array rather than the database. dbfServer handles these read/write operations in a single instruction. All database functions are mapped to the function dbfSocket(). This function accepts 3 parameters: dbfSocket(<nFunction> [,<aParams> [, <lCache>] ] ) <nFunction> This is a numeric constant identifying the database operation to call. You should use the constants defined in dbfSocket2.ch <aParams> This optional parameter is an array of parameters to be passed to the function defined in <nFunction> <lCache> When this parameter is TRUE, the command is automatically cached and not sent to the server until a non-cached command is issued. Certain commands are automatically cached, but you can use dbfSocket() with the auto-cache option to increase performance. The automatically cached commands are a subset of all commands which return NIL. Some NIL-returning commands are excluded from the subset because they are considered immediate, e.g. dbCommit() and dbCommitAll() should not be cached, nor unlock operations. If you know that you are not interested in the return value from a command, then you should consider caching it. As an Example, Select(cAlias) may be used to select a workarea prior to performing operations on it, and if you are not interested in saving the return value then you should cache the command. dbfScatter() and dbfGather() are special functions which request the server to return and process an array respectively. This is more efficient than a standard Scatter()/Gather() which sends at least 3 requests to the server for each element of the array. As with the standard Alaska functions, dbfScatter() and dbfGather() handle arrays of simple values or objects. Additionally, dbfScatter() can accept an array of code blocks. This can be used to explicitely identify the fields which are accessed in the event that you don't need all the fields or want to get field data from multiple workareas. Automatically Cached Commands The following commands are automatically cached: dbAppend() dbGoTo() dbSetFilter() dbClearFilter() dbGoTop() dbSetIndex() dbClearIndex() dbRefresh() dbSetOrder() dbClearRelation() dbResumeSelect() dbSetRelation() dbClearScope() dbRollBack() dbSetScope() dbCloseRelation() dbRSuspendSelect() dbSkip() dbGoBottom() dbSelectArea() OrdCondSet() dbGoPosition() dbSetDescend() Remote Procedures Remote Procedures are functions which are called by the client program, but are run entirely on the server. The client tells the server to run the procedure, then waits until the process has completed. The return value of the process is the return value of the called procedure. A Remote Procedure is programmed as a function in a dynamically loadable DLL which must be visible to the server. If the function needs parameters, the function must accept two parameters. The second parameter is an array containing all the values the function requires, the first parameter is a numeric indicating the length of the array. Note that you cannot pass variables by reference to a remote procedure. To call the remote procedure, use this command: REMOTE CALL <func> IN <dll> [WITH <parms,...>] [<lUnload:UNLOAD>] <func> is the name of the function to call <dll> is the name (and path if required) of the dll where the function can be found <parms> is a comma-seperated list of all parameters that need to be passed to the function UNLOAD is an optional statement which causes the dll to be unloaded once the procedure has finished. An example of a (totally senseless!) function that can be used as a remote procedure: Function Useless(argc, argv) local bFilt := {|| TRUE } local i := 0 if argc > 0 bFilt := argv[1] endif dbGoTop() While !Eof() if Eval(bFilt) i += RecNo() // this is silly... endif SKIP Enddo return (i) To call this (assume it is linked into "RProc.DLL"): REMOTE CALL Useless IN RProc WITH {|| GENDER == 'M' } UNLOAD Error Handling If the client attempts to perform an operation which fails at the server, the server returns an error object to the client, which automaticaly passes it to the current error handler. Any error message displayed by the client will probably display the callstack of the client program, so you should be able to track logic errors in your code. In the event the error really belongs to a bug in the server (oh, horror!), you can find the callstack of the server embedded in the error object's cargo slot. By passing the error back to the client to handle, the error is displayed where the problem most likely lays - if you attempt to write a record without locking it first, the problem is in the client code not the server. This also makes the server robust and resilient, allowing other connected clients to continue working without interruption. Rebuilding the Server and dbfSocket.dll To rebuild either the server or dbfSocketC.dll, you will require ASINET1C.LIB, which is part of the Professional Subscription. ASINET1C.DLL, which is part of the Professional Subscription redistributable libraries, is supplied with the package, and should be placed in your RUNTIME directory or somewhere in your PATH. GNU GENERAL _______________________________________________ Harbour mailing list Harbour@harbour-project.org http://lists.harbour-project.org/mailman/listinfo/harbour _______________________________________________ Harbour mailing list Harbour@harbour-project.org http://lists.harbour-project.org/mailman/listinfo/harbour