Understanding tcp
[1/10] from: patrick::philipot::laposte::net at: 15-Aug-2003 19:18
Hi List
I am still struggling to build a Rebol Client for dbtcp.
(dbtcp is a server for ODBC databases).
I am able to send dbtcp my requests, but I am not able to receive anything from it.
I know it is receiving the requests because I am using a tcp-sniffer (tcpview for windows).
There is my Rebol code:
>> serv: open/binary tcp://localhost:3000
>> insert serv "^(05)^(00)^(0A)^(00)^(00)^(01)DSN=mytest;"
>> insert serv "^(05)^(00)^(17)^(00)^(00)^(02)SELECT * from mytable^(0D)^(0A)"
>> insert serv "^(05)^(00)^(0B)^(00)^(00)^(06)FetchRecord"
>> copy serv
** Access Error: Network timeout
** Near: copy serv
>> close serv
The line with "copy serv" is meant to read whatever the server is sending.
I have tried to use it after each 'insert with the same result.
There is the output from tcpview:
Local Port (3000) opened
Waiting for connections
156: Client connected; 127.0.0.1:1213
156: Connecting to Server
156: Connected to Server
156: Client to Server (17 bytes)
0000 05 00 0A 00 00 01 44 53 4E 3D 6D 79 74 65 73 74 ......DSN=mytest
0010 3B ;
156: Server to Client (18 bytes)
0000 05 00 0C 01 01 33 44 53 4E 3D 6D 79 74 65 73 74 .....3DSN=mytest
0010 0D 0A ..
156: Client to Server (29 bytes)
0000 05 00 17 00 00 02 53 45 4C 45 43 54 20 2A 20 66 ......SELECT * f
0010 72 6F 6D 20 6D 79 74 61 62 6C 65 0D 0A rom mytable..
156: Server to Client (29 bytes)
0000 05 00 17 01 01 33 53 45 4C 45 43 54 20 2A 20 66 .....3SELECT * f
0010 72 6F 6D 20 6D 79 74 61 62 6C 65 0D 0A rom mytable..
156: Client to Server (17 bytes)
0000 05 00 0B 00 00 06 46 65 74 63 68 52 65 63 6F 72 ......FetchRecor
0010 64 d
156: Server to Client (21 bytes)
0000 05 00 0F 01 01 36 00 05 48 61 72 72 79 00 06 50 .....6..Harry..P
0010 6F 74 74 65 72 otter
156: Client disconnected
156: Disconnected from Server
It is clear that dbtcp is sending data (line like 156: Server to Client (21 bytes)).
Furthermore, when using the dbtcp-client-test provided with dbtcp,
I can see the client receiving this exact data.
Something else is troubling me. It is "1213" in the line:
156: Client connected; 127.0.0.1:1213
(the number is always different for each connection).
If i use probe serv, I can see it again:
>> probe serv
make object! [
scheme: 'tcp
host: "localhost"
port-id: 3000
user: none
pass: none
target: none
path: none
proxy: none
access: none
allow: none
buffer-size: none
limit: none
handler: none
status: none
size: none
date: none
url: none
sub-port: none
locals: none
state:
make object! [
flags: 524835
misc: [124 [] 0]
tail: 0
num: 0
with: "^M^/"
custom: none
index: 0
func: 3
fpos: 0
inBuffer: none
outBuffer: none
]
timeout: none
local-ip: 127.0.0.1
local-service: none
remote-service: none
last-remote-service: none
direction: none
key: none
strength: none
algorithm: none
block-chaining: none
init-vector: none
padding: none
async-modes: none
remote-ip: 127.0.0.1
local-port: 1213 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< here
remote-port: 3000
backlog: none
device: none
speed: none
data-bits: none
parity: none
stop-bits: none
rts-cts: true
user-data: none
awake: none
]
My guess is that I should use this number in some way.
But I have looked everywhere for an example without success.
Does anybody have a clue?
Regards
Patrick
The journey is the reward.
~ Chinese Proverb
[2/10] from: Rebolinth:nodep:dds:nl at: 15-Aug-2003 19:39
--
Conversation/lunch: "How do you Eat your Rebol in the Morning?"
Quoting patrick
Hello Patrick,
Here some tips programming servers under rebol, maybe usefull i dont know,
but what i know is dat i had to rethink the rebol logic befor i was able
to program servers in rebol...but finaly its easy ;-)
Make sure what yuo want with your server when opening it: ie.
lines based?
binary based?
blocking or non-blocking? (no-wait)
direct? (which is default)
Then when you setup a server under rebol like:
server: open tcp://:listenport
the server will listen on ALL nic's on your machine
so the connection coming in could be on ANY ip address on that NIC.
the server 'port is now opened and is ready to read incoming sessions,
BUT when yuo have a tcp server make a selection to distinguish between
the SERVER itself and or a NEW connection.
that looks like this:
connection: wait server ;; this wait for events on the server 'port
when a session comes INto server the 'connection will be SERVER-PORT itself !!!
(Depends if yuo have it all inside a forever loop with a wait inside!!
because then you might split it with a "either equal? server conenction [yes
then][ no then] or use dispatch or port/awake)
do a "first" on "connection" and you will get the 'port information from the
new remote-client!! like ->
client: first connection
This client 'port has information like client/remote-ip client/remote-port
client/local-ip client/local-port and ie. client/state/flags
Now you can read or write to THAT specific client, Still !!
If client: first connection
you can do read-io write-io on the client-server or insert copy anyting with
block related ...
STILL keep in mind, that tracking a remote-port disconnect is not possible
in rebol, you can only check if the remote hung up by doing am action like
insert, first copy on the client port befor you know its closed (irritating..)
Anyway.. here my MULTI SESSION ECHHO SERVER ...
enjoy, Norman.
rebol [ author: "rebolinth nodep dds nl" version: "1.0" ]
ports: [] ; ports we listen on
clients: [] ; ip:port dbase
listener: open/lines/direct tcp://:2000
ports: reduce [ 0:01 listener ]
forever [
;;; wait for 0:01 or ports event, use timer to prevent deadlocks,
dispatch
;;; is an option perhpas here, or even WAIT awake
port: wait ports
if port [
either port = listener [
append ports new: first port
print rejoin [ now " [NEW] " new/remote-ip ":"
new/remote-port ]
append/only clients rejoin [ new/remote-ip ":"
new/remote-port ]
insert new rejoin [ "Welcome " new/remote-ip ":"
new/remote-port ]
][
;;; try to read a line from the port, if error? then
client is gone
either error? try [ line: first port ] [
find clients rejoin [ port/remote-ip ":"
port/remote-port ]
print rejoin [ now " [DIS] " port/remote-ip ":"
port/remote-port ]
remove find ports port
remove find clients rejoin [ port/remote-ip ":"
port/remote-port ]
][
print rejoin [ now " [INC] " port/remote-ip ":"
port/remote-port " > " line ]
;;; send all ports, because we read a line from
one, to all
foreach x next next ports [ insert x line ]
]
]
]
]
close listener
[3/10] from: Rebolinth:nodep:dds:nl at: 15-Aug-2003 19:45
--
Conversation/lunch: "How do you Eat your Rebol in the Morning?"
Quoting patrick
..Hehe oo yes..forgot..
client/remote-ip remote address it connects to
client/remote-port remote port is connects to
client/local-ip local ip it connects from
client/local-port local port it connects from
(R)egards,
Norman
[4/10] from: patrick:philipot:laposte at: 15-Aug-2003 21:29
Hi Norman,
Thank you for this quick answer.
You are giving me a fine Rebol server however I was looking more for a Rebol
client.
I will re-read your code again of course because I do not understand
everything.
For example, I had never seen 'wait used like this before:
port: wait ports
I wonder now if dbtcp is a special case...
But the fact that tcpview can intercept the traffic is a proof that it is
regular server isn't it?
It woulds have been a lot more easier if I could have got a Rebol tcp
sniffer instead of tcpview...
So still looking for help ...
Regards
Patrick
[5/10] from: Rebolinth:nodep:dds:nl at: 15-Aug-2003 22:50
Quoting patrick <[patrick--philipot--laposte--net]>:
Hello Patrick,
Well yes basicly i gave you a server to understand the working of the client ;-)
Never the less Rebol tcp client connections work the same way..only different...hehe
Basicly the idea behind the 'ports under rebol is very nice, because a file a
serial a tcp an udp a console a sound a crypting they are all ports ....
Coming back to your server connection, If you know how the data is sent from
the server to you client (line based or raw binary?) you should change the
behaviour of your client on this...
this sets your network timeout on the revceive or send over network ports...
system/options/schemes/timeout: 60
(default its 30 seconds)
The best thing to make a client is that, for very write action you should
also read (because you probably will receive data after writing?) or reverse...
here an exmaple:
b: open/direct/binary/no-wait tcp://localhost:21
print copy b
the print copy b will return for you the data from the port
putting the above in a loop with your data to send and the expected result
to read should make a nice client...
Try it...dont forget to read the online docs.. takes some minutes to
understand but then you have someting nice ;-)
Dont forget to peek in the library section too..
(R)egards,
Norman.
[6/10] from: patrick:philipot:laposte at: 15-Aug-2003 23:50
Hello Norman,
Thank you again but your client is basically what I have done in the first
place.
The big problem being that 'copy never gave me any data.
No matter where I put it.
>> serv: open/binary tcp://localhost:3000
>> insert serv "^(05)^(00)^(0A)^(00)^(00)^(01)DSN=mytest;"
>> insert serv "^(05)^(00)^(17)^(00)^(00)^(02)SELECT * from
mytable^(0D)^(0A)"
>> insert serv "^(05)^(00)^(0B)^(00)^(00)^(06)FetchRecord"
>> copy serv
** Access Error: Network timeout
** Near: copy serv
>> close serv
I'am still thinking that dbtcp deserves a special coding.
Regards
Patrick
[7/10] from: g:santilli:tiscalinet:it at: 16-Aug-2003 0:50
Hi Patrick,
On Friday, August 15, 2003, 11:50:45 PM, you wrote:
p> I'am still thinking that dbtcp deserves a special coding.
Actually, I think you just need to open your port as /NO-WAIT.
Otherwise, COPY waits for the other end to close the port before
returning.
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amiga Group Italia sez. L'Aquila --- SOON: http://www.rebol.it/
[8/10] from: maarten:vrijheid at: 16-Aug-2003 7:46
OK, see below for some explanations.
> -----Original Message-----
>
> There is my Rebol code:
>
> >> serv: open/binary tcp://localhost:3000
Change this to open/binary/no-wait
> >> insert serv "^(05)^(00)^(0A)^(00)^(00)^(01)DSN=mytest;"
> >> insert serv "^(05)^(00)^(17)^(00)^(00)^(02)SELECT * from
> mytable^(0D)^(0A)"
> >> insert serv "^(05)^(00)^(0B)^(00)^(00)^(06)FetchRecord"
> >> copy serv
copy will now return either an empty binary/string or what's available.
So you'll need to do something like:
Buff: make binary! 2048
Until
[
wait serv
data: copy serv
if not empty? Data [ append buff data]
finished-reading buff
]
with finished-reading a function that checks to see if you have got all
data. This for example how the old Rugby does these things.
> >> close serv
> My guess is that I should use this number in some way.
> But I have looked everywhere for an example without success.
No, that's just some info you can forget. It is the number that is
assigned to the connection by the OS, but you are handed back (and are
only interested in).... the connection itself.
--Maarten
[9/10] from: maarten:vrijheid at: 16-Aug-2003 7:51
> For example, I had never seen 'wait used like this before:
> port: wait ports
>
You can wait on a block of ports and get the port that has an event
back:
Wait [ ... ]
Or with a timeout:
Wait [ ... 0:0:1]
Which returns none (tomeout) or the port
But the best is:
Wait/all [ ... timeout ]
This will return none with a timeout and a block of all ports that have
an event. This is a bit more efficient than entering the event loop
again and again.
--Maarten
[10/10] from: patrick:philipot:laposte at: 16-Aug-2003 12:28
Hi Maarten, Gabriele, and Norman
Thanks to you, it works now.
I needed the open/no-wait !
So now, I am trying to put all this in a 'scheme.
It works pretty well but for the no-wait part.
The scheme is very short:
make root-protocol [
scheme: 'dbtcp
port-id: 3000
port-flags: system/standard/port-flags/pass-thru
open-check: none
open: func [port [port!] /local connection-string][
open-proto port
port/sub-port/state/flags: 524835 ; force /direct/binary mode
connection-string: system/words/copy
^(05)^(00)^(0A)^(00)^(00)^(01)
if not none? port/target [ append connection-string join "DSN="
port/target ";" ] ]
if not none? port/user [ append connection-string join "UID="
port/user ";" ] ]
if not none? port/pass [ append connection-string join "PWD="
port/pass ";" ] ]
insert port connection-string
]
insert: function [ port data ] [ ] [
system/words/insert port/sub-port data
]
copy: func [ port ] [
make string! system/words/copy port/sub-port
]
net-utils/net-install dbtcp self 3000
]
The test program is very short too.
Rebol[]
trace/net on
do %dbtcp-scheme.r
db: open dbtcp://localhost/mytest
insert db rejoin ["^(05)^(00)^(17)^(00)^(00)^(02)" "select * from mytable"
^(0D)^(0A)
]
insert db "^(05)^(00)^(0B)^(00)^(00)^(06)FetchRecord"
print mold copy db ; <<<<<<<<<<<<<< TIMEOUT ERROR
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
close db
I can tell that all this work pretty well because tcpview shows a correct
trace.
The server is receiving and sending its data.
However, as before, I am not in no-wait mode.
And so the copy fails.
I have looked deeply in the code of all existing protocols but found
nothing..
Does anybody have a clue?
Regards
Patrick