3

Let's say we have the following abstraction for a server (XMPP, but here it doesn't matter a lot):

type Server struct {
    Addr   string
    Conn   net.Conn
    tlsCon *tls.Conn
    R      *bufio.Reader
    SSL    bool
    reader chan string
}

And a helper function to init it:

func createServer(domain string) (s *Server, err error) {
    conn, err := net.Dial("tcp", domain+":5222")
    if err != nil {
        return nil, err
    }
    s = &Server{domain, conn, nil, bufio.NewReader(conn), false, make(chan string, 8)}
    go func(t *Server) {
        for {
            buf := bufsPool.Get().([]byte)
            n, err := s.R.Read(buf)
            if err == nil {
                s.reader <- string(buf[:n])
            } else {
                fmt.Println(err)
            }
        }
    }(s)

    return s, err
}

The idea is simple: create a connection and, if everything went well, get a buffered reader for it (I don't use the conn.read function just because, if server requires, I start TLS connection and reassign R to reader created based of it, but now this isn't the case).

Now we have the two functions, write and read:

func (s *Server) read() (t string) {
    t = ""
Inn:
    for {
        select {
        case u := <-s.reader:
            fmt.Println("debug", u)
            t += u
        default:
            break Inn
        }
    }
    return t
}

So I want the read function to receive data sent by the goroutine which reads from socket (the one started in createServer()) from chan . The idea is to call write and than read the response. I created all of this because the server some times sends response as two parts, e.g. I have to do traditional read() 2 times. But this didn't work, my read function (see above) simply returns nothing. Most probably, that's because the server doesn't manage to send back the data and my function exits because there's nothing in the chan. But one concern is that although I call write and read multiple times, read always returns nothing.

So I guess I have some general design error and the question is if the community can help me find it. Thanks.

Gonzalez
  • 681
  • 1
  • 11
  • 21
  • See http://stackoverflow.com/questions/36105199/how-to-read-data-xml-sent-by-server-if-it-doesnt-send-new-line for information on how to read an XMPP stream from Go. – Charlie Tumahai Mar 24 '16 at 15:40
  • Oh, you asked that question. You need to use an XML parser to correctly parse stanzas from an XMPP stream. Your question implies that you are relying on buffering and fragmentation of the data stream. That will not work in general. – Charlie Tumahai Mar 24 '16 at 16:09

1 Answers1

1

The problem is that your select selects the default branch because there's nothing in the reader channel yet, so it breaks the for immediately. (https://golang.org/ref/spec#Select_statements)

You want read to block until you receive enough data for it. E.g. if you know that your response needs to end with a "\n", keep reading and don't break until you get a "\n", or the channel is closed.

Perhaps a better solution would be to use bufio.Scanner in goroutine with the reader, if the incoming data is newline delimited, and use a chan string to pass the whole string to the other goroutine.

You can also use Scanner.Split to set a different splitter function.

(see also this question and answer about tcp and delimiters: Golang: TCP client/server data delimiter)

Edit: Using xml.Decoder.Token you can keep reading the tokens from the stream, and handle them appropriately. You can combine this with Decode (this will decode the next token) or DecodeElement (this allows you to decode the just read token) to decode the xml.

Community
  • 1
  • 1
user1431317
  • 2,674
  • 1
  • 21
  • 18
  • Thanks for your reply. The problem with delimiters is that the XMPP servers don't send any type of delimiter. It sends the XML and it ends with ">", no newline etc (and we obviously cannot use '>' as a delimiter). I initially thought that handling EOF may help but the server doesn't always send it, only some times. So when I receive data I cannot know if I have to do another read or not. I can empirically determine when the data is being sent as two parts, but I doubt it cannot change from server to server, though I tried my code with 10+ servers. – Gonzalez Mar 24 '16 at 10:12
  • I'm not familiar with XMPP, but it seems you'll have to check for the closing root tag then, and probably handle malformed xml too (e.g. if you get a ` – user1431317 Mar 24 '16 at 10:30