Page 1 of 1

Simple Comet framework

Posted: Thu Jun 17, 2010 10:53 am
by Seymour Clufley
Comet is a technique for sending stuff from a web server to client pages whenever the server wants to send it - not when the client has just requested it. It's ideal for newsfeeds, chatrooms, status updates and the like.

I've been curious about using Comet on my website since I first read about it in 2008. However, plagued by the apprehension of the gutless beginner, I've postponed "taking the dive" until now - which is ridiculous because Comet could be very useful for my website. And perhaps, yours too!

This implementation (which I've successfully tested with IE, Chrome and Opera) has three components:
  • the client webpage
  • a "slave" executable
  • an include for your main server program, containing some procedures (see below)
On startup, the webpage opens an XMLHttpPost connection with the slave exe. The slave exe creates a file, telling the main server program that there's a client which wants to receive Comet feeds. When the server has some information it wants to send, it goes through all the waiting clients and, for each one that should get the information, it saves a unique "response file" containing the information. Meantime the slave exe has been watching for its unique response file to be created and when the file appears, it reads the contents in and sends it to the client and terminates. The connection is then restarted by the client, and our new slave exe waits for new information.

There are other ways to do it, of course. I've done it this way because we end up with a generic framework which can be easily used in different ways. For example...

To send information to one visitor:

Code: Select all

CometOne(info$,ipaddress$)
To send information to all visitors who are on a particular page:

Code: Select all

CometPage(info$,pagename$)
To send information to all visitors on your site:

Code: Select all

CometCast(info$)
When the webpage opens the XMLHttpPost connection, it optionally sends a variable containing whatever information you want. This is passed to the slave exe, which passes it to the main server program. Thus, you can use it to filter out visitors according to whether their info contains a certain string. CometPage() and CometCast() take this as an optional parameter.

There's also another procedure, CometCast_OncePerIP(). This does the same as CometCast() except that it only sends the data to one page for each IP address. This could be used, for example, in the following situation: you're sending an alert out to everyone, and someone has two (or more) pages open on your site. Using CometCast(), the alert would show on every page they have open. Using CometCast_OncePerIP(), it will only appear on one page that they have open. Perhaps this could be finetuned so as to target the page they most recently opened, as that's what they're likely to be looking at.


So anyway, here's a sample webpage:

Code: Select all

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Comet test</TITLE>

<SCRIPT type="text/javascript">

function DefineString(t1) {
	var t2 = "";
	if (!t1) {
		return t2;
	}
	if (t1==null) {
		return t2;
	}
	if (t1=="undefined") {
		return t2;
	}
	if (typeof(t1)==undefined) {
		return t2;
	}
	return t1;
}


var cometinfo = ""
var docomet = false;

function OpenCometConnection(info) {
	info = DefineString(info);
	if (info!="") {
		cometinfo = info;
	}
	docomet = true;
	CometWait(document.URL+"~"+cometinfo+"~");
}

function CloseCometConnection() {
	docomet = false;
}

function CometWait(instrux) {
	var CometReq = false;
	var selfcomet = this;
	// Mozilla/Safari
	if (window.XMLHttpRequest) {
		selfcomet.CometReq = new XMLHttpRequest();
	}
	// IE
		else if (window.ActiveXObject) {
			try {
				selfcomet.CometReq = new ActiveXObject("Msxml2.XMLHTTP");
			} catch (e) {
				try {
					selfcomet.CometReq = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {
				selfcomet.CometReq = false; // real trouble here - couldn't create ANY XHR
			}
		}
	}
	
	selfcomet.CometReq.open("POST", "comet.exe", true);
	selfcomet.CometReq.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
	selfcomet.CometReq.onreadystatechange = function() {
		//alert("selfcomet.CometReq.readyState: "+selfcomet.CometReq.readyState);
		if (selfcomet.CometReq.readyState == 4) {
			// data received...
			if (docomet) {
				if (selfcomet.CometReq.responseText!="null") {
					//alert("COMET DATA...\n\n"+selfcomet.CometReq.responseText);
					GenericCometResponder(selfcomet.CometReq.responseText);
				}
				// restart connection...
				//alert("RESTARTING COMET");
				setTimeout("OpenCometConnection()",10);
			}
		}
	}
	selfcomet.CometReq.send(instrux);
}




function GenericCometResponder(info) {
	document.getElementById("news_display").innerHTML += "<BR><BR>"+info;
}

</SCRIPT>

</HEAD>




<BODY onload="OpenCometConnection()">


<DIV id="news_display"></DIV>


</BODY>
</HTML>
Obviously you'll want to change the GenericCometResponder() function in practice, but it'll do for this demo (hopefully).


Here's the code for the slave executable, which you should compile as a console program called "comet.exe" in your site's local folder. Change the "cometfolder" variable to something that suits you, but make sure the folder exists!

Code: Select all

Structure CommonGatewayInterface
	ContentLength.i
	RemoteAddr.s
	PostData.s
EndStructure
Global cgi.CommonGatewayInterface

Macro CGI_GetVariables
	
	cgi\ContentLength = Val(GetEnvironmentVariable("CONTENT_LENGTH"))
	cgi\RemoteAddr = GetEnvironmentVariable("REMOTE_ADDR") ; client's ip address
	
	If cgi\ContentLength>0
		*Buffer = AllocateMemory(cgi\ContentLength)
		hInput = GetStdHandle_(#STD_INPUT_HANDLE)
		ReadFile_(hInput, *Buffer, cgi\ContentLength, @bRead, 0)
		cgi\PostData = PeekS(*Buffer)
		FreeMemory(*Buffer)
		CloseHandle_(hInput)
	EndIf
	
EndMacro 




;-
;- PROGRAM START

CGI_GetVariables
OpenConsole() ; needed for output


cometfolder.s = GetPathPart(ProgramFilename())+"comets\"
Macro CometRequestFile(code)
	cometfolder+code+".request"
EndMacro
Macro CometResponseFile(code)
	cometfolder+code+".response"
EndMacro
Macro CometResignationFile(code)
	cometfolder+code+".resign"
EndMacro


Repeat ; create a uniquely-named "request file"
	tokencode.s = ""
	For a = 1 To 16
		tokencode+Str(Random(8)+1)
	Next a
	tokencode = Str(ElapsedMilliseconds())+" "+tokencode
	If FileSize(CometRequestFile(tokencode)) = -1
		Break
	EndIf
ForEver


f = CreateFile(#PB_Any,CometRequestFile(tokencode))
If f
	cgi\PostData = URLDecoder(cgi\PostData)+cgi\RemoteAddr+"~"
	WriteStringN(f,cgi\PostData)
	CloseFile(f)
Else
	; error creating request file. abort...
	End
EndIf


; wait for response...
starttime = ElapsedMilliseconds()
Repeat 
	
	If FileSize(CometResponseFile(tokencode))>-1
		; server has responded
		
		fullreply.s = ""
		f = ReadFile(#PB_Any,CometResponseFile(tokencode))
		If f
			While Not Eof(f)
				fullreply + ReadString(f)+Chr(13)
			Wend
			CloseFile(f)
		EndIf
		
		DeleteFile(CometResponseFile(tokencode))
		
		fullreply = "Content-type: text/plain;charset=UTF-8"  + #CRLF$ + #CRLF$ + fullreply
		written = WriteConsoleData(@fullreply,MemoryStringLength(@fullreply))
		
		End
	EndIf
	
	Delay(250) ; change this to whatever you want. probably, a comet setup won't need to be very fast.
Until (ElapsedMilliseconds()-starttime)>30000 ; wait 30 seconds for response

; tell server we're no longer interested...
f = CreateFile(#PB_Any,CometResignationFile(tokencode))
If f : CloseFile(f) : EndIf

; send "null" reply to client...
fullreply.s = "Content-type: text/plain;charset=UTF-8"  + #CRLF$ + #CRLF$ + "null"
written = WriteConsoleData(@fullreply,MemoryStringLength(@fullreply))

End

Here's the PBI for your server program. This program should be threadsafe and running all the time.
Before the line where you include this PBI, create a global string variable "cometfolder.s" and use it to store the directory where Comet request files will be saved by the slave executable(s). This variable should be identical in the slave code above.

Code: Select all


If cometfolder=""
	MessageRequester("COMET","Need to set folder where comet request and response files will be saved.",0)
EndIf

Macro CometRequestFile(code)
	cometfolder+code+".request"
EndMacro
Macro CometResponseFile(code)
	cometfolder+code+".response"
EndMacro
Macro CometResignationFile(code)
	cometfolder+code+".resign"
EndMacro

Structure CometStructure
	pagename.s
	ip.s
	info.s
EndStructure
Global NewMap waitingcomet.CometStructure()


Macro ReviewCometClients
	
	NewMap delcomet.b()
	ForEach waitingcomet()
		code.s = MapKey(waitingcomet())
		If FileSize(CometResignationFile(code))>-1
			AddMapElement(delcomet(),code)
			DeleteFile(CometResignationFile(code))
		EndIf
	Next
	ForEach delcomet()
		code.s = MapKey(delcomet())
		DeleteMapElement(waitingcomet(),code)
	Next
	FreeMap(delcomet())
	
	
	d = ExamineDirectory(#PB_Any,cometfolder,"*.request")
	If d
		While NextDirectoryEntry(d)
			fn.s = DirectoryEntryName(d)
			code.s = StringField(fn,1,".")
			fn = cometfolder+fn
			If Not FindMapElement(waitingcomet(),code)
				;MessageRequester("NEW COMET",code,0)
				AddMapElement(waitingcomet(),code)
				f = ReadFile(#PB_Any,fn)
				If f
					ln.s = ReadString(f)
					waitingcomet(code)\pagename = GetURLPart(StringField(ln,1,"~"),#PB_URL_Path)
					waitingcomet(code)\info = StringField(ln,2,"~")
					waitingcomet(code)\ip = StringField(ln,3,"~")
					CloseFile(f)
				EndIf
				DeleteFile(fn)
			EndIf
		Wend
		FinishDirectory(d)
	EndIf
	
EndMacro


Macro GiveCometInfo
	code.s = MapKey(waitingcomet())
	f = CreateFile(#PB_Any,cometfolder+code+".response")
	If f
		WriteStringN(f,sendinfo)
		CloseFile(f)
	EndIf
EndMacro

Macro DeleteSatedComets
	ForEach delcomet()
		code.s = MapKey(delcomet())
		DeleteMapElement(waitingcomet(),code)
	Next
EndMacro

Procedure.b CometOne(sendinfo.s,ip.s)
	
	ForEach waitingcomet()
		If waitingcomet()\ip = ip
			GiveCometInfo
			DeleteMapElement(waitingcomet(),code)
			ProcedureReturn #True
		EndIf
	Next
	
EndProcedure

Procedure.b CometPage(sendinfo.s,client_pagename.s,client_eligibility.s="")
	
	NewMap delcomet.b()
	ForEach waitingcomet()
		If waitingcomet()\pagename=client_pagename And (client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1))
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure

Procedure.b CometCast(sendinfo.s,client_eligibility.s="")
	
	;R("COMETS: "+Str(MapSize(waitingcomet())))
	NewMap delcomet.b()
	ForEach waitingcomet()
		;R("COMET CLIENT ON PAGE: "+waitingcomet()\pagename)
		If client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1)
			;R("SENDING TO CLIENT: "+MapKey(waitingcomet()))
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure

Procedure.b CometCast_OncePerIP(sendinfo.s,client_eligibility.s="")
	
	NewMap ipdone.b()
	NewMap delcomet.b()
	ForEach waitingcomet()
		If FindMapElement(ipdone(),waitingcomet()\ip) : Continue : EndIf
		If client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1)
			AddMapElement(ipdone(),waitingcomet()\ip)
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure
In your main program loop, call the ReviewCometClients macro regularly - it watches the cometfolder for new requests.

That's it. All you need to do now is call any of the procedures when you want to send some information to your visitors.


Here's an example "server program"... make sure to compile it as threadsafe.

Code: Select all

Global cometfolder.s = "C:\temp\comets\"
XIncludeFile "Comet.pbi"

Procedure.b FeedStuffToComets(void)
	Repeat
		Delay(10)
		If GetAsyncKeyState_(#VK_P)
			CometPage("Transmitting to everyone on this page","my_webpage.html")
		EndIf
		If GetAsyncKeyState_(#VK_C)
			arr.s = "AVRIL LAVIGNE~KIM BASINGER~PAMELA ANDERSON~KRISTEN STEWART~BRITNEY SPEARS~CHRISTINA APPLEGATE~JULIETTE LEWIS~BILL CLINTON~"
			CometCast("TRANSMITTING TO THE WHOLE WORLD!! TERRIBLE NEWS! ALIENS HAVE ABDUCTED "+StringField(arr,Random(CountString(arr,"~")-1)+1,"~")+"!!!!!!!")
		EndIf
	ForEver
EndProcedure

CreateThread(@FeedStuffToComets(),0)

While Not GetAsyncKeyState_(#VK_Escape)
	ReviewCometClients
	Delay(50)
Wend

End
Run that, open your test page, and hit your C and P keys!


Hope this is useful to someone,
Seymour.

Re: Simple Comet framework

Posted: Thu Jun 17, 2010 1:18 pm
by SFSxOI
Very nice. Thank You :)

Now I can send all those pop up windows :)

Just kidding. I think we actually have a use for this here at work.

Re: Simple Comet framework

Posted: Thu Jun 17, 2010 1:29 pm
by skywalk
I have no problem with Bill Clinton in space. :)

Re: Simple Comet framework

Posted: Thu Jun 17, 2010 1:52 pm
by Seymour Clufley
skywalk wrote:I have no problem with Bill Clinton in space. :)
Yes, there was a storyline almost developing there... a porn movie, perhaps, with Bill and all these beautiful young women trapped in an alien spacecraft.

Re: Simple Comet framework

Posted: Thu Jun 17, 2010 6:43 pm
by SFSxOI
Seymour Clufley wrote:
skywalk wrote:I have no problem with Bill Clinton in space. :)
Yes, there was a storyline almost developing there... a porn movie, perhaps, with Bill and all these beautiful young women trapped in an alien spacecraft.

With a supply of cigars, don't forget the cigars. :)

Re: Simple Comet framework

Posted: Thu Jun 17, 2010 9:38 pm
by Seymour Clufley
:)

So, does the code work okay?

Re: Simple Comet framework

Posted: Fri Jun 18, 2010 7:28 pm
by HAnil
CometResignationFile() is not a function and is missing. would you check ? thx.

Re: Simple Comet framework

Posted: Fri Jun 18, 2010 9:15 pm
by Seymour Clufley
Sorry about that - I've fixed it now.

Re: Simple Comet framework

Posted: Sat Jun 19, 2010 8:59 pm
by Seymour Clufley
Does it work now?

Re: Simple Comet framework

Posted: Sat Jun 19, 2010 10:19 pm
by HAnil
yes, source can be compiled. index.html runs well but screens seems a lot of MZ.
yes comet.pb is compiled with console options. I use Abyss web server. maybe additionaly options is required.
still I looking for good run.
Thanks for this good work.

Re: Simple Comet framework

Posted: Sat Jun 19, 2010 11:55 pm
by Seymour Clufley
I don't know about Abyss. I'm using it on Apache (XAMPP) with no problems.

What does "MZ" mean?

Re: Simple Comet framework

Posted: Sun Jun 27, 2010 12:48 pm
by Seymour Clufley
Hello again everyone,

Here's a slightly better version. I'm leaving the original version up because it's simpler than this version.

The code for the slave executable has been tightened up. Most of the changes are in the server PBI.

I've renamed CometOne() to CometIP(), and the ReviewCometClients macro is now called ReviewComets. I think these names make more sense.

New functions:
  • Comet()
  • CometForIPAndPage()
  • CometNotPage()
  • CometCast_OncePerPage()
  • NewComet()
  • EndComet()
Comet() is for replying to a specific comet - the input parameters are the info to be sent, and the ID of the comet to send it with. To see why this function may be useful, read on.

If your visitor has two pages open and they click something in Page A that should affect change in Page B, you can identify what comet to update Page B with using the CometForIPAndPage() function:

Code: Select all

cometid.s = CometForIPAndPage(ip$,pagename$)
If cometid
    Comet(sendinfo$,cometid)
EndIf
NewComet() and EndComet() are called whenever the appropriate "event" occurs and you can do what you want with them. They could be used to keep track of where various people are on your site - for example, registered users. If you don't want to use them you should comment out the calls to them. Otherwise your NewComet() and EndComet() procedures should look something like this:

Code: Select all

Procedure NewComet(id.s)
  ; id is the new comet's unique id
  
  With waitingcomet(id)
    ; your code
  EndWith
  
EndProcedure


Procedure EndComet(id.s,replied.b)
  ; id is that of a comet which has just been replied to or timed out.
  ; replied=false: the comet timed out
  ; replied=true: the comet was replied to
  
  With waitingcomet(id)
    ; your code
  EndWith
  
  ; after this procedure finishes, the comet's slot in the waitingcomet() map will be deleted (its id may be used for another comet later)
EndProcedure
The other two new functions are trivial. CometNotPage() is for sending info to everyone except people who are on a particular page. CometCast_OncePerPage() will send a comet to one person per page.


Finally, the JavaScript has been enhanced slightly. It now calls a function CometPeripherals() before sending its request. CometPeripherals() is where you should pack any needed information, such as log-in variables. This also calls a function (if it exists) called PageSpecificCometInfo(), which may be different for each page.

JAVASCRIPT:

Code: Select all

var d1 = "|";
var d2 = "~";

function DefineString(t1) {
	var t2 = "";
	if (!t1) {
		return t2;
	}
	if (t1==null) {
		return t2;
	}
	if (t1=="undefined") {
		return t2;
	}
	if (typeof(t1)==undefined) {
		return t2;
	}
	return t1;
}
function FunctionExists(fn) {
	if (typeof(fn)== "string") {
		if (eval("typeof "+fn)=="function") {
			return true;
		}
	}
	return false;
}

function CometPeripherals() {
	var t = "";
	t += userid+d2+userpassword+d2; // example usage
	if (FunctionExists("PageSpecificCometInfo")) {
		t += PageSpecificCometInfo()+d2;
	} else {
		//alert("PageSpecificCometInfo doesn't exist");
	}
	return t;
}


var docomet = false;

function OpenCometConnection() {
	docomet = true;
	//alert("Comet peripheral information...\n\n"+CometPeripherals());
	CometWait(document.URL+d1+CometPeripherals());
}

function CloseCometConnection() {
	docomet = false;
}

function CometWait(instrux) {
	var CometReq = false;
	var selfcomet = this;
	// Mozilla/Safari
	if (window.XMLHttpRequest) {
		selfcomet.CometReq = new XMLHttpRequest();
	}
	// IE
		else if (window.ActiveXObject) {
			try {
				selfcomet.CometReq = new ActiveXObject("Msxml2.XMLHTTP");
			} catch (e) {
				try {
					selfcomet.CometReq = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {
				selfcomet.CometReq = false; // real trouble here - couldn't create ANY XHR
			}
		}
	}
	
	selfcomet.CometReq.open("POST", "comet.exe", true);
	selfcomet.CometReq.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
	selfcomet.CometReq.onreadystatechange = function() {
		//alert("selfcomet.CometReq.readyState: "+selfcomet.CometReq.readyState);
		if (selfcomet.CometReq.readyState == 4) {
			// data received...
			if (docomet) {
				if (selfcomet.CometReq.responseText!="null") {
					//alert("COMET DATA...\n\n"+selfcomet.CometReq.responseText);
					GenericCometResponder(selfcomet.CometReq.responseText);
				}
				// restart connection...
				//alert("RESTARTING COMET");
				setTimeout("OpenCometConnection()",50);
			}
		}
	}
	selfcomet.CometReq.send(instrux);
}
SLAVE EXECUTABLE:

Code: Select all

ContentLength = Val(GetEnvironmentVariable("CONTENT_LENGTH"))
ip.s = GetEnvironmentVariable("REMOTE_ADDR") ; client's ip address

If ContentLength>0
	*Buffer = AllocateMemory(ContentLength)
	hInput = GetStdHandle_(#STD_INPUT_HANDLE)
	ReadFile_(hInput, *Buffer, ContentLength, @bRead, 0)
	custominfo.s = URLDecoder(PeekS(*Buffer))
	FreeMemory(*Buffer)
	CloseHandle_(hInput)
EndIf




OpenConsole() ; needed for output




basefolder.s = GetPathPart(ProgramFilename())

Global cometfolder.s = basefolder+"comets\"
Macro CometRequestFile(code)
	cometfolder+code+".request"
EndMacro
Macro CometResponseFile(code)
	cometfolder+code+".response"
EndMacro
Macro CometResignationFile(code)
	cometfolder+code+".resign"
EndMacro




Repeat ; create a uniquely-named "request file"
	tokencode.s = ""
	For a = 1 To 10
		tokencode+Str(Random(8)+1)
	Next a
Until FileSize(CometRequestFile(tokencode)) = -1





f = CreateFile(#PB_Any,CometRequestFile(tokencode))
If f
	WriteStringN(f,custominfo+ip+"|")
	CloseFile(f)
Else
	; error creating request file. abort...
	End
EndIf




; wait for response...
starttime = ElapsedMilliseconds()
Repeat 
	
	If FileSize(CometResponseFile(tokencode))>-1
		; server has responded
		
		f = ReadFile(#PB_Any,CometResponseFile(tokencode))
		If f
			While Not Eof(f)
				fullreply.s + ReadString(f)+Chr(13)
			Wend
			CloseFile(f)
		EndIf
		
		deletion = DeleteFile(CometResponseFile(tokencode))
		
		fullreply = "Content-type: text/plain;charset=UTF-8"  + #CRLF$ + #CRLF$ + fullreply
		written = WriteConsoleData(@fullreply,MemoryStringLength(@fullreply))
		
		End
	EndIf
	
	Delay(25) ; change this to whatever you want. probably, a comet setup won't need to be very fast.
Until (ElapsedMilliseconds()-starttime)>30000 ; 30 seconds

; tell server we're no longer interested...
f = CreateFile(#PB_Any,CometResignationFile(tokencode))
If f : CloseFile(f) : EndIf

; send "null" reply to client...
fullreply.s = "Content-type: text/plain;charset=UTF-8"  + #CRLF$ + #CRLF$ + "null"
written = WriteConsoleData(@fullreply,MemoryStringLength(@fullreply))

End
SERVER INCLUDE:

Code: Select all

If cometfolder=""
	MessageRequester("COMET","Need to set folder where comet request and response files will be saved.",0)
EndIf


Structure CometStructure
	pagename.s
	ip.s
	info.s
EndStructure
Global NewMap waitingcomet.CometStructure()

Declare.b NewComet(code.s)
Declare.b EndComet(code.s,replied.b)



Macro ReviewComets
	
	NewMap delcomet.b()
	ForEach waitingcomet()
		code.s = MapKey(waitingcomet())
		If FileSize(CometResignationFile(code))>-1
			AddMapElement(delcomet(),code)
			DeleteFile(CometResignationFile(code))
		EndIf
	Next
	ForEach delcomet()
		code.s = MapKey(delcomet())
		EndComet(code,#False)
		DeleteMapElement(waitingcomet(),code)
	Next
	FreeMap(delcomet())
	
	
	d = ExamineDirectory(#PB_Any,cometfolder,"*.request")
	If d
		While NextDirectoryEntry(d)
			fn.s = DirectoryEntryName(d)
			code.s = StringField(fn,1,".")
			fn = cometfolder+fn
			If Not FindMapElement(waitingcomet(),code)
				;MessageRequester("NEW COMET",code,0)
				AddMapElement(waitingcomet(),code)
				f = ReadFile(#PB_Any,fn)
				If f
					ln.s = ReadString(f)
					waitingcomet(code)\pagename = GetURLPart(StringField(ln,1,#d1),#PB_URL_Path)
					waitingcomet(code)\info = StringField(ln,2,#d1)
					waitingcomet(code)\ip = StringField(ln,3,#d1)
					CloseFile(f)
					NewComet(code)
				EndIf
				DeleteFile(fn)
			EndIf
		Wend
		FinishDirectory(d)
	EndIf
	
EndMacro



Procedure.s CometForIPAndPage(ip.s,pagename.s)
	
	ForEach waitingcomet()
		If waitingcomet()\ip = ip
			If waitingcomet()\pagename = pagename
				ProcedureReturn MapKey(waitingcomet())
			EndIf
		EndIf
	Next
	
EndProcedure



Macro GiveCometInfo
	code.s = MapKey(waitingcomet())
	f = CreateFile(#PB_Any,cometfolder+code+".response")
	If f
		WriteStringN(f,sendinfo)
		CloseFile(f)
	EndIf
EndMacro

Macro DeleteSatedComets
	ForEach delcomet()
		code.s = MapKey(delcomet())
		EndComet(code,#True)
		DeleteMapElement(waitingcomet(),code)
	Next
EndMacro

Procedure.b Comet(sendinfo.s,cometcode.s)
	
	If FindMapElement(waitingcomet(),cometcode)
		GiveCometInfo
		EndComet(code,#True)
		DeleteMapElement(waitingcomet(),code)
		ProcedureReturn #True
	EndIf
	
EndProcedure

Procedure.b CometIP(sendinfo.s,ip.s)
	
	ForEach waitingcomet()
		If waitingcomet()\ip = ip
			GiveCometInfo
			EndComet(code,#True)
			DeleteMapElement(waitingcomet(),code)
			ProcedureReturn #True
		EndIf
	Next
	
EndProcedure

Procedure.b CometPage(sendinfo.s,client_pagename.s,client_eligibility.s="")
	
	NewMap delcomet.b()
	ForEach waitingcomet()
		;R("THIS ONE IS ON PAGE: "+waitingcomet()\pagename+c13+"TARGET PAGE: "+client_pagename)
		If waitingcomet()\pagename=client_pagename And (client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1))
			;R(MapKey(waitingcomet()))
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure

Procedure.b CometNotPage(sendinfo.s,client_pagename.s,client_eligibility.s="")
	
	NewMap delcomet.b()
	ForEach waitingcomet()
		If waitingcomet()\pagename<>client_pagename And (client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1))
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure

Procedure.b CometCast(sendinfo.s,client_eligibility.s="")
	
	;R("COMETS: "+Str(MapSize(waitingcomet())))
	NewMap delcomet.b()
	ForEach waitingcomet()
		;R("COMET CLIENT ON PAGE: "+waitingcomet()\pagename)
		If client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1)
			;R("SENDING TO CLIENT: "+MapKey(waitingcomet()))
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure

Procedure.b CometCast_OncePerIP(sendinfo.s,client_eligibility.s="")
	
	NewMap ipdone.b()
	NewMap delcomet.b()
	ForEach waitingcomet()
		If FindMapElement(ipdone(),waitingcomet()\ip) : Continue : EndIf
		If client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1)
			AddMapElement(ipdone(),waitingcomet()\ip)
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure

Procedure.b CometCast_OncePerPage(sendinfo.s,client_eligibility.s="")
	
	NewMap pagedone.b()
	NewMap delcomet.b()
	ForEach waitingcomet()
		If FindMapElement(pagedone(),waitingcomet()\pagename) : Continue : EndIf
		If client_eligibility="" Or FindString(waitingcomet()\info,client_eligibility,1)
			AddMapElement(pagedone(),waitingcomet()\pagename)
			GiveCometInfo
			AddMapElement(delcomet(),code)
		EndIf
	Next
	DeleteSatedComets
	
EndProcedure

Re: Simple Comet framework

Posted: Mon Jul 26, 2010 8:30 am
by HAnil
Hi Seymour Clufley,
I didn't run second version. maybe I mixed some code. do you resend running second version to me by e-mail ? or would you post full code ?

myfirst post is about screen is "MZ" code. this is executable start letter code. I tried many times, but result is not changed.
I guess, when I start the browser, comet.exe runs and startup exe letter filled "MZ" capital.
thanks,