Alaska Software Inc. - IDSC on SOCKETFDSETNEW
Username: Password:
AuthorTopic: IDSC on SOCKETFDSETNEW
Jose Adriano Baltieri IDSC on SOCKETFDSETNEW
on Sat, 05 Feb 2005 10:19:42 -0200
I'm having some IDSC occuring on SOCKETFDSETNEW.

I cant isolate it cause the real application is too complex. Have tried but, 
at the prototype the error wont occur. And the prototype is too complex already.

What's the real application ?

I open a TELNET client socket on a thread and create at the same time a 
descriptor set. Because later I'll use SOCKETSELECT to "sense" if I have data 
waiting at this socket (are there incoming messages ?).

Below I have a simplified scheme of the operations I'm doing.

Just would like that someone else may have a look and point if the order of 
operations is wrong, or if I'm doing something risky or mistakenly.

Be aware that these operations are performed on a thread. Also, connection and 
disconnection are protected by a "critical section" or, a SYNC METHOD. I tried 
this bottleneck to see if I could solve the problem but it didnt. My 
hypothesis is that it wouldnt be a good idea to have two or more threads 
connecting or disconnecting at the same time. As for the communication itself 
I cant bottleneck it of course cause it would be horrible. So, open/close of a 
socket on a single pass. One at a time. Communication happens N at a time.

Follows the operations :

On socket opening, I do this :

SOCKETNEW(AF_INET,SOCK_STREAM,IPPROTO_TCP)
SOCKETCONNECT(LNSOCKET,AF_INET,LCIP,23)
SOCKETSETBLOCKINGMODE(LNSOCKET,FALSE)

SOCKETFDSETNEW(@LNERROR)   here comes the IDSC. Always happens here
SOCKETFD_SET(LNSOCKET,LNDS)

Then I add an array like this {LNSOCKET,LNDS} to a PUBLIC array. As a matter 
of fact, this PUBLIC array is an array of terminals that my application holds 
on. The application is called TELNET CONNECTION KEEPER and, this array is the 
"KEEPER" itself.

Several threads will operate with lines of this array. So, one thread searches 
this array for a terminal and does communication through it. Other thread may 
catch the same terminal later, but not simultaneously, of course.

For each terminal I have a thread ? No. I may have say 30 terminals opened and 
only 10 threads for instance. A thread receives a web transaction from a user 
and will communicate using that user's terminal (which is on that array). 
After sending result back to the browser, this thread will end it's job and 
await for another transaction, from other user, other terminal.



During communication, which I call XMIT/RECEIVE I do this :

SOCKETSEND
REPEAT
   SOCKETRECV(...MSG_NORMAL)
   SOCKETSELECT
   IF SOCKETSELECT returns 0 then
      IF  NOT  SOCKETFD_ISSET(mysocket)
          SOCKETFD_SET(mysocket)
      END IF
   END IF
    I dont know why but sometimes the socket is lost from the descriptor set,
    then I have to add it again... I guess that when I have a timeout
    the socket falls out of the descriptor
UNTIL I HAVE special ending character (ETX) at the end of the message



On closing :

SOCKETFD_ZERO
SOCKETFDSETDELETE
SOCKETSHUTDOWN
SOCKETCLOSE


So, after all, is this order of operations OK ?

Something risky ?

Something I could do to have a better diagnosis of what's wrong ?

Something to prevent the IDSC or at least to survive after it ?

A way to simplify things ?




thanXs for your suPPort !
Phil Ide
Re: IDSC on SOCKETFDSETNEW
on Mon, 07 Feb 2005 11:08:31 +0000
Adriano,

> I'm having some IDSC occuring on SOCKETFDSETNEW.

I'm not sure why you are setting a socket-set.  This is only required in
functionally challenged environments such as VB which are single-threaded,
and there a single test will determine whether this is incoming data on any
of a given set of sockets.

In a multi-threaded environment, where each socket is handled in it's own
thread, simply ..recv() the data.  If the socket is blocking, this will
pause the thread until either data is received or the socket is closed.

In a non-blocking socket, it will return immediately either with data (if
any was pending) or without.

Alternatively, you can make the socket event driven, so when data arrives
at the socket, a function is automatically called to process the data.

The only reason you should want to handle a socket-set that I can see, is
when you have to monitor multiple sockets in a single thread.

Regards,

Phil Ide

***************************************
* Xbase++ FAQ, Libraries and Sources: *
* goto: http://www.idep.org.uk/xbase  *
***************************************

Do not pay any attention to this.
Jose Adriano Baltieri Re: IDSC on SOCKETFDSETNEW
on Mon, 07 Feb 2005 09:21:53 -0200
Phil Ide wrote:

> Adriano,

Hi Phil !

What a coincidence. It's Carnival here in Brazil but I'm working. I came "dry 
and thirsty" to see if there was an answer and there was none...

Then, WHILE I was doing some other postings, you replied.

I guess I should make some cards on Lottery with 09:08 !
> 
> 
>>I'm having some IDSC occuring on SOCKETFDSETNEW.
> 
> 
> I'm not sure why you are setting a socket-set.  This is only required in
> functionally challenged environments such as VB which are single-threaded,
> and there a single test will determine whether this is incoming data on any
> of a given set of sockets.
>
> In a multi-threaded environment, where each socket is handled in it's own
> thread, simply ..recv() the data.  If the socket is blocking, this will
> pause the thread until either data is received or the socket is closed.

I wanna have a timeout procedure.

> 
> In a non-blocking socket, it will return immediately either with data (if
> any was pending) or without.
> 
> Alternatively, you can make the socket event driven, so when data arrives
> at the socket, a function is automatically called to process the data.

That would be better, more stethical. Don't know how to do that, however.

How to hook a code block to a TCP/SIP event ?

> The only reason you should want to handle a socket-set that I can see, is
> when you have to monitor multiple sockets in a single thread.

Don't do that. Each thread has only one socket....


Basically, what I need is this :

EMIT data through the socket
AWAIT until there is data

The "AWAIT" has some requirements :

- I don't want to consume CPU for that or, I want to use the minimum CPU.
- Need to have a maximum timeout, which is 90 seconds nowadays.
- I consider that data is ready to be grabbed when there is a special set of 
characters in it. They are iac + eor (CHR(255) + CHR(239)).


So, could I do the same without using Socket Descriptors ?





> Regards,
Phil Ide
Re: IDSC on SOCKETFDSETNEW
on Mon, 07 Feb 2005 13:50:50 +0000
Adriano,

> I wanna have a timeout procedure.

Then you should look at a better way to do it.

Using event-driven sockets allows your thread to monitor idle-time.  For
example:

   nEvent := AppEvent( @mp1, @mp2, @oXbp, nTimeout )

This will receive an FD_READ event when data is pending, FD_CLOSE when the
client attempts to close the connection, and the event-wait will timeout
after nTimeout expires.

Note that even though your event-handler has timed-out, the socket hasn't.
If you want to close the socket now, you'll have to issue a SocketClose()
on the socket.  NOte that this will cause an event to be fed into the event
queue, so you should re-enter the wait-state to process it.

The other way to do it, is to set a timeout on the socket itself.  Using
Xb2.NET this is simplicity itself, and also allows you to set different
timeouts for send and receive.  You can also do the same with ASINET, but
need to call a WinAPI routine (SetSocketOpt()) to do this.

>> Alternatively, you can make the socket event driven, so when data arrives
>> at the socket, a function is automatically called to process the data.
> 
> That would be better, more stethical. Don't know how to do that, however.
> 
> How to hook a code block to a TCP/SIP event ?

You don't do it like that.  You create a window using the WinAPI and in the
windows WinProc you watch out for an XbeP_User event.  When the XbeP_User
event you have specified occurs, you use _conCall() to invoke your
favourite Xbase++ function to handle the event.

Once the window has been created, set the socket to send events to the
event queue using WSAAsyncSelect().

For an existing application, this is a non-trivial excercise because not
only do you have to change your code path for client-oriented sockets, you
also have to change it for your listening socket (making the listening
socket event driven is good because all subsequent client-sockets generated
inherit the even-driven configuration).

> Basically, what I need is this :
> 
> EMIT data through the socket
> AWAIT until there is data
> 
> The "AWAIT" has some requirements :
> 
> - I don't want to consume CPU for that or, I want to use the minimum CPU.
> - Need to have a maximum timeout, which is 90 seconds nowadays.
> - I consider that data is ready to be grabbed when there is a special set of 
> characters in it. They are iac + eor (CHR(255) + CHR(239)).
> 
> 
> So, could I do the same without using Socket Descriptors ?

Yes.  When in ..recv() or ..send() call, no CPU time is consumed until data
arrives or while actually sending.

See above for 2 techniques.  Additionally, you could spawn another thread
to monitor other threads handling client-connections.  This thread should
maintain an array of socket descriptors and a time when they should
timeout, and periodically scan the array to see if a socket has time out.
If it finds one, simply close the socket and remove the data from the
array.  Each time the cleint-thread enters a ..recv() call, update the
timeout.  On exit from a ..recv() call, set the next timout to something
that will not cause the monitor thread to timeout the socket before you
re-enter the ..recv() call.

When you close the socket for any reason other than a timeout, remember to
remove the socket from the monitor thread!

You should NEVER wait for specific characters in the input stream.  There
is a possibility that the input buffer of the ..recv() function is not
large enough to hold sufficient data to make these characters visible.
Even if the input buffer is large enough, there is no guarantee that the
sending client's send buffer is large enough to transmit that data
(requiring you to empty the buffer in order to let the client write more).

Whenever you see data on the socket - fetch it! You may need to buffer
retrieved data to build a complete message, but don't leave data in a
socket longer than you have to.

Regards,

Phil Ide

***************************************
* Xbase++ FAQ, Libraries and Sources: *
* goto: http://www.idep.org.uk/xbase  *
***************************************

As I said before, I never repeat myself.
Jose Adriano Baltieri Re: IDSC on SOCKETFDSETNEW
on Wed, 09 Feb 2005 16:39:55 -0200
Phil Ide wrote:

>    nEvent := AppEvent( @mp1, @mp2, @oXbp, nTimeout )
> 
> This will receive an FD_READ event when data is pending, FD_CLOSE when the
> client attempts to close the connection, and the event-wait will timeout
> after nTimeout expires.

This is nice but I would have to change my whole application...
Cant do that right now....



> The other way to do it, is to set a timeout on the socket itself.  Using
> Xb2.NET this is simplicity itself, and also allows you to set different
> timeouts for send and receive.  You can also do the same with ASINET, but
> need to call a WinAPI routine (SetSocketOpt()) to do this.

XB2.NET - I would have to buy it. Also cant afford...

SetSocketOption, from ASINET itself, doesnt seem to have anything related to 
timeout

Would I be able to DLLCALL something in order to set what you said ?

This alternative, to have a timeout for the socket itself, that is, inside the 
socket itself, would be the best cause it wont disturb my application so much.

Thanks again !
Phil Ide
Re: IDSC on SOCKETFDSETNEW
on Thu, 10 Feb 2005 10:30:41 +0000
Adriano,

>>    nEvent := AppEvent( @mp1, @mp2, @oXbp, nTimeout )
>> 
>> This will receive an FD_READ event when data is pending, FD_CLOSE when the
>> client attempts to close the connection, and the event-wait will timeout
>> after nTimeout expires.
> 
> This is nice but I would have to change my whole application...
> Cant do that right now....

That's the thing with event-driven sockets - it's not a simple change, it
completely changes the architecture of your application, so needs to be
done at the start.

Regards,

Phil Ide

***************************************
* Xbase++ FAQ, Libraries and Sources: *
* goto: http://www.idep.org.uk/xbase  *
***************************************

Hell hath no pizza
Jose Adriano Baltieri Re: IDSC on SOCKETFDSETNEW
on Wed, 09 Feb 2005 16:47:52 -0200
Phil Ide wrote:

> Adriano,
> 
> 
> The other way to do it, is to set a timeout on the socket itself.  Using
> Xb2.NET this is simplicity itself, and also allows you to set different
> timeouts for send and receive.  You can also do the same with ASINET, but
> need to call a WinAPI routine (SetSocketOpt()) to do this.

Of course I'd prefer to use ASINET itself for specifying this TIMEOUT for 
receiving data from a socket.

At the docs, there isn't such parameter.

However, at the Asinetc.ch, on line #101, there's a define for that :

#define SO_RCVTIMEO     0x1006          /* receive timeout */

Would it be a problem of no documentation or shouldn't these DEFINEs be used ?

Or should they be used internally, only ?
Phil Ide
Re: IDSC on SOCKETFDSETNEW
on Thu, 10 Feb 2005 10:29:05 +0000
Adriano,

> Of course I'd prefer to use ASINET itself for specifying this TIMEOUT for 
> receiving data from a socket.
> 
> At the docs, there isn't such parameter.
> 
> However, at the Asinetc.ch, on line #101, there's a define for that :
> 
> #define SO_RCVTIMEO     0x1006          /* receive timeout */
> 
> Would it be a problem of no documentation or shouldn't these DEFINEs be used ?
> 
> Or should they be used internally, only ?

It will probably work, provided you call the function correctly.  If I get
a chance, I'll investigate this later today (but no promises, I have a busy
day ahead).

Regards,

Phil Ide

***************************************
* Xbase++ FAQ, Libraries and Sources: *
* goto: http://www.idep.org.uk/xbase  *
***************************************

Call your Father today: I wish I still COULD!
Jose Adriano Baltieri Re: IDSC on SOCKETFDSETNEW
on Thu, 10 Feb 2005 08:49:43 -0200
Phil Ide wrote:
> Adriano,
> 
> 
>>Of course I'd prefer to use ASINET itself for specifying this TIMEOUT for 
>>receiving data from a socket.
>>
>>At the docs, there isn't such parameter.
>>
>>However, at the Asinetc.ch, on line #101, there's a define for that :
>>
>>#define SO_RCVTIMEO     0x1006          /* receive timeout */
>>
>>Would it be a problem of no documentation or shouldn't these DEFINEs be used ?
>>
>>Or should they be used internally, only ?
> 
> 
> It will probably work, provided you call the function correctly.  If I get
> a chance, I'll investigate this later today (but no promises, I have a busy
> day ahead).

Thank you !

I thought about asking Alaska what the DEFINEs are there for.
Would it simply be a "Berkeley Save As" issue or they are just hidden at the 
documentation. Someone forgot to mention them.

If I have the time today, I'm also curious to write a test.

See ya.


> 
> Regards,
Phil Ide
Re: IDSC on SOCKETFDSETNEW
on Thu, 10 Feb 2005 12:42:01 +0000
Adriano,

>> It will probably work, provided you call the function correctly.  If I get
>> a chance, I'll investigate this later today (but no promises, I have a busy
>> day ahead).
> 
> Thank you !
> 
> I thought about asking Alaska what the DEFINEs are there for.
> Would it simply be a "Berkeley Save As" issue or they are just hidden at the 
> documentation. Someone forgot to mention them.
> 
> If I have the time today, I'm also curious to write a test.

There are three main reasons why something is in Xbase++ but undocumented:

1.  The feature was added, but not fully tested or known to be flaky
2.  The feature was added for a particular use, used internally, and
    not guaranteed to work in other circumstances.
3.  The feature is intended only for internal use, and Alaska reserve the
    right to change it's name, location (library), API or even remove it
    altogether (::noIvarCallBack() and ::noMethodCallback() are prime
    examples).

Additionally, something might simply have been 'forgotten' when the
documentation was updated, but I really wouldn't count on this 

Regards,

Phil Ide

***************************************
* Xbase++ FAQ, Libraries and Sources: *
* goto: http://www.idep.org.uk/xbase  *
***************************************

Fanatic: can't change his mind, won't change the subject.