Comunicating with a Telnet Server

lu_ozzie

New Member
Licensed User
My question is, can I comunicate with a telnet server? I have a PPC application and i need to write and read string of text with a telnet server...
I'm trying to use Network.dll and BinaryStream.dll, but I don't have enough info and I don't know how to deal with these classes...
Any suggestion or example will be very appreciate! :confused:
Thanks!
 

Erel

Administrator
Staff member
Licensed User
Do you know the server's IP?
Create a Client object and connect to this IP (use port 23):
Don't forget to add a reference to the Network library and add a Client object named client.
B4X:
[LEFT][FONT=Courier New][COLOR=#0000ff]Sub [/COLOR][/FONT][FONT=Courier New][COLOR=#010101]App_Start[/COLOR][/FONT]
[FONT=Courier New][COLOR=#010101]      client.New1[/COLOR][/FONT]
[FONT=Courier New][COLOR=#010101]      client.Connect("xxx.xxx.xxx.xxx",23) [/COLOR][/FONT][FONT=Courier New][/FONT]
[FONT=Courier New][/FONT][FONT=Courier New][COLOR=#0000ff]msgbox[/COLOR][/FONT][FONT=Courier New][COLOR=#010101]("Client connected.")[/COLOR][/FONT]
[FONT=Courier New][COLOR=#010101]      client.Close[/COLOR][/FONT][/LEFT]
[FONT=Courier New][COLOR=#0000ff]End Sub
[/COLOR][/FONT]
 

lu_ozzie

New Member
Licensed User
Ok, that's what i did... and it connects properly...
My biggest problem was the dialogue with the tother computer after connecting..
That's what i did:
B4X:
Sub Connect(IP)
   'ErrorLabel(errConnect)
   WaitCursor(true)
   TextBox2.Text = "Connecting to " & IP
   Dim client
   client.New1
   client.Connect(IP,23)
   stream.New1(client.GetStream,true)
   TextBox2.Text = "Connection established."
   Return
End Sub
I'm trying to read something from the server...I need to write sentences and read replies... Thanks for the quick answer! :)
 

Erel

Administrator
Staff member
Licensed User
You should remove the Dim client line.
Did you add a BinaryFile object named stream?
See this example:
You should add a Bitwise object named bit.
B4X:
Sub Globals
    Dim buffer(0) As byte
End Sub

Sub App_Start
    bit.New1
    AddTimer("Timer1")
    Timer1.Interval = 500
End Sub

Sub Connect(IP)
    'ErrorLabel(errConnect)
    WaitCursor(true)
    TextBox2.Text = "Connecting to " & IP
    client.New1
    client.Connect(IP,23)
    stream.New1(client.GetStream,true)
    TextBox2.Text = "Connection established."
    timer1.Enabled = true
    WaitCursor(false)
End Sub

Sub Timer1_Tick
    If client.DataAvailable Then
        Dim buffer(4096) As byte
        count = stream.ReadBytes(buffer(),4096)
        TextBox2.Text = bit.BytesToString(buffer(),0,count)
    End If
End Sub
 

TWELVE

Active Member
Licensed User
Hello Erel,

i don't wanna highjack this thread, but i've got a question regardings this.

That piece never works for me:

B4X:
Sub Timer1_Tick
   [COLOR="Red"] If client.DataAvailable Then[/COLOR]        Dim buffer(4096) As byte
        count = stream.ReadBytes(buffer(),4096)
        TextBox2.Text = bit.BytesToString(buffer(),0,count)
    End If
End Sub
The line

If client.DataAvailable Then
..
end if

gets never triggered and therefore i never catch the server's response.If i remove this check, i can read the server's response but run risk to get stuck because there's maybe no answer from the server pending anymore .


cheers

TWELVE
 

TWELVE

Active Member
Licensed User
Still no success with your code.I just copied the timer part and it does not catch any data from the network, with the result, that my routine is exiting because the server did not respond the way it expected.

Furthermore i can't see the advantage of the timer here..if you issue a command against a server in a certain protocol language, you have to wait for the server's response because depending on the answer you will decide how to proceed.Even if the timer does catch the answer, it does not help as long as my routine does not wait for the answer, because it's crucial for further processing.

If the task is to get a telnet server response into a command line style window, timing is not important and also the server's response doesn't matter, if there's no one who is waiting for a certain answer.

Providing you've got a fast network connection...your timer has an interval of 500ms setup.So 500ms goes by, before the timer is activated the first time.Can you imagine, how long 500ms in terms of networks is...? Server connections with 500ms delay are supposed to be very high latency connections.The latency to my mail server is 8ms.So you never will catch up a server response unless you lower your timer interval.

Beside this it appears like it takes far too long to start the timer the first time, whatever i use for the interval.I built in a function printing out something within the timer code.This does come up after my routine exited for "long" time.

So, for me and at this time only works to retrieve the server's reponse right after i sent the command to server and this only, if i strip of the client.DataAvailable thingy.

This looks then like this:

B4X:
Sub asub

...
 s = "a command against the server"     
     netwritebuffer() = bit.StringToBytes(s,0,StrLength(s))
    stream.WriteBytes(netwritebuffer())
...

GetServerResponse()

if expected response then

...
do the next command
...

end if

no success

End Sub

...

Sub GetServerResponse

 'If client.DataAvailable Then 
     Dim bufferbits(4096) As byte
     count = stream.ReadBytes(bufferbits(),4096)
     buf = bit.BytesToString(bufferbits(),0,count)
     Return buf
' End If

End Sub
If i remove the ticks in front of client.DataAvailable and Endif, i never get the response ( even though i can see the correct response by the server in a network trace...) and therefore exit the sub.


So this looks more complicated as it appears to look.I personally don't think the client.DataAvailable does not work, rather i think it has to do with timing.Can you explain me, what advantage the use of a timer has in that case, given the fact that the "sender part" has to wait for the response anyhow..? For me that looks this way: i setup a timer that polls something and i poll then, if the timer got already the result..? This makes no sense, and given the fact that the timer does not run in parallel it makes even less sense...?

Usually this would be handled this way:

..
Send something
setup callback: wait for network response & call me as soon as result is available at CB_resume
go to sleep...zzz..zzzz..zzzz
..
CB_resume:
Yas i got an answer and can proceed
...



So for now i'm a little bit confused and need investigate this further...any suggestions..?


cheers

TWELVE
 

Erel

Administrator
Staff member
Licensed User
After a connection is established between a client and a server no data can get lost.
It doesn't matter if you read the data immediately or not. The data is buffered.
If you try to read data before it arrived the process will be blocked until data arrives.

In the case of the ChitChat example the Timer and the DataAvailable check are required to prevent blocking.
You must use a timer or some other polling mechanism if you are checking DataAvailable.
 

agraham

Expert
Licensed User
Check this link http://www.basic4ppc.com/forum/showthread.php?t=1130 for another example of a client/server example using timers and DataAvailable. Start an instance on your desktop as a server, start aother as a client and they should talk to each other. One thing to note is the loop on DataAvailable in the timer loop, you still get the timer latency but once data starts arriving you suck it all out before exiting.
 

TWELVE

Active Member
Licensed User
Hello Erel & agraham,

i did further testing and was successful in getting my code working, but..I'm still having issues with the client.DataAvailable.


I was able to get this here to work:


B4X:
Sub MySub
...

send something to the server
GetNetworkResponse
make a decision depending on buf

...
End Sub

Sub GetNetworkResponse
                     Do Until client.DataAvailable = true
      Sleep(300)
            Loop
      
      
      Dim bufferbits(4096) As byte
      count = stream.ReadBytes(bufferbits(),4096)
      buf = bit.BytesToString(bufferbits(),0,count)
      Return buf      
    
End Sub

So far so good.But if i change the time in Sleep() to more than 300, the program will deadlock within the Loop.This is because the client.DataAvailable never becomes true.You mentioned above, that the time does not matter for this function.So i could - theoretically - execute client.DataAvailable at the end of my program and would get back a true ( yes there's data i received from the server) and could then retrieve this data - if the server ever responded someting.

So..why does my code above not work anymore if the sleep() is set higher than 300...? I do not have an explanation for that, do you..?

If i add a counter, which exits the loop after a certain number of cycles, everything works ok:


B4X:
Sub GetNetworkResponse
                          x=1
      buf = ""
              Do Until client.DataAvailable = true
      Sleep(500)
      x = x+1
          If x > 50 Then
          Return
          End If
   Loop
         
      Dim bufferbits(4096) As byte
      count = stream.ReadBytes(bufferbits(),4096)
      buf = bit.BytesToString(bufferbits(),0,count)
      Return buf
End Sub
If the condition persists, the if..then needs to break the loop after the specified maximum number of loop cycles...and if this occurs, the buf has the correct server response stored.So the answer was there, but the client.DataAvailable prevent the loop from finishing, so it loops for ever.



To circumvent that problem, i use then this code :


B4X:
                   x=1
                   buf = ""
      
      Do While buf = "" 
      x = x+1
'      If x > 20 Then
'      Return
'      End If
      
      
      Dim bufferbits(4096) As byte
      count = stream.ReadBytes(bufferbits(),4096)
      buf = bit.BytesToString(bufferbits(),0,count)
      Sleep(500)
                          Loop
      Return buf
end sub

It just reads the buffer in a loop, until the buffer (buf) is different from an empty string.To prevent a deadlock from happening, if there is no answer from the server, i built in the If...then which is ending the loop after a certain number of cycles.

So i assume then: data.ClientAvailable is working in general, but sometimes it does not.I suspect it happens when the retrieved server response is not complete yet.

This would the look like this:

i send some request data to the server ( netwritebuffer() = bit.StringToBytes(s,0,StrLength(s)) )
after 100ms i check client.DataAvailable
client.DataAvailable is true
==> i retrieve the server response
the server is sending more response data
after 100ms i check client.DataAvailable
client.DataAvailable is false
If i rely on the client.DataAvailable ==> loop is infinite now
if i just retrieve the data, the buffer contains what i was looking for.

This probably happens, if the delay betweens checks is shorter than the server needs to send the complete answer.Since i cannot know the server's response time in advance and i don't know, if the server has completed its answer, i have either to check, if i can expect more data from server or i have to set a time out, after which i will give up waiting.

For this, i've to rely on the client.DataAvailable or i can do a cyclic stream.ReadBytes until i get a non-empty string or the timeout is reached.

Therefore my guess is, client.DataAvailable does indicate the first data received after a netwritebuffer(), but if the server sends more packets after the first data ( which was indicated by client.DataAvailable) had been retrieved using stream.ReadBytes, any subsequent reply packet is then not indicated by the the client.DataAvailable any more.


I took now the first code example, added the cycle count check as a timeout and complemented the Do Until..Loop check with "OR (buf <> "")", which prevents the code from running unnecessary loops passes:


B4X:
Sub GetNetworkResponse
                          x=1
      buf = ""
              Do Until client.DataAvailable = true OR (buf <> "")
      Sleep(500)
      x = x+1
          If x > 50 Then
          Return
          End If
   Loop
         
      Dim bufferbits(4096) As byte
      count = stream.ReadBytes(bufferbits(),4096)
      buf = bit.BytesToString(bufferbits(),0,count)
      Return buf
End Sub

@Erel: please can you have a look at this behavior, why the client.DataAvailable does not indicate subsequent packets from the server, while i can read them anyhow..?


regards

TWELVE
 

Erel

Administrator
Staff member
Licensed User
I don't know why it doesn't work with Sleep > 300.
DataAvailable checks whether there is data waiting in the network buffer.

Writing and reading from the network stream is a blocking operation.
When you try to read from the stream the program is blocked until data arrives.
It is possible that DataAvailable returns false and still you can read from the stream (which will cause a small internal waiting).

I'm not sure you should use DataAvailable at all in your case.
If you expect to receive a message after sending one you should just read from the stream.
Remember that the server socket can also get blocked waiting for you to read the first message.
 

agraham

Expert
Licensed User
This is my Sub from a Request/Response app that has been running for months on several different computers and OSs. We have noticed that different computers seem to suffer from different numbers of timeouts which we have up to now put down to network problems as the server is situated in a cottage overlooking the Irish Sea with ADSL delivered over overhead phone lines subject to salt spray and so whose connections can be a little iffy. It normally sleeps for 50mS between checks but I have just tried 500mS and 1000mS and it works fine first time, but my computer always does. I don't have access to the other computers that appear to need several retries most of the time.

Maybe these differences are due to something like this DataAvailable problem. I've looked with Reflector and the DataAvailable call ends up calling the native OS ioctlsocket() function to get the number of bytes available to read from the socket. I can't look any deeper than that.

B4X:
Sub NetGetData(req)
   msg ="An error occurred connecting to " & URL
   ErrorLabel("err2")
   Client.New1
  Client.Connect(URL, Port)
  Stream.New1(Client.GetStream,true) 
   msg = "GET /" & req & " HTTP/1.1" & Crlf 
   msg = msg & "Host: " & URL & Crlf
   msg = msg & "Proxy-Connection: Keep-Alive" & Crlf
   msg = msg & "Pragma: no-cache" & crlf
   If Secure Then
      p = StringToBase64(User & ":" & password)
      msg = msg & "Authorization: Basic " & p & Crlf
   End If
   msg = msg & Crlf   
   WebReq() = Stream.StringToBytes(msg)   
   Stream.WriteBytes2(WebReq(),0,StrLength(msg))
   Timer = 0
   Do
      Sleep(SleepMs)
      Timer = Timer + SleepMs
      If Timer >= Timeout Then
         Return    "A timeout occurred communicating with " & Device
      End If
   Loop Until Client.DataAvailable = true
   count = Stream.ReadBytes(WebResp(),4096)
   msg = Stream.BytesToString (WebResp(), 0, Count)
  Client.Close
   If SubString(msg, 13,2) = "OK" Then
      msg = SubString(msg, 44, StrLength(msg)-44)
   Else
      msg = "Badly formatted data received from " & Device
   End If   
   err2:
   If ChkDebug.Checked Then   
      Msgbox(msg,"Message data received")
   End If
   Return msg
End Sub
 

schimanski

Well-Known Member
Licensed User
I think, thats my old problem!

Hello TWELVE!

My english is not as good, that I can understand everything of your thread, but I think, you have a simular problem, that I have. Perhaps, you have a look at my thread:

http://www.basic4ppc.com/forum/showthread.php?t=1318

I haven't found an answer for my problem until today, but I also have noticed, that client.dataavailable is affected from the timerinterval. Do you know, how i can eliminate a lock up of my application, when the server sends no data? You have experienced a lot and you have perhaps found some answers to avoid the problem.

Thanks a lot....:sign0085:
 
Top