Page 1 of 2

Tsocket Bug

Posted: Mon Apr 19, 2010 12:55 am
by Davide
Hello all.

Small sample taken from senddata.prg:

Code: Select all

// Sockets use sample developed by Enrico M.G.

#include "Fivewin.ch" 

FUNCTION MAIN() 

    LOCAL oWnd 

    DEFINE WINDOW oWnd 

    @ 1, 2 BUTTON "Send"; 
           SIZE 100, 50; 
           ACTION SENDDATA( 25, "209.191.88.254")   // yahoo.com - Infinite loop
//           ACTION SENDDATA( 25, "209.85.135.111") // smtp.gmail.com - This works good

    ACTIVATE WINDOW oWnd 

RETURN NIL 

STATIC FUNCTION SENDDATA( nPort, cIP, cMsg ) 

    LOCAL oSocket := TSocket():New( nPort )

    oSocket:lDebug  :=.t.
    oSocket:cLogFile:="senddata.log"

    oSocket:bConnect := { || oSocket:GetData() , SysWait(1) , oSocket:SendData( "HELO TEST"+CRLF ), SysWait(1),;
                             oSocket:GetData() , SysWait(1) , oSocket:SendData( "QUIT"+CRLF ), SysWait(1),;
                             oSocket:GetData() , SysWait(1) , oSocket:End() }

    oSocket:Connect( cIP ) 

RETURN NIL
When it's run through smtp.gmail.com you obtain a senddata.log file like follows:
04/19/10 02:23:14: Connect Socket handle: 340
04/19/10 02:23:14:
04/19/10 02:23:14: Write Socket handle: 340
04/19/10 02:23:14: Read Socket handle: 340
04/19/10 02:23:16: 220 mx.google.com ESMTP j10sm25406824mue.48
250 mx.google.com at your service

04/19/10 02:23:17: Read Socket handle: 340
04/19/10 02:23:17: Close Socket handle: 340
04/19/10 02:23:18: 221 2.0.0 closing connection j10sm25406824mue.48
While with yahoo.com ...
04/19/10 02:26:31: Connect Socket handle: 340
04/19/10 02:26:31:
04/19/10 02:26:32: Sent: -1 Len: 11 Buffer Len: 0 Error: 10057
04/19/10 02:26:32: Sent: -1 Len: 11 Buffer Len: 0 Error: 10057
04/19/10 02:26:32: Sent: -1 Len: 11 Buffer Len: 0 Error: 10057
04/19/10 02:26:32: Sent: -1 Len: 11 Buffer Len: 0 Error: 10057
04/19/10 02:26:32: Sent: -1 Len: 11 Buffer Len: 0 Error: 10057
... infinite
I tried manually telnetting the yahoo IP and it kindly reply me a couple of text lines stating that I'm disallowed because I'm on a residential/dynamic IP, then it puts the connection down, but that's not the problem (this is just a sample I made for you to understand the problem).
The problem is that looks like a WSA error in SendData() like the above puts tSocket in an infinite loop instead of retrieving those 2 lines of text, that are informative and helpful to know why the 10057 error was triggered, and then closing the socket.

How could I fix it ?

Thanks,
Davide

Re: Tsocket Bug

Posted: Mon Apr 19, 2010 2:53 am
by Bayron
Davide,

Do you have a Yahoo Mail Plus account???

If you don't, that may be the problem......

Re: Tsocket Bug

Posted: Mon Apr 19, 2010 7:05 am
by xProgrammer
Hi Davide

At face value it looks like yahoo is dropping the connection (as per your telnet experience) and you pc keeps retrying the failed connection. Given your "success" with the gmail address it is less likely to be a "bug" in tsocket.

I have mainly done IP socket programming from[x]Harbour running on Linux, but I have successfully run sessions between Linux and Windows pcs. I know that I had to fiddle with timeouts and looping to get code that was working properly on Linux hosts to work on Windows hosts.

In case its of any use, here is the TSocket class that I am currently using:

Code: Select all

// Socket.prg

#include "hbclass.ch"

CLASS TSocket

  CLASSDATA log_Initialised INIT .F.
  DATA str_IPAddress
  DATA int_PortNumber
  DATA pSocket
  DATA log_Connected
  DATA str_Data

  METHOD New() CONSTRUCTOR
  METHOD SetIP( str_IPAddress )
  METHOD SetPort( int_PortNumber )
  METHOD CreateQueryObject()
  METHOD Connect()
  METHOD Send( cMessage )
  METHOD Receive()
  METHOD Close()
  METHOD SendReceive( cMessage )
  METHOD CleanUp()

ENDCLASS

METHOD New() CLASS TSocket

  ::str_IPAddress := "127.0.0.1"
  ::int_PortNumber := 1800
  IF !::log_Initialised
     INetInit()
     ::log_Initialised := .T.
  ENDIF

  RETURN self

METHOD SetIP( str_IPAddress ) CLASS TSocket

  ::str_IPAddress := AllTrim( str_IPAddress )

  RETURN nil


METHOD SetPort( int_PortNumber ) CLASS TSocket

  ::int_PortNumber := int_PortNumber

  RETURN nil


METHOD CreateQueryObject()

  RETURN( TQuery():New( self ) )


METHOD Connect() CLASS TSocket

  LOCAL log_OK
  LOCAL log_Retry

  log_Retry := .T.
  DO WHILE log_Retry
    log_OK := .T.
    TRY
      ::pSocket := INetConnect( ::str_IPAddress, ::int_PortNumber )
    CATCH
      MsgInfo( "Unable to connect to data server" )
      log_OK := .F.
    END
    IF log_OK
      IF INetErrorCode( ::pSocket ) == 0
        ::log_Connected := .T.
        RETURN .T.
       ELSE
        ? "Socket error:", INetErrorDesc( ::pSocket )
      ENDIF
    ENDIF
    log_OK := .F.
    ::log_Connected := .F.
    // ? "Press [Esc] to exit, [Enter] to retry"
    // IF InKey( 0 ) == 27
    IF !MsgYesNo( "Can't communicate with the server. Do you want to retry?", "Communication Error" ) 
      INetCleanUp()
      log_OK := .F.
      ProgExit()
    ENDIF
  ENDDO


METHOD Send( var_Message ) CLASS TSocket

  INetSend( ::pSocket, var_Message )

  RETURN nil


METHOD Receive() CLASS TSocket

  LOCAL str_Buffer
  LOCAL int_Bytes

  INetSetTimeOut( ::pSocket, 1000 )
  ::str_Data := ""
  int_Bytes := 1
  DO WHILE int_Bytes > 0
    ? "in loop"
    str_Buffer := Space( 1024 )
    int_Bytes := INetRecv( ::pSocket, @str_Buffer )
    ? int_Bytes
    ::str_Data += Left( str_Buffer, int_Bytes )
  ENDDO

  RETURN nil


METHOD Close() CLASS TSocket

  INetClose( ::pSocket )

  RETURN nil


METHOD SendReceive( cMessage ) CLASS TSocket

  ::Send( cMessage )
  ::Receive()
  ::Close()

  RETURN ::cData


METHOD CleanUp() CLASS TSocket

  RETURN InetCleanUp()
 

Re: Tsocket Bug

Posted: Mon Apr 19, 2010 11:11 am
by Davide
Thank you guys for your replies.
Bayron wrote: Do you have a Yahoo Mail Plus account???
No, but I don't need to necessarily connect to that yahoo server. That was just an example to show the problem.
xProgrammer wrote:At face value it looks like yahoo is dropping the connection (as per your telnet experience) and you pc keeps retrying the failed connection. Given your "success" with the gmail address it is less likely to be a "bug" in tsocket.
The Google sample works good because that server doesn't drop the connection down, but replies as expected.
The Yahoo sample doesn't work because tSocket.prg doesn't correctly handle the 10057 WSA error.

Thank you for posting your class, but tSocket is used by other classes, like tSmtp, tWebClient, etc. so I need to fix the original one shipped with FWH because in reality this is causing me trouble on other classes, when the remote server closes the connection unexpectedly (for many reasons; not necessarily the reason in this sample).

Thanks,
Davide

Re: Tsocket Bug

Posted: Wed Apr 21, 2010 3:42 pm
by Davide
ok, this solves the endless loop (and even a problem with WSAGetLastError() that's called twice if logging is enabled):

Code: Select all

METHOD SendData( cData ) CLASS TSocket

   local nSize := Len( cData )
   local nLen  := nSize
   local nSent := 0
   local nError:= 0

   if ! ::lSending
      ::lSending = .t.
   else
      AAdd( ::aBuffer, cData )
      return nSize
   endif

   while ( nLen > 0 .and. ;
           ( nSent := SocketSend( ::nSocket, cData ) ) < nLen ) .or. ;
      Len( ::aBuffer ) > 0
      if ::lDebug .and. ! Empty( ::cLogFile )
         nError:=WSAGetLastError()
         LogFile( ::cLogFile, { "Sent:", nSent, "Len:", Len( cData ), "Buffer Len:", Len( ::aBuffer ), "Error:", nError } )
      endif
      // Check for buffered packets to send
      if nLen == 0 .and. Len( ::aBuffer ) > 0
         cData = ::aBuffer[ 1 ]
         ADel( ::aBuffer, 1 )
         ASize( ::aBuffer, Len( ::aBuffer ) - 1 )
      endif
      if nSent != -1
         cData = SubStr( cData, nSent + 1 )
         nLen  = Len( cData )
      else
         if WSAGetLastError() != WSAEWOULDBLOCK
            // exit
         endif
        if !(::lDebug .and. ! Empty( ::cLogFile )) ; nError:=WSAGetLastError() ; Endif  // *GD* Cannot do it twice
        if nError = 10057                 // WSAENOTCONN  *GD* 21.04.2010
          cData:="" ; nSize:=0 ; exit // ::lSending:=.f. ; ::End()
        endif
      endif
      SysRefresh()
   end

   // if ::lDebug .and. ! Empty( ::cLogFile )
   //    LogFile( ::cLogFile, { cData } )
   // endif

   ::lSending = .f.

return nSize

 
however, I found out that the source of the problem is before this point.
Method ConnectTo() doesn't detect the dropdown and returns a 10060 WSA Error just when the Timeout expires.

Any chance to have ConnectTo() acting like the Terminal Emulator (retrieving the server's reply and returning immediately) ?

Thanks,
Davide

Re: Tsocket Bug

Posted: Sat May 07, 2011 11:52 am
by Horizon
Hi Davide,

Can you solve this problem?

I have 11.01. When I try to run SockCli.prg as an example without running SockSer.prg, Tsocket runs to bConnect. I think it should say can not connected.

Thanks,

Re: Tsocket Bug

Posted: Sat May 07, 2011 12:16 pm
by Davide
Dear Hakan,
Horizon wrote:I have 11.01. When I try to run SockCli.prg as an example without running SockSer.prg, Tsocket runs to bConnect. I think it should say can not connected.
unfortunately I never solved the problem above, and I'm still on 9.05

Hi,
Davide

Re: Tsocket Bug

Posted: Sat May 07, 2011 12:21 pm
by Horizon
Daniel,

Can you help us?

Re: Tsocket Bug

Posted: Sat May 07, 2011 2:04 pm
by Daniel Garcia-Gil
Hello

yes, sure...

can you post a little sample?

or the sample in first post is enough?

Re: Tsocket Bug

Posted: Sat May 07, 2011 8:42 pm
by Horizon
Hi Daniel,

I use standart SockCli.prg and SockSer.prg's as an example.

My main problem is connect method. My scenario is like below.

SockSer.prg is not running. There is not any Socket server as I know. I use standart port 2000 and use 127.0.0.1 as IP. (I have tried several unknown ip)

I run SockCli.prg and connect. The bConnect block is evaluated, but the return value of connect method is -1. It is not connected. so if you call senddata method after that, the unexpected errors occur. The socket thinks connection is establish. but there is no connection.

if the senddata method is used in this situation, it gives an error. if the logfile is used, logfile is grow and grow. (Davide explained this above messages)

I think the other problem is checking the connection is established or not.

Re: Tsocket Bug

Posted: Sat May 07, 2011 9:18 pm
by Daniel Garcia-Gil
Hello

i tested sockserv.prg and sockcli.prg and run fine

from samples folder
Download: http://www.sitasoft.net/fivewin/samples/testsock.zip
sockserv.prg

Code: Select all

// Socket server connection sample

#include "FiveWin.ch"

#define ST_COMMAND  1
#define ST_SENDFILE 2
#define FILE_BLOCK 8000

static oWnd, oSocket, oClient

//------------------------------------------------------------------------//

function Main()

   local oBar

   DEFINE WINDOW oWnd TITLE "Server socket"

   DEFINE BUTTONBAR oBar OF oWnd _3D

   DEFINE BUTTON OF oBar ACTION Server() TOOLTIP "Listen"

   DEFINE BUTTON OF oBar ACTION oClient:SendData( "Hello from server!" ) TOOLTIP "Talk to client"

   ACTIVATE WINDOW oWnd

return nil

//------------------------------------------------------------------------//

function Server()

   oSocket = TSocket():New( 2000 )

   oSocket:bAccept = { | oSocket | oClient := TSocket():Accept( oSocket:nSocket ),;
                       oClient:Cargo := ST_COMMAND,;
                       oClient:bRead := { | oSocket | OnRead( oSocket ) },;
                       oClient:bClose := { | oSocket | OnClose( oSocket ) } }

   oSocket:Listen()

return nil

//------------------------------------------------------------------------//

function OnRead( oSocket )

   local cData := oSocket:GetData()
   local cToken

   LogFile( "sockserv.txt", { Len( cData ), cData } )

   do case
      case oSocket:Cargo == ST_COMMAND
           cToken = StrToken( cData, 1 )
           do case
              case cToken == "SENDFILE"
                   oSocket:Cargo = ST_SENDFILE
                   oSocket:hFile = fcreate( StrToken( cData, 2 ) )

              case cToken == "MSG"
                   MsgInfo( SubStr( cData, 5 ) )
           endcase

      case oSocket:Cargo == ST_SENDFILE
           fwrite( oSocket:hFile, cData, Len( cData ) )
           LogFile( "sockserv.txt", { "writting..." } )

           if Len( cData ) < FILE_BLOCK
              // fclose( oSocket:hFile )
              // MsgInfo( Len( cData ) )
              // oSocket:Cargo = ST_COMMAND
           endif
   endcase

return nil

//------------------------------------------------------------------------//

function OnClose( oSocket )

   MsgInfo( "Client has closed!" )

   do case
      case oSocket:Cargo == ST_SENDFILE
           fclose( oSocket:hFile )
   endcase

   oSocket:End()

return nil

//------------------------------------------------------------------------//
 

sockcli.prg

Code: Select all

// Socket server connection sample

#include "FiveWin.ch"

static oWnd, oSocket

function Main()

   local oBar

   DEFINE WINDOW oWnd TITLE "Client socket"

   DEFINE BUTTONBAR oBar OF oWnd _3D

   DEFINE BUTTON OF oBar ACTION Client() TOOLTIP "Connect"

   DEFINE BUTTON OF oBar ;
      ACTION oSocket:SendData( "MSG This is a test" ) ;
      TOOLTIP "Send data"
      
   DEFINE BUTTON OF oBar ;
      ACTION SendFile() TOOLTIP "Send file"

   ACTIVATE WINDOW oWnd

return nil

function Client()

   oSocket = TSocket():New( 2000 )

   oSocket:bRead    = { | oSocket | MsgInfo( oSocket:GetData() ) }

   // Never use a MsgInfo() here because it hangs Windows!!!
   oSocket:bConnect = { || oWnd:SetText( "Connected!" ) }

   oSocket:bClose   = { || MsgInfo( "Server has closed!" ) }

   oSocket:Connect( "127.0.0.1" ) // use the server IP address here

return nil

function SendFile()

   local cFileName := cGetFile( "*.*", "Select a file to send by Internet" )

   if ! Empty( cFileName ) .and. File( cFileName )
      oSocket:SendData( "SENDFILE " + cFileName( cFileName ) )
      oSocket:SendFile( cFileName )
      MsgInfo( "File sent" )
   endif

return nil
 

Re: Tsocket Bug

Posted: Mon May 09, 2011 10:31 am
by Horizon
Hi Daniel,

I have tested your sockcli.exe in my computers. All of my computers says Connected, when I press connect button. There is no working socket server.

Re: Tsocket Bug

Posted: Mon May 09, 2011 11:43 am
by Daniel Garcia-Gil
Hello...

can you contact with me by msn (danielgarciagil@cantv.net) or gmail chat (danielgarciagil@gmail.com) or other way? for test

Re: Tsocket Bug

Posted: Mon May 09, 2011 5:37 pm
by Daniel Garcia-Gil
Horizon

i'll explain...

it isn't a bug... is a behavior of windows socket...

when we connected to socket, we are open a port to send and receive data, it's no a direct link to a server, the ip address is only the "path" between client and server...
we dont know if the server accept the connection until them notify us, i didn't found a message send by server to notify "connection accepted"

With a nonblocking socket, the connection attempt cannot be completed immediately. In this case, connect will return SOCKET_ERROR, and WSAGetLastError will return WSAEWOULDBLOCK. ( you can call WSAGetLastError after connect, will see )

try with this new sockcli.prg / socksrv.prg

look oSocket:bAccept ... i sent "ok" to notify the client the connection is accepted, other way the client no show "connected" only show "port opened"

Code: Select all

// Socket server connection sample

#include "FiveWin.ch"

#define ST_COMMAND  1
#define ST_SENDFILE 2
#define FILE_BLOCK 8000

static oWnd, oSocket, oClient

//------------------------------------------------------------------------//

function Main()

   local oBar

   DEFINE WINDOW oWnd TITLE "Server socket"

   DEFINE BUTTONBAR oBar OF oWnd _3D

   DEFINE BUTTON OF oBar ACTION Server() TOOLTIP "Listen"

   DEFINE BUTTON OF oBar ACTION oClient:SendData( "Hello from server!" ) TOOLTIP "Talk to client"

   ACTIVATE WINDOW oWnd

return nil

//------------------------------------------------------------------------//

function Server()

   oSocket = TSocket():New( 5000 )

   oSocket:bAccept = { | oSocket | oClient := TSocket():Accept( oSocket:nSocket ),;
                       oClient:Cargo := ST_COMMAND,;
                       oClient:bRead := { | oSocket | OnRead( oSocket ) },;
                       oClient:bClose := { | oSocket | OnClose( oSocket ) },;
                       oClient:SendData( "ok" ),;
                       MsgInfo( "accepted" ) }

   oSocket:Listen()

return nil

//------------------------------------------------------------------------//

function OnRead( oSocket )

   local cData := oSocket:GetData()
   local cToken

   LogFile( "sockserv.txt", { Len( cData ), cData } )

   do case
      case oSocket:Cargo == ST_COMMAND
           cToken = StrToken( cData, 1 )
           do case
              case cToken == "SENDFILE"
                   oSocket:Cargo = ST_SENDFILE
                   oSocket:hFile = fcreate( StrToken( cData, 2 ) )

              case cToken == "MSG"
                   MsgInfo( SubStr( cData, 5 ) )
           endcase

      case oSocket:Cargo == ST_SENDFILE
           fwrite( oSocket:hFile, cData, Len( cData ) )
           LogFile( "sockserv.txt", { "writting..." } )

           if Len( cData ) < FILE_BLOCK
              // fclose( oSocket:hFile )
              // MsgInfo( Len( cData ) )
              // oSocket:Cargo = ST_COMMAND
           endif
   endcase

return nil

//------------------------------------------------------------------------//

function OnClose( oSocket )

   MsgInfo( "Client has closed!" )

   do case
      case oSocket:Cargo == ST_SENDFILE
           fclose( oSocket:hFile )
   endcase

   oSocket:End()

return nil

//------------------------------------------------------------------------//
 

Code: Select all

// Socket server connection sample

#include "FiveWin.ch"

static oWnd, oSocket

function Main()

   local oBar

   DEFINE WINDOW oWnd TITLE "Client socket"

   DEFINE BUTTONBAR oBar OF oWnd _3D

   DEFINE BUTTON OF oBar ACTION Client() TOOLTIP "Connect"
    
   DEFINE BUTTON OF oBar ;
      ACTION oSocket:SendData( "MSG This is a test" ) ;
      TOOLTIP "Send data"
      
   DEFINE BUTTON OF oBar ;
      ACTION SendFile() TOOLTIP "Send file"

   ACTIVATE WINDOW oWnd

return nil

function Client()
   
   if oSocket != NIL
      oSocket:End()
   endif
   oSocket = TSocket():New( 5000 )

   oSocket:bRead    = { | oSocket | HandleRecived( oSocket, oWnd ) }

   // Never use a MsgInfo() here because it hangs Windows!!!
   oSocket:bConnect = { || oWnd:SetText( "Socked Opened!" ) }

   oSocket:bClose   = { || MsgInfo( "Server has closed!" ) }
   
   oSocket:Connect( "127.0.0.1" )
   MsgInfo( WSAGetLastError() )
   
return nil

function SendFile()

   local cFileName := cGetFile( "*.*", "Select a file to send by Internet" )

   if ! Empty( cFileName ) .and. File( cFileName )
      oSocket:SendData( "SENDFILE " + cFileName( cFileName ) )
      oSocket:SendFile( cFileName )
      MsgInfo( "File sent" )
   endif

return nil

function HandleRecived( oSocket, oWnd )

   local cData := oSocket:GetData()
   
   
   if cData = "ok"
      oWnd:SetText( "Connected!!" )
   else 
      MsgInfo( cData )
   endif


return nil

 

Re: Tsocket Bug

Posted: Mon May 09, 2011 6:46 pm
by Horizon
Thank you very much Daniel.

There is a help in Fivewin Function about ConnectTo like that.


ConnectTo( <nSocket>, <nPort>, <cIPAddr ) --> nRetCode

<nRetCode> Zero if successful, otherwise SOCKET_ERROR.

unfortunately, it does not work. It returns always -1.