Using the gorilla websocket api for go, how do i know if a client is still connected?
What Im trying with now is:
func Listen(ws *websocket.Conn) {
connTimeout := 3
timeLastSent := time.Now().Second()
for ((timeLastSent + connTimeout) % 60) != time.Now().Second() {
msg := Message{}
err := ws.ReadJSON(&msg)
if err == websocket.ErrCloseSent {
break
} else if err != nil {
continue
}
//Message recived
EventMessage <- msg
timeLastSent = time.Now().Second()
}
//Connection timed out.
return
}
But this results in the error repeated read on failed websocket connection.
Ive been looking into using ws.SetReadDeadline(t), but Ive no idea of either how to use it nor if its even the thing Im looking for.
How should i go about this?
When the websocket connection fails with an error other than websocket.ErrCloseSent, the program spins in a tight loop until the timeout.
To help applications detect this programming error, the websocket package panics when read is called 1000 times on a failed connection (view code here).
To fix the problem, break out of the loop on all errors:
err := ws.ReadJSON(&msg)
if err != nil {
// optional: log the error
break
}
Use the connection's read deadline to handle timeouts.
Related
I would like to see a minimal and simple example of how to connect to a websocket using gorilla.
The only example I found to connect to a websocket was this,
but I couldn't understand it, and I couldn't find an explanation of how it works.
Edit:
Line 20:
Why did he keep the websocket address in flag.String instead of a variable string?
Line 26:
Does this part create a signal that will notify the program when the user presses crtl + C?
interrupt: = make(chan os.Signal, 1)
signal.Notify (interrupt, os.Interrupt)
Line 32:
What does this line do?
websocket.DefaultDialer.Dial (u.String (), nil)
Line 60:
Because []byte(t.String()) instead of t.string()?
The sample's README says:
The client sends a message every second and prints all messages received.
Looking at the code, the latter part (prints all messages received) happens here:
go func() {
defer close(done)
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
return
}
log.Printf("recv: %s", message)
}
}()
This spins up a goroutine that reads messages from c (the websocket connection) in a loop and prints them out. It stops when the read fails, which happens for errors and when the connection is closed.
The main goroutine does this:
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-done:
return
case t := <-ticker.C:
err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
if err != nil {
log.Println("write:", err)
return
}
case <-interrupt:
log.Println("interrupt")
// Cleanly close the connection by sending a close message and then
// waiting (with timeout) for the server to close the connection.
err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("write close:", err)
return
}
select {
case <-done:
case <-time.After(time.Second):
}
return
}
}
It has a ticker firing every second, on which a message is sent to the websocket. When the reader goroutine is done it closes the done channel, which signals this loop with the select to exit too.
Finally, there's an "interrupt" selection to handle Ctrl+C, cleanly stopping the client.
To answer your specific questions:
Line 20: this flag enables you to set the address from the command line when executing the program, instead of it being hardcoded. You can call it like client -addr localhost:9044 to set a different port, for example. See also https://gobyexample.com/command-line-flags
Line 26: yes this is for Ctrl+C; see also https://gobyexample.com/signals
Line 32: DefaultDialer is a preconfigured dialer in the websocket package. Per the documentation, it's equivalent to
var DefaultDialer = &Dialer{
Proxy: http.ProxyFromEnvironment,
HandshakeTimeout: 45 * time.Second,
}
Line 60: WriteMessage takes a []byte, so you have to convert the string to a []byte
I cannot understand how to use net conn.SetWriteDeadline function?
conn, err := net.DialTimeout("tcp", "10.1.10.1:2000", 3*time.Second)
if err!=nil{
fmt.Println("Error: ", err)
}
//waits as expected 3 seconds and returns error if dial not succeeded
conn.SetWriteDeadline(time.Now().Add(5*time.Second))
n, err:= conn.Write([]byte{0x00})
if err != nil {
fmt.Printf("Write: %v", err)
}
fmt.Println("Bytes wrote: ", n)
//always returns n=1, no wait, no error if connection lost.
I expect that conn.Write must try to write data to port during 5 seconds and then it must return n=0 and error if there is a trouble with connection. But, even if I unplug network cable, it returns no error and n=1.
If I try to do something like this,
for {
conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
n, err = conn.Write(buffer)
if err != nil {
log.Println("Timeout error")
} else {
break
}
if t.GetElapsed() >= 30*time.Second {
err = errors.New("Connection timeout")
break
}
}
I expect that there will be several attempts to write data, with escaping from FOR if succeeded, or via timeout. But behaviour is the same - even if network is disconnected, there is no error, there is only 1 iteration.
What am I doing wrong? How to use? How to understand if data has been really written to port? How to handle disconnection?
Your write has been accepted by the TCP stack, which now has its own mechanism to handle retries and failures. The write deadline does not guard against the condition you are creating. If you are expecting a response from the peer you are communicating with, use a read deadline. If the connection is terminated by the peer, either a read or a write should identify this condition with an error. Simply disconnecting the network will not make this happen in a timely fashion, but it TCP timeouts are used, the disconnection will eventually trigger an error.
I have a test function which both creates a server and spawns a goroutine acting as a client. Now, simply sending a message from the client to the server works, but if I want to create an exchange, they seem to deadlock since the test never runs to completion (if no r/w deadlines are set). For example, I want the client to send a message to the server, the server to copy that message and send it back to the client, and then the client to verify that the received message was identical. Here is my test code:
func TestSendAwait(t *testing.T) {
m := "Hello World"
go func() {
conn, err := net.Dial("tcp", testingAddr)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
t.Log("client connected to server") // DEBUG
conn.SetDeadline(time.Now().Add(2 * time.Second))
conn.Write([]byte(m))
conn.SetDeadline(time.Now().Add(2 * time.Second))
buf, err := ioutil.ReadAll(conn)
if err != nil {
t.Fatal(err)
}
t.Log(string(buf))
}()
ln, err := net.Listen("tcp", testingAddr)
if err != nil {
t.Fatal(err)
}
defer ln.Close()
t.Log("server started") // DEBUG
conn, err := ln.Accept()
if err != nil {
t.Fatal(err)
}
defer conn.Close()
t.Log("server received connection") // DEBUG
buf, err := ioutil.ReadAll(conn)
if err != nil {
t.Fatal(err)
}
t.Logf("server read buffer: %v", buf) // DEBUG
_, err = conn.Write(buf)
if err != nil {
t.Fatal(err)
}
t.Log("server wrote to connection") // DEBUG
}
The deadlines are set on the connection because otherwise the deadlock would be indefinite. The output is as follows:
transmission_test.go:42: server started
transmission_test.go:24: client connected to server
transmission_test.go:49: server received connection
transmission_test.go:32: read tcp 127.0.0.1:41164->127.0.0.1:9090: i/o timeout
transmission_test.go:55: server read buffer: [72 101 108 108 111 32 87 111 114 108 100]
transmission_test.go:61: server wrote to connection
Process finished with exit code 1
I don't understand why the client is unable to read and exits, and only then the server decides to send data down the socket? This happens even if I increase the read deadline in the client.
The program blocks on the call to ioutil.ReadAll. This function reads until io.EOF or some other error is returned.
One fix is to shutdown write after writing data to the connection. This will cause read on the peer to return io.EOF and for ioutil.ReadAll to return successfully.
conn.Write(data)
cw, ok := conn.(interface{ CloseWrite() error })
if !ok {
// handle error
}
cw.CloseWrite()
playground example
The program in the question does not guarantee that the listener is opened before the connection is dialed or that client will print print the received message. The playground example corrects these issues.
Another approach is to frame the messages in some way:
Write newline or some other byte sequence not allowed in message after message. Read until this byte sequence is found.
Write message length before message. Read length and then specified number of bytes.
Snippet from WebSocket RFC:
To Start the WebSocket Closing Handshake with a status code (Section 7.4) /code/ and an optional close reason (Section 7.1.6) /reason/, an endpoint MUST send a Close control frame, as described in Section 5.5.1, whose status code is set to /code/ and whose close reason is set to /reason/. Once an endpoint has both sent and received a Close control frame, that endpoint SHOULD Close the WebSocket Connection as defined in Section 7.1.1.
I am trying to do the Close Handshake using Gorilla WebSocket package with the following code:
Server:
// Create upgrader function
conn, err := upgrader.Upgrade(w, r, nil)
// If there is an error stop everything.
if err != nil {
fmt.Println(err)
return
}
for {
// Read Messages
_, _, err := conn.ReadMessage()
// Client is programmed to send a close frame immediately...
// When reading close frame resend close frame with same
// reason and code
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(1000, "woops"))
fmt.Println(err)
break
}
Client:
d := &websocket.Dialer{}
conn, _, err := d.Dial("ws://localhost:8080", nil)
if err != nil {
fmt.Println(err)
return
}
go func() {
for {
// Read Messages
_, _, err := conn.ReadMessage()
if c, k := err.(*websocket.CloseError); k {
if(c.Code == 1000) {
// Never entering since c.Code == 1005
fmt.Println(err)
break
}
}
}
}()
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(1000, "woops"))
for {}
The server is reading the Close Frame as expected outputting the following:
websocket: close 1000 (normal): woops
However the client is like its stopping to read once it sends a close message. The ReadMessage continue to return error 1005. What am I doing wrong?
The server responds to a close frame with the code:
c.WriteControl(CloseMessage, []byte{}, time.Now().Add(writeWait))
This is translated to close code 1005 (no status received) by the client.
The 1000 oops close frame written by the server is not seen by the client application because the websocket connection stops reading from network after receiving the first close frame.
The client application should exit the loop when an error is returned from ReadMessage. There's no need to check for specific close codes.
for {
// Read Messages
_, _, err := conn.ReadMessage()
if err != nil {
break
}
}
Unrelated to the issue in the question, the server application should close the websocket connection after sending the close frame.
Also unrelated to the issue in the question, use select {} instead of for {} to block the main goroutine. The former simply blocks the goroutine. The latter spins using CPU time.
I wrote simply server program to received data form client. I little not understand what sometimes I get error read tcp4 IP:PORT i/o timeout from function
int, err := conn.Read([]byte) event time set in function SetDeadline() not was exceeded. I present some part of my code, but I think that this is will enough.
main loop where I receive data is below.
c := NewClient()
c.kickTime: time.Now()
func (c *Client) Listen(){
durationToClose := time.Minute*time.Duration(5),
c.conn.SetDeadline(c.kickTime.Add(c.durationToClose))
buffer := make([]byte, 1024)
for{
reqLen, err := c.conn.Read(buffer)
if err != nil || reqLen == 0 {
fmt.Printf(err)
break
}
if err = c.CheckData(buffer) ; err != nil{
fmt.Printf("something is bad")
}else{
result := c.PrepareDataToSendInOtherPlace(buffer)
go c.RecievedData(result)
}
c.conn.SetDeadline(c.kickTime.Add(c.durationToKick))
}
}
For me only suspicious can be additional function as PrepareDataToSendInOtherPlace() , CheckData() which may take some times CPU, and then new data was be send by client, and server at the time doing something else and rejects connect. This is only my supposition, but I'm not sure.
Syntax errors and undeclared variables aside, what you're showing us can't possibly be walking the Read/Write deadline forward indefinitely.
The longest this could run is until a fixed duration after the first time.Now() (c.kickTime.Add(c.durationToKick)). You probably want something like:
c.conn.SetDeadline(time.Now().Add(c.durationToKick))