WebSocket - Closing Handshake Gorilla - go

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.

Related

How I can receive data for ever from TCP server

I try to create TCP client to receive data from TCP server,
but after server sending data only I receive data one even if server send many data, and I want to receive data forever, and I don't know what is my problem,and
Client:
func main() {
tcpAddr := "localhost:3333"
conn, err := net.DialTimeout("tcp", tcpAddr, time.Second*7)
if err != nil {
log.Println(err)
}
defer conn.Close()
// conn.Write([]byte("Hello World"))
connBuf := bufio.NewReader(conn)
for {
bytes, err := connBuf.ReadBytes('\n')
if err != nil {
log.Println("Rrecv Error:", err)
}
if len(bytes) > 0 {
fmt.Println(string(bytes))
}
time.Sleep(time.Second * 2)
}
}
I'm following this example to create TCP test server
Server:
// Handles incoming requests.
func handleRequest(conn net.Conn) {
// Make a buffer to hold incoming data.
buf := make([]byte, 1024)
// Read the incoming connection into the buffer.
_, err := conn.Read(buf)
if err != nil {
fmt.Println("Error reading:", err.Error())
}
fmt.Println(buf)
// Send a response back to person contacting us.
var msg string
fmt.Scanln(&msg)
conn.Write([]byte(msg))
// Close the connection when you're done with it.
conn.Close()
}
Read requires a Write on the other side of the connection
want to receive data forever
Then you have to send data forever. There's a for loop on the receiving end, but no looping on the sending end. The server writes its message once and closes the connection.
Server expects to get msg from client but client doesn't send it
// conn.Write([]byte("Hello World"))
That's supposed to provide the msg value to the server
_, err := conn.Read(buf)
So those two lines don't match.
Client expects a newline but server isn't sending one
fmt.Scanln expects to put each whitespace separated value into the corresponding argument. It does not capture the whitespace. So:
Only up to the first whitespace of what you type into server's stdin will be stored in msg
Newline will not be stored in msg.
But your client is doing
bytes, err := connBuf.ReadBytes('\n')
The \n never comes. The client never gets done reading that first msg.
bufio.NewScanner would be a better way to collect data from stdin, since you're likely to want to capture whitespace as well. Don't forget to append the newline to each line of text you send, because the client expects it!
Working code
I put these changes together into a working example on the playground. To get it working in that context, I had to make a few other changes too.
Running server and client in the same process
Hard coded 3 clients so the program ended in limited amount of time
Hard coded 10 receives in the client so program can end
Hard coded 3 server connections handled so program can end
Removed fmt.Scanln and have server just return the original message sent (because playground provides no stdin mechanism)
Should be enough to get you started.

binance websocket not responding to ping

Using gorilla/websocket I dial the binance websocket endpoint, which succeeds without error. After setting the pong handler on the connection, I write a ping control message and wait for a pong to arrive at the pong handler, which never seems to happen. I use a channel, a context with timeout and a select block to check if the pong arrived.
The code:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/gorilla/websocket"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
conn, resp, err := websocket.DefaultDialer.DialContext(ctx, "wss://stream.binance.com:9443/ws", nil)
if resp != nil {
log.Printf("status: %s", resp.Status)
}
if err != nil {
panic(err)
}
pong := make(chan struct{})
conn.SetPongHandler(func(appData string) error {
log.Println(appData)
pong <- struct{}{}
return nil
})
if err := conn.WriteControl(websocket.PingMessage, []byte("Hello, world!"), time.Now().Add(5*time.Second)); err != nil {
panic(err)
}
select {
case <-ctx.Done():
panic(fmt.Errorf("pong wait: %w", ctx.Err()))
case <-pong:
}
}
Output:
$ go run ./cmd/ping
2022/02/07 20:01:23 status: 101 Switching Protocols
panic: pong wait: context deadline exceeded
goroutine 1 [running]:
main.main()
/workspaces/yatgo/cmd/ping/ping.go:39 +0x2ba
exit status 2
As per rfc6455, section-5.5.2:
5.5.2. Ping
The Ping frame contains an opcode of 0x9.
A Ping frame MAY include "Application data".
Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
response, unless it already received a Close frame. It SHOULD
respond with Pong frame as soon as is practical. Pong frames are
discussed in Section 5.5.3.
An endpoint MAY send a Ping frame any time after the connection is
established and before the connection is closed.
NOTE: A Ping frame may serve either as a keepalive or as a means to
verify that the remote endpoint is still responsive.
I kind off expected this to work. Binance websocket API limits doc does mentions ping messages:
WebSocket connections have a limit of 5 incoming messages per second. A message is considered:
A PING frame
So I wonder:
Is something wrong with my code?
Or is binance not respecting RFC6455?
The Gorilla Websocket documentation says:
The application must read the connection to process close, ping and pong messages sent from the peer. If the application is not otherwise interested in messages from the peer, then the application should start a goroutine to read and discard messages from the peer.
Fix the application by starting a goroutine to read the connection before the select statement:
go func() {
defer cancel()
for {
if _, _, err := conn.NextReader(); err != nil {
fmt.Println(err)
return
}
}
}()
select {
⋮
This is a fix for the application shown in the question. If your actual application reads data from the connection in a loop, then you should not add the goroutine shown here. The application should use one read loop to handle control and data messages.

How to connect to a websocket using gorilla?

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

Apparent deadlock between server and client

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.

Gorilla Websocket - Read error: repeated read on failed websocket connection

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.

Resources