Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Receiving multiple lines through socket at once

Status
Not open for further replies.

bgrupczy

Programmer
Sep 10, 2009
6
CA
Hi all,

I am interfacing two Tcl apps. The server app opens a socket. I can send commands to it and get responses. The responses are multi-line. My client receive socket handler only gets the response line by line (no matter how many different fconfigure combinations I try.)

What I'd like to happen is for me to get the entire response in one shot. I tried "read" instead of "gets" in the receive handler and it got locked up.

Any direction would be greatly appreciated. TIA.

(I have hunted for an answer and experimented for over a day. I can't find a solution. I bet this is child play for a seasoned Tcl person.)


-Brad
 
When you say that you tried various fconfigure combinations, did you try -translation binary?

_________________
Bob Rashkin
 
Hi Bong,

Yes I tried that. When I use -translation binary, my rx handler never pops. I changed the buffering from -line to -none to -full and the rx handler just never gets called.

I have a hack here that does what I want it to do. I hack in a prefix and suffix on the response on the socket then look for it in the response and grab everything in between. And I can only do this because I know the internals of the server. There HAS to be a better way, eh?

Here's my code with the hack and the server code. I hope you can see something here. Is it possibly something in the server side?

And, yes, I know that the server is very crude (just wrong). Out of my hands... I can probably push some changes into the server too. I'm trying to keep my fingers out of it for now.

Thanks for any help.

Code:
client.tcl
----------

  set ::CmdComplete 0
  set ::AppendData 0
  
  proc Open { } {
    # Server opens localhost port 15124
    set ::SockChan [socket localhost 15124]
    fileevent $::SockChan readable [list Rx $::SockChan]
    [b]fconfigure[/b] $::SockChan -buffering line
  }
  
  proc Tx { cmd } {
    catch { unset ::RetVal }
    set ::CmdComplete 0
    # The server just takes the command coming in and executes it. I know that "msg" is what is returned thru the socket
	# so I pack it in the command I send. I prefix and suffix the "msg" with something I can trigger off of to know
	# exactly what the command I sent in is trying to return.
    puts $::SockChan "set msg \"\\nStart\\n\" ; append msg \[[b]$cmd[/b]\] ; append msg \"\\nEnd\\n\"" ; flush $::SockChan
    after 10000 {set ::RetVal "Error:  Timeout" ; set ::CmdComplete 1}
    vwait ::CmdComplete
    return $::RetVal
  }

  proc Rx {sock} {
    set msg [gets $sock]
    
    if { $msg == "Start" } {
      set ::AppendData 1
      return
    } elseif { $msg == "End" } {
      set ::AppendData 0
      set ::CmdComplete 1
    }
    
    if { $::AppendData == 1 } {
      append ::RetVal "\n" $msg
    }
  }

Code:
server.tcl
----------

####################################################################
# ServerOpen
####################################################################
proc serverOpen {channel addr port} {
  # Setup a fileevent to  be called when input is available
  fileevent $channel readable "readLine $channel"
  set ::srvData(Port) $port
  set ::srvData(Chan) $channel
  set ::srvData(Addr) $addr
  
}
####################################################################
# readLine
####################################################################
proc readLine {channel} {
  set len [gets $channel line]		;# line stores the line, and len the length
  
  # If we read 0 chars, check for EOF. Close the channel and delete that client
  # entry if we have hit the end of the road
   if { ($len <= 0) | [eof $channel]} {
    catch {close $channel} msg
    set ::srvData(ServerNotes) "CONNECTION TERMINATED"
  } else {
    # PROCESS THE LINE OF DATA HERE
    set ::srvData(LastCom) $line
    if { [info commands [lindex $line 0]] == [lindex $line 0] } {
      set ::srvData(ServerNotes) "COMMAND RECOGNIZED"
      set ::srvData(ServerTime)  [clock format [clock seconds]]
      set ::clientSock $channel
      set ::srvData(clientResponse) "OK"
      if { [catch [b]{eval $line} msg[/b]] } { 					;# run the command
        puts "ERROR! - $msg | $line"
        set ::srvData(clientResponse) ERR
      } else {
        [b]lappend ::srvData(clientResponse) $msg[/b]
      }
      [b]puts $channel $::srvData(clientResponse) ; flush $channel[/b]
    } else {
      set ::srvData(ServerNotes) "ILLEGAL COMMAND"
      puts $channel "FAILED Unrecognized command" ; flush $channel
    }
  }
} ;# end proc
####################################################################
# START THE SERVER!!!
####################################################################
### set server [socket -server serverOpen 15123]
catch {socket -server serverOpen 15124} server


-Brad
 
Maybe you already tried this but your Rx proc is using gets which reads 1 line. Have you tried using read?

_________________
Bob Rashkin
 
I tried that before and it hung. But I'm getting better at this so I'll go try again.

In the mean time I did find something similar to what I was asking about.


I'm doing pretty much the same thing. But I am hacking in the "start" and "end". I'd like to get rid of that portion.


-Brad
 
Using a read locks up if I leave it open ended.

If I tell it to read a small number of bytes (less than what's in the buffer), I do get the data in chunks of that size. It then locks up on the last chunk since it's (probably) trying to read more than is available. See next paragraph.

If I tell it to read a large number of bytes (greater than what's in the buffer), it locks up.

Is it possible to ask how many bytes are in the buffer and ask for that number exactly?

I've tried every combination of these parameters:
1. gets vs read
2. translation binary vs auto
3. buffering full vs none vs line

I am stuck!




-Brad
 
Is it possible to ask how many bytes are in the buffer and ask for that number exactly?

I don't think so but you can have the server tell you how many bytes it's going to send, right?

Anyway Ken Jones (Avia) was/is certainly the expert in this and all things Tcl.

_________________
Bob Rashkin
 
I could make that mod to the server. I will try it to see if it works for "read". I think it would be cleaner than the start/end tags.

Thanks for that!


-Brad
 
Because of the:

lappend ::srvData(clientResponse) $msg

on the server side, I'm not getting consistent return data. Depending on the command I issue, I will get just data back e.g. "test" or lists e.g. "{test1 test2}".

So I give up. I don't think it's worth it. The server should be improved to the point that it has an actual API.

I am just going to use my prefix / suffix hack.

Thanks again for the help.



-Brad
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top