Hello Francois,
I've checked several times and I don't believe that I'm calling anything I
shouldn't but I could be wrong of course. Yes, I know ICS can handle fast
large scale communications.
Pardon the long text reply but below is the full code for the Socket's client
thread's Execute method. If you or anyone else sees anything wrong, please let
me know. Note, because of the problems I've been having, my override of the
Send method posts the data to be sent to the background thread via a
PostMessage() operation. I know that the TWSocket.Send() method is thread-safe
and is protected by a critical section, but I was desperate and wanted to
remove any chance of a bad inter-thread access causing a problem. The BgSend()
call you will see in the thread's Execute method is just a call to the
inherited send method (TWSocket). TWSocketClientDeluxe is my sub-class of
TWSocket:
procedure TClientThread.Execute;
procedure validateSocketParameters(theReason: string);
begin
if WSocket.Addr = '' then
raise Exception.Create('(TClientThread.Execute) ' + theReason + '
requested without the Addr parameter having a valid value.');
if WSocket.Port = '' then
raise Exception.Create('(TClientThread.Execute) ' + theReason + '
requested without the Port parameter having a valid value.');
if WSocket.Proto = '' then
raise Exception.Create('(TClientThread.Execute) ' + theReason + '
requested without the Proto parameter having a valid value.');
end;
// ---------------------------------------------------------------
procedure checkPublishStatistics(theSocket: TWSocketClientDeluxe;
bShuttingDown: boolean);
var
intfSocketStatistics: IClientSocketDeluxeStatistics;
bPublish: boolean;
begin
intfSocketStatistics := nil;
bPublish := false;
// Do we have anyone waiting for statistics?
if WSocket is TWSocketClientDeluxe then
begin
with WSocket as TWSocketClientDeluxe do
begin
if notifyStatisticsHandle > 0 then
begin
if bShuttingDown then
begin
// We're shutting down. Just publish a "blank"
// statistics record.
intfSocketStatistics :=
TClientSocketDeluxeStatistics.Create(Name, ID);
end
else
// Calculate the statistics and publish them.
intfSocketStatistics :=
TClientSocketDeluxeStatistics.Create(WSocket as TWSocketClientDeluxe);
// Are we shutting down?
if bShuttingDown then
begin
// Publish an empty statistics record to let
// the notifyee we are done.
// intfSocketStatistics.statistics :=
newClientSocketDeluxeStatistics;
bPublish := true
end
// Has one second elapsed since we last published
// statistics or are we shutting down? (Need
// to
else if diffMilliseconds(FLastStatisticsPublished_dt) >
1000 then
begin
// Publish statistics.
bPublish := true;
end;
if bPublish then
begin
// Post it to the notification handle.
if notifyStatisticsHandle = 0 then
raise
Exception.Create('(TClientThread.Execute::checkPublishStatistics) The handle
for statistics update notifications is zero.');
PostMessageWithUserDataIntf(notifyStatisticsHandle,
WM_CLIENT_SOCKET_DELUXE_STATISTICS, POSTMESSAGEUSERDATA_LPARAM_IS_INTF,
intfSocketStatistics);
// Update the time statistics were last published.
FLastStatisticsPublished_dt := Now;
end; // if bPublish then
end; // if notifyStatisticsHandle > 0 then
end; // with WSocket as TWSocketClientDeluxe do
end; // if WSocket is TWSocketClientDeluxe then
end;
var
errMsg: string;
MsgRec : TMsg;
theSockCliDeluxe: TWSocketClientDeluxe;
theSocketName: string;
intfSocketStatistics: IClientSocketDeluxeStatistics;
intf: IPostMessageUserData;
intfCliSockSendDataReq: IClientSocketSendDataRequest;
P: Pointer;
S: string;
begin
intfSocketStatistics := nil;
intfCliSockSendDataReq := nil;
try
theSocketName := '(unassigned)';
if not Assigned(WSocket) then
// exit;
raise Exception.Create('(TClientThread.Execute) The client socket
is unassigned.');
theSocketName := WSocket.Name;
// 1-24-2012: Added empty component name check.
if theSocketName = '' then
raise Exception.Create('(TClientThread.Execute) The client socket
is nameless.');
// Set our thread name.
setThreadName_delphi(theSocketName + '_clientthread');
{ Attach client socket to this thread
}
WSocket.ThreadAttach;
// I assume that if multithreading is being used than we want
// higher than normal priority.
// SetThreadPriority(ThreadID, THREAD_PRIORITY_ABOVE_NORMAL);
{ Signal main thread that we've attached socket to this thread
}
ThreadAttached := TRUE;
{ Now let the main thread continue starting the connection.
}
{ This little Sleep avoids race condition.
}
Sleep(0);
// Check for auto-connect flag being set to TRUE.
if FAutoConnect then
begin
// Validate the Addr/Port/Proto parameters.
validateSocketParameters('Auto-connect');
OutputDebugString(PChar(
'(TClientThread.Execute) Calling CONNECT for the socket named:
' + theSocketName
));
// Connect.
WSocket.Connect;
end; // if FAutoConnect then
{ Then process messages until WM_QUIT message is posted.
}
{ TWSocket is event-driven. So even when used within a thread, we
}
{ have to have a "message pump". Any message pump will do and there
}
{ is one built in TWSocket, so use it !
}
// WSocket.MessageLoop;
{
We need our own message loop to catch WM_BG_CUSTOM_REQUEST
messages. The code below is identical to WSocket.MessageLoop
with the additional handling we require.
}
FLastStatisticsPublished_dt := Now;
{ If GetMessage retrieves the WM_QUIT, the return value is FALSE and
}
{ the message loop is broken.
}
while GetMessage(MsgRec, 0, 0, 0) do
begin
if MsgRec.hwnd = 0 then {<== ** VERY IMPORTANT ** }
begin
// --------------- PostThreadMessage() ---------------------
// The window handle field is 0 indicating this is one of
// our messages posted to this thread using
// PostThreadMessage(). Handle it.
// Is it a request to execute a custom background thread
request?
if MsgRec.message = WM_BG_CUSTOM_SOCKET_REQUEST then
begin
// ATI: 1-10-2012: Custom background thread request.
//
// If our owner socket is a TWSocketClientDeluxe instance
and
// we have a custom background request event handler, then
// call it now.
if WSocket is TWSocketClientDeluxe then
begin
theSockCliDeluxe := TWSocketClientDeluxe(WSocket);
if Assigned(theSockCliDeluxe.OnBgCustomRequestProc) then
theSockCliDeluxe.OnBgCustomRequestProc(MsgRec);
end; // if WSocket is TWSocketClientDeluxe then
end
// Is it a request to reconnect with the same settings as the
// last connect (current settings)?
else if MsgRec.message = WM_BG_SOCKET_RECONNECT then
begin
// If we are not "closed" then this is an error.
if not (WSocket.State = wsClosed) then
raise Exception.Create('(TClientThread.Execute) A
reconnect request was received when our socket was not in the "closed" state.
Socket name: ' + WSocket.Name);
// --------------- RECONNECT ---------------
// Validate the Addr/Port/Proto parameters.
validateSocketParameters('Reconnect');
// Here! Kludge.
{
For some reason, despite the Addr/Port/Proto assignments being
valid,
the F*Assigned (e.g. - FPortAssigned) values are FALSE. There is a
bug
somewhere that we should fix, but for now we are redoing the
assignments
simply to get those flags set to TRUE.
}
WSocket.Addr := WSocket.Addr;
WSocket.Port := WSocket.Port;
WSocket.Proto := WSocket.Proto;
// Re-connect.
WSocket.Connect;
end // if (GAUNTLET) then
else if MsgRec.message = WM_BG_SOCKET_SEND_REQUEST then
begin
// -------------- SEND DATA REQUEST ------------------
// Recover the send data request interface.
intf := nil;
P := Pointer(MsgRec.lParam);
if not Assigned(P) then
raise Exception.Create('(TClientThread.Execute) Long
parameter was not assigned, received pointer invalid while recovering the
IPostBufferToCollection interface.');
Move(P, intf, sizeof(intf));
if not Assigned(intf) then
raise Exception.Create('(TClientThread.Execute)
Received an unassigned interface object.');
if not Supports(intf, IClientSocketSendDataRequest,
intfCliSockSendDataReq) then
raise Exception.Create('(TClientThread.Execute)
Interface object received is not a IClientSocketSendDataRequest interface.');
if not Assigned(intfCliSockSendDataReq.data) then
raise Exception.Create('(TClientThread.Execute) The
send data request has an unassigned data pointer.');
if intfCliSockSendDataReq.dataLen <= 0 then
raise Exception.Create('(TClientThread.Execute) The
send data request''s buffer length is less than or equal to 0.');
// Send the data now but use the background send method or
we
// will create a recursive self-posting situation.
with WSocket as TWSocketClientDeluxe do
begin
// S := intfCliSockSendDataReq.dataAsStr;
BgSend(intfCliSockSendDataReq.data,
intfCliSockSendDataReq.dataLen);
end; // with WSocket as TWSocketClientDeluxe do
// Release the interface variables we used.
intf := nil;
intfCliSockSendDataReq := nil;
end; // else - if (GAUNTLET) then
// See if it's time to publish a statistics update.
checkPublishStatistics(WSocket as TWSocketClientDeluxe, false);
end
else
begin
// --------------- TWSocket WndProc() ---------------------
// Message is meant for TWSocket component.
TranslateMessage(MsgRec);
// Pass it on.
DispatchMessage(MsgRec);
end; // else -
// ------------ SOCKET STATISTICS UPDATE ---------------------
end; // while()
{ Be sure to have main thread waiting for termination before
terminating}
Sleep(0);
// Final publish (empty statistics record).
checkPublishStatistics(WSocket as TWSocketClientDeluxe, true);
{ Detach the hidden window from within the thread
}
WSocket.ThreadDetach;
// Unblock the main thread if it is waiting on us to exit and it gave us
// an Event to signal.
if Assigned(FThreadTerminate_event) then
begin
if not FThreadTerminate_event.SetEvent then
begin
errMsg := 'Terminate thread event was unable to be signaled
during the exiting of this client thread. Socket name: ' + FWSocket.Name;
postComponentLogMessage_error(errMsg, FWSocket.Name);
OutputDebugString(PChar(errMsg));
end;
end; // if Assigned(FThreadTerminate_event) then
except
On E: Exception do
begin
// Post a message to the component log.
postComponentLogMessage_error('ERROR in client thread for socket('
+ theSocketName +'). Details: ' + E.Message, Self.ClassName);
// Continue raising the Exception.
raise;
end;
end; // try
end;
// ---------------------------------------------------------------
Thanks,
Robert
--- On Mon, 2/6/12, François Piette <[email protected]> wrote:
> From: François Piette <[email protected]>
> Subject: Re: [twsocket] Problems with TWSocket and Skype
> To: "'ICS support mailing'" <[email protected]>
> Date: Monday, February 6, 2012, 10:01 PM
> It is likely that your problem comes
> from the different programming paradigm
> between the two libraries. ICS is non-blocking
> (asynchronous) while the
> other is blocking. It is possible that you don't follow the
> rules for
> asynchronous programming and get problems... There are two
> very important
> rules: 1) Never call directly or indirectly the message pump
> from one of the
> component event and 2) never forget that all calls to
> methods - such as Send
> - are merely requests and that you get control back
> immediately while your
> request execute in the background.
>
> If you follow the rules, everything will be OK. You can
> verify by yourself
> that ICS components are capable of high speed communication
> with a huge
> number of simultaneous connections, both client or server
> side. See the HTTP
> and FTP client and server to convince yourself.
>
> --
> [email protected]
> The author of the freeware multi-tier middleware MidWare
> The author of the freeware Internet Component Suite (ICS)
> http://www.overbyte.be
>
>
>
>
> -----Message d'origine-----
> De : [email protected]
> [mailto:[email protected]]
> De la
> part de robertoschler
> Envoyé : mardi 7 février 2012 05:07
> À : TWSOCKET
> Objet : [twsocket] Problems with TWSocket and Skype
>
> Hello,
>
> A couple of years ago I tried using TWSocket with Skype to
> send audio back
> and forth between my Delphi 6 application and the Skype
> client. I never
> could get it to work until I switched to the Indy
> components.
>
> Since then I've used ICS in several applications they have
> always worked
> great. Now I have another application that interfaces
> with Skype and again
> I am having trouble trying to get TWSocket to work with
> Skype.
>
> My application acts as a middleman between an external WiFi
> webcam device
> relaying audio from its microphone to Skype's input audio
> port and relaying
> audio from Skype's output audio port to the webcam device's
> speaker. I am
> using ICS on both sides now. TWSocket works just fine
> with the external
> WiFi webcam, however, I can't make it work with Skype.
> The audio going to
> Skype is frequently "jamming up" whereby the buffered byte
> count rises
> quickly for short durations, enough to make the audio stream
> going to Skype
> unusable (calling TWSocket.Send). The socket receiving
> audio from Skype
> receives about 11 to 20 data deliveries successfully and
> then just dies
> (OnDataAvailable stops firing). The connection stays
> open, but Skype stops
> sending audio permanently.
>
>
> Both sockets for the pair of connections are spawned by a
> listening socket.
> The way you tell the Skype client to receive audio from your
> application is
> to open a socket on a port number of your choice and
> Listen. You then tell
> Skype the port number you are using and Skype connects to
> you on that port.
> The same goes for the socket you use send audio to
> Skype. In both cases you
> Listen and Skype connects to you on the given port.
> The difference of
> course being that you repeatedly handle OnDataAvailable()
> events on the
> socket that is receiving audio from Skype, and repeatedly
> call Send() on the
> socket that is sending audio to Skype.
>
> I'd rather keep things ICS all around but I'm close to
> switching to Indy on
> the Skype side. I recently found out that Skype uses
> Indy for its Windows
> clients. I'm also aware that Indy uses a thread that
> blocks to do its work
> as opposed to TWSocket which uses a message loop.
>
> Can anyone speculate as to what about Indy's sockets are
> more compatible
> with Skype than (at least for me) TWSocket sockets?
> What could I try to
> quickly fix this situation, perhaps by more closely
> emulating Indy's
> behavior?
>
> Thanks,
> Robert
> --
> To unsubscribe or change your settings for TWSocket mailing
> list please goto
> http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
> Visit our website at http://www.overbyte.be
>
> --
> To unsubscribe or change your settings for TWSocket mailing
> list
> please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
> Visit our website at http://www.overbyte.be
>
--
To unsubscribe or change your settings for TWSocket mailing list
please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be