Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to use WSOCK32.DLL in VFP?

Status
Not open for further replies.
There are 66 exports in that DLL, you can use the DECLARE DLL command to make use of some.

Some usage examples can be found here:

Also you can do o = CreateObject("MSWinsock.Winsock") as shown in faq184-3479

Also you find winsock sample code at the fox.wikis.com and universalthread.

If you get more concrete with what you want to do, we can come up with more concrete answers. Eg to use winsock to make http request would not be wise, as there are more specific solutions to that, which cost less code and are easier to setup and maintain.

So the answer is: It depends.

Bye, Olaf.
 
I am still listening,

if you have any more specific question, I'm sure I can show you something. Eg using MsXML2.HttpRequest, DownLoadURLToFile, The Hyperlink Control, using WiniNet.dll, VFPConnection (HTTP/FTP/FTPS etc), libcurl class for VFP, and more.

But nobody can read your mind, about what of the 66 exported functions and properties of the DLL interest you.

Bye, Olaf.
 
Hi Olaf
Sorry for not replying :)
What i really wanted to do is
to make a License Server listening (Running on an SBS 2007) to Clients.
I had done this using MSWINSCK.OCX .
as i mentioned in thread 184-1693203 somewhere the memory leaks and i couldn't resolve it.

{
On connection request from Clients
socketarray(x)=CreateObject("Socket")
accepts connection
authenticates its client
if failure{ close socket and makes the Socketarray(x)=.f.}
}

it works.. but running for few hours the memory leak starts.
i think instead of using OCX it is better to try using the DLL.

 
OK, now that's more concrete.

So your real question is, if using the wsock32.dll instead of the mswinscok.ocx will be more stable and not show memory leaks.

I alrady said:

1. memory leaks are hard to detect and could be solved by restarting an exe/service, which frees memory resources from time to time, obviously. You said you wanted to try this route

2. The correct design in working with the OCX is, you have one listener and accept requests in secondary worker sockets.
And this is only halfway what you've done in thread184-1693203.

You're trying to reuse/recycle socket objects you store in an array, which is not the correct way to work with sockets.
The wiki example I pointed to has the correct usage of creating a fresh object instead and disposing unused ones.

The essential line of the listening socket class in the wiki example is ThisForm.Addobject( SYS(2015), 'frmSock2', m.tnRequestID ), so it adds a fresh new socket object. Also pay attention the winsock sample uses two winsocks, one listener and the new winsock created is frmsock2, a worker winsock class.

Before the new socket is created, inactive sockets are removed, not recycled, it's most probably that, which disposes their used memory and prevents a memory leak in comparison to reusing sockets, like you do. But again said: It's hard to determine the root cause of memory leaks, they could be anywhere, but you can monitor sys(1104).

I would not dispose unused sockets that way, I would program a worker socket in a way, it removes itself from memory, once it's finished, eg in it's SendComplete Event it could close and remove itself, eg via:

This.Close()
* Dispose self (depends on the socket being added by AddObject() as child object with a valid Parent object):
lcName = This.name
This.Parent.&lcName = .NULL.

Bye, Olaf.
 
Using Dependencywalker (depends.exe) I find (non surprising) mswinsock.ocx is depending on (using) wsock32.dll and that depends/uses ws2_32.dll.

If the memory leaks really come from the winsock implementation it's more likely the memory leak is inside either core DLL mswinsock32.dll or ws2_32.dll, than in the ocx, which is merely an ActiveX wrapper around the socket functionality.

It may be worth a try using the DLL directly, but the most promising change would be disposing unused sockets, no matter if staying with ocx or not. That's just my feeling of it.

Bye, Olaf.
 
Correction: You can monitor sys(1016). You can of course try sys(1104) to free memory, too.

I'd still first use the short term solution to restart the server process and then try out several ways of working with the sockets, to see what prevents a memory leak. Having that solved or improved, will make the server process restart less often, or not at all, in the ideal case.

Bye, Olaf.
 
i had put a timer in mainForm which checks Socket State every 10 secs.
when socketstate<>7 it makes SocketArray(x)=.f. thus unloads socket OBJ from memory.
moreover whenever a Close event of SocketOBJ occurs i do the same thing.
I'm not re using the Socket.
 
OK, I didn't know anything about the timer.

But you're still wrong about creating a winsock per request, you oinly build them once in the initial array creation.
You're resuing a socket with licarray(i).janal.ConnectionRequest(requestid), you never ever again createobject('lic'), at least not in the code you've shown so far.

Also you havn't shown the lic class definitions, maybe for reasons not to disclose your license check. But you can hardly be helped, if you cannot share the essential code portions to reproduce your problem.

I think the solution in the wiki has been proven over time, there would be feedback to the wiki, if it would leak memory.

Bye, Olaf.

 
Hi Olaf,
Here is my code which is re written



****** * D:\MIS\SERVER_MAIN.PRG

*: Documented using Visual FoxPro Formatting wizard version .05
*:******************************************************************************
*: SERVER_MAIN


_SCREEN.CAPTION='MIS Server'
LOCAL lcmainclasslib
*Local lclastsettalk,lclastsetpath,lclastsetclasslib,lconshutdown
PUBLIC MAX_USERS,hexmacid,lic_string
*lic_string=''
MAX_USERS=0
SET SYSMENU OFF
*-- Save and configure environment.
SET TALK OFF
SET DATE TO british
SET ECHO OFF
SET SAFETY OFF
SET STATUS BAR OFF
SET CONSOLE OFF
SET CENTURY ON
SET RESOURCE OFF

SET PROCEDURE TO mis_srv.fxp


lcmainclasslib="stock"
SET CLASSLIB TO (lcmainclasslib) ADDITIVE
SET CLASSLIB TO "spl" ADDITIVE
*Set Classlib To "ajm_class" Additive

*-- Instantiate application object.
*Release goApp
PUBLIC goapp
_SCREEN.WINDOWSTATE= 0

*_Screen.BackColor=Rgb(177,199,254)
_SCREEN.CLOSABLE=.F.





**(B)**END******************

goapp=CREATEOBJECT("cApplication")

*-- Configure application object.


goapp.cstartupform="misserver"


*-- Show application.
goapp.SHOW






****** * D:\MIS\MISSERVER.SCX

PROCEDURE INIT
SET TALK OFF
SET ECHO OFF
SET DATE TO british


_VFP.AUTOYIELD=.F.


abcd='9999'
THIS.TAG=licensed()
licstring=ALLTRIM(THIS.TAG) && load lic string
IF licstring==''
MESSAGEBOX('License Error -1',0+16,'Error')
ELSE
srvport=VAL(abcd)&& load this from config File REcommended
THISFORM.rmtlisten1.janal.OBJECT.LocalPort = srvport
THISFORM.rmtlisten1.janal.OBJECT.LISTEN
lstport=THISFORM.rmtlisten1.janal.OBJECT.LocalPort
THIS.label1.CAPTION='Listening to Port : '+ALLTRIM(STR(lstport))+CHR(13)
THIS.label2.CAPTION='Max Users : '+ALLTRIM(STR(MAX_USERS))+ CHR(13)
PUBLIC ARRAY licarray(MAX_USERS,5)
FOR i=1 TO MAX_USERS
licarray(i,2)='' &&logged user name
licarray(i,3)=''
licarray(i,4)='' && login time
licarray(i,5)=0
ENDFOR

ENDIF




ENDPROC


CAPTION = "ShutDown"
TABINDEX = 1
NAME = "cmdClose"
PROCEDURE CLICK
x=0
IF TYPE('licarray',1)=='A'
FOR i=1 TO MAX_USERS
IF VARTYPE(licarray(i,1))='O'
IF licarray(i,1).janal.OBJECT.state=7 && if connected
x=x+1
ENDIF
ENDIF
ENDFOR
ENDIF
IF MESSAGEBOX("Users connected : "+ALLTRIM(STR(x))+CHR(13)+;
"Shut Down ?",4+32+256,'M.I.S')=6
THISFORM.rmtlisten1.janal.OBJECT.CLOSE()
RELEASE licarray

THISFORM.RELEASE()
goapp.RELEASE
ENDIF

ENDPROC



NAME = "Rmtlisten1"
janal.NAME = "janal"
PROCEDURE janal.ConnectionRequest
*** ActiveX Control Event ***
LPARAMETERS requestid



IF TYPE('licarray',1)=='A'
***count Users connected already
x=0
FOR i=1 TO MAX_USERS
IF VARTYPE(licarray(i,1))='O'
IF licarray(i,1).janal.OBJECT.state=7 && if connected
x=x+1
ENDIF
ENDIF
ENDFOR

IF x=MAX_USERS
**Noop**
ELSE

**Accept connection to First available socket
FOR i=1 TO MAX_USERS
IF VARTYPE(licarray(i,1))='O'
ELSE
licarray(i,1)=CREATEOBJECT('lic') && assign new socket
licarray(i,1).janal.OBJECT.LocalPort=0
licarray(i,1).janal.OBJECT.LISTEN
licarray(i,1).COMMENT=ALLTRIM(THISFORM.TAG) && Lic String
licarray(i,2)=''
thaakkol=SYS(2)
licarray(i,3)=SYS(2007,thaakkol,0,1)
licarray(i,1).janal.ConnectionRequest(requestid,i,thaakkol)
EXIT
ENDIF
ENDFOR
ENDIF

ENDIF
=SYS(1104) && Purge memory Buffer

ENDPROC



INTERVAL = 3000
NAME = "Timer1"
PROCEDURE TIMER
samayam=VAL(THIS.TAG)
THIS.TAG=STR(samayam+THIS.INTERVAL)
x=0
Y=0
IF TYPE('licarray',1)=='A'
***count Users connected already
FOR i=1 TO MAX_USERS

IF VARTYPE(licarray(i,1))='O'
x=x+1
IF licarray(i,1).janal.OBJECT.state=7 && if connected
Y=Y+1
auth_FLAG=VAL(licarray(i,1).TAG)

IF auth_FLAG>0
DO CASE

CASE auth_FLAG=3
licarray(i,1).janal.OBJECT.CLOSE()
licarray(i,1)=.F.
licarray(i,2)=''
licarray(i,3)=''
licarray(i,4)=''
licarray(i,5)=0
THISFORM.list1.INIT()

CASE auth_FLAG=1 && if connection not authenticated
mt=0
ct=VAL(SYS(2))
ot=licarray(i,5)

IF ct<ot
mt=(86400-ot)+ct
ELSE
mt=ct-ot
ENDIF

**check time out ..max 5 sec.
IF mt>5

*** if timed out then close socket
licarray(i,1).janal.OBJECT.CLOSE()
licarray(i,1)=.F.
licarray(i,2)=''
licarray(i,3)=''
licarray(i,4)=''
licarray(i,5)=0
THISFORM.list1.INIT()
ENDIF
ENDCASE

ENDIF
ELSE && if State<>7
licarray(i,1).janal.OBJECT.CLOSE()
licarray(i,1)=.F.
licarray(i,2)=''
licarray(i,3)=''
licarray(i,4)=''
licarray(i,5)=0
THISFORM.list1.INIT()
ENDIF
ENDIF
ENDFOR
ENDIF


ENDPROC
****** * D:\MIS\SPL.VCX

NAME = "janal"

PROCEDURE DataArrival
*** ActiveX Control Event ***
LPARAMETERS bytestotal

strData = SPACE(256) && Define string to pass to GetData
THIS.OBJECT.GETDATA(@strData)
arrived_msg=xorwrap(strData)
STRTOFILE(CHR(13)+'DataArrival from '+THIS.OBJECT.RemoteHostIP+' and memory : '+SYS(1016)+TTOC(DATETIME())+'#','mislicserver.log',1)
STRTOFILE(CHR(13)+'Data '+ALLTRIM(arrived_msg)+' and memory : '+SYS(1016)+TTOC(DATETIME())+'#','mislicserver.log',1)

ALINES(msgarray,arrived_msg,4,CHR(200))
IF TYPE('msgarray')='U' && if array not created
THIS.PARENT.TAG='3'
**error
ELSE
IF MOD(ALEN(msgarray),2)>0 && elements are not correct
THIS.PARENT.TAG='3'
**error3
ELSE
msgID=VAL(msgarray(1))
IF msgID<1
THIS.PARENT.TAG='3'
ELSE
DO CASE
CASE msgID=1 && handshaking return msg against rahasya chodyam
rahasyam=(ALLTRIM(msgarray(2)))


IF rahasyam==ALLTRIM(THIS.TAG)
THIS.PARENT.TAG='2' && authenticated
THIS.OBJECT.SendData("2"+CHR(200)+ALLTRIM(THIS.PARENT.COMMENT)) && send license string 'HPO' if everything ok


ELSE && if handshaking failed
THIS.OBJECT.SendData("3"+CHR(200)+"Hand shaking Error") && send Error Msg to client'
THIS.PARENT.TAG='3'
ENDIF


CASE msgID=5 && arrives when an user logged in msg=user name,licarray element No
* MESSAGEBOX(msgarray(2))
ALINES(poda,msgarray(2),4,",")
IF TYPE('poda',1)='A'

licarray(VAL(poda(2)),2)=poda(1) &&msgarray(2)


ENDIF
CASE msgID=4 &&user log request from client

IF TYPE('licarray',1)=='A'

log_string=''
x=0
******
FOR i=1 TO MAX_USERS
IF VARTYPE(licarray(i,1))='O'
IF licarray(i,1).janal.OBJECT.state=7 && if connected
log_string=log_string+licarray(i,2)+;
' @ '+licarray(i,1).janal.OBJECT.RemoteHostIP+'¦'
ENDIF
ENDIF
ENDFOR

******

THIS.OBJECT.SendData("4"+CHR(200)+;
log_string) && send users connected
ENDIF
ENDCASE
ENDIF
ENDIF
ENDIF

*=Sys(1104)


ENDPROC

PROCEDURE ConnectionRequest
*** ActiveX Control Event ***
LPARAMETERS requestid,socket_element,chaavi
IF PARAMETERS()=3
thaakkol=ALLTRIM(SYS(2007,ALLTRIM(chaavi),0,1))
IF ALLTRIM(licarray(socket_element,3))==thaakkol
THIS.OBJECT.CLOSE()
*IF THIS.OBJECT.State<>7
THIS.OBJECT.ACCEPT(requestid)
IF THIS.OBJECT.state=7 && CONNECTED
***SEND GREEN FLAG SIGNAL TO CONNECTED IP

rahasyam=ALLTRIM(SYS(2007,SYS(2),33,1))
licarray(socket_element,2)=''
THIS.TAG=rahasyam && store secret question for later comparison
licarray(socket_element,4)=TTOC(DATETIME())
send_sTR="1"+CHR(200)+rahasyam+","+ALLTRIM(STR(socket_element))
THIS.PARENT.TAG='1'
licarray(socket_element,5)=VAL(SYS(2))
THIS.OBJECT.SendData(send_sTR) && ask first question

ENDIF
ENDIF
ENDIF



*ENDIF


ENDPROC
PROCEDURE CLOSE
*** ActiveX Control Event ***
FOR i=1 TO MAX_USERS
IF VARTYPE(licarray(i,1))='O'
IF licarray(i,1).janal.OBJECT.state=7 && if connected

ELSE
licarray(i,1).janal.OBJECT.CLOSE()
licarray(i,1)=.F.
licarray(i,2)=''
licarray(i,3)=''
licarray(i,4)=''
licarray(i,5)=0
ENDIF
ENDIF
ENDFOR
*=SYS(1104)



ENDPROC
 
Beautify that code, please. It's not readable this way. Also put it into CODE tags to let it show as code here.

Now, just taking this part:
Code:
licarray(i,1)=Createobject('lic') && assign new socket
licarray(i,1).janal.Object.LocalPort=0
licarray(i,1).janal.Object.Listen
licarray(i,1).Comment=Alltrim(Thisform.Tag) && Lic String
licarray(i,2)=''
thaakkol=Sys(2)
licarray(i,3)=Sys(2007,thaakkol,0,1)
licarray(i,1).janal.ConnectionRequest(requestid,i,thaakkol)
You now do create a new 'lic' socket, but instead of calling it's Accept() method, you call it's ConnectionRequest(), that's not how it is intended:
Your code of lic.ConnectionRequest() should rather be put into the lic.Init(), and lic sockets accepting requests don't listen, they send, so this is all wrong. You're calling an event, just that alone is wrong, you never call an event.

Compare that to how the frmsock2 class is defined at the wiki solution, in the ConnectionRequest() event of the frmsock class a new socket (class frmsock2) is created, that new socket receives the requestid in it's Init() and calls it's own Accept(). The frmsock2 socket never has a ConnectionRequest() event raised, it's not a listening socket, but a socket precessing the request.

Besides all that I'm unsure if setting an array element .f. really does garbage collection. The object is destroyed, if not referenced in any other variable or property, sure. But you should at least also call into SYS(1104) to purge memory and DOEVENTS FORCE to let windows do events, eg also garbage collection of sockets, perhaps.

Does you cApplication class have a read events? Or is the MISSERVER.SCX a modal form? If neither is the case please takle a look at the task manager process tab, if your exe is hogging a whole cpu core. Never allowing any event queu to be processed, the typical cpu usage would be 50% on dual core systems, 25% on quad core etc. Then you never give the chance for garbage collection to free memory.

Bye, Olaf.
 
there is a master Socket in the main form.
which is listening to port 9999 when ever it gets a request it handing over the request with the same request ID which Master Socket got.

licarray(i,1).janal.ConnectionRequest(requestid,i,thaakkol) *licarray is what SocketOBJ Array which is holding Socket Object 'LIC'
In LIC's connectionrequest event it accept(requestID).


 
Sorry, that I couldn't give you a concreter code change. I try to setup something on my own based on your code, to see, if I can reproduce the problem.

Bye, Olaf.
 
I took your code and modified it slightly and have the server part running. As the client part is not shown, I have difficulties in using the server. I can technicall connect and also only get MAX_USERS connections, but I never arrive in DataArrival and the series of communications you do there to check the viability of a user.

I understand this is the more delicate and secret part of the code. I will try to get this up and running without that client/server communication, as the main intent is to find a memory leak only, anyway.

So far I was able to repeat connect/disconnect from a secondary VFP session in an endless loop, but got towards non reacting server, crashing with C5, before a memory leak could be detected. So I think I have to give the server a little more time.

What I found irritiating, is your setting of _VFP.Autoyield = .F., the moment you use COM/ActiveX, this should perhaps be .T. , the Sample code in the Wiki also sets this .F., so that might be what is needed for the Winsock, but someone also mentions he got the code to work, setting it .T. in some places.

I'm still on it and will report back in a few days with more results about the memory consuiption monitored by SYS(1106).

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top