When I close a browser I want to disconnect a websocket in 3 seconds instead of 1 minute. The following just keep writing into a void without error until the tcp ip timeout I guess, not the SetWriteDeadline.
f := func(ws *websocket.Conn) {
for {
select {
case msg := <-out:
ws.SetWriteDeadline(time.Now().Add(3 * time.Second))
if _, err := ws.Write([]byte(msg)); err != nil {
fmt.Println(err)
return
}
case <-time.After(3 * time.Second):
fmt.Println("timeout 3")
return
}
}
}
return websocket.Handler(f)
I need to wait for this err
write tcp [::1]:8080->[::1]:65459: write: broken pipe
before it finally closes the connection, which takes about a minute or more.
You are you using WriteDeadline correctly. The deadline specifies the time for writing data to the TCP stack's buffers, not the time that the peer receives the data (if it does at all).
To reliably detect closed connections, the application should send PINGs to the peer and wait for the expected PONGs. The package you are using does not support this functionality, but the Gorilla package does. The Gorilla chat application shows how use PING and PONG to detect closed connections.
Related
When I read this opensource code.
I have two questions about the two functions:
func listenTCP() {
for {
conn, err := tcpListener.Accept()
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
log.Printf("Temporary error while accepting connection: %s", netErr)
}
log.Fatalf("Unrecoverable error while accepting connection: %s", err)
return
}
go handleTCPConn(conn) // check below
}
}
func handleTCPConn(conn net.Conn) {
log.Printf("Accepting TCP connection from %s with destination of %s", conn.RemoteAddr().String(), conn.LocalAddr().String())
defer conn.Close()
remoteConn, err := conn.(*tproxy.Conn).DialOriginalDestination(false)
if err != nil {
log.Printf("Failed to connect to original destination [%s]: %s", conn.LocalAddr().String(), err)
return
}
defer remoteConn.Close()
var streamWait sync.WaitGroup
streamWait.Add(2)
streamConn := func(dst io.Writer, src io.Reader) {
io.Copy(dst, src)
streamWait.Done()
}
go streamConn(remoteConn, conn)
go streamConn(conn, remoteConn)
streamWait.Wait()
}
Based on my understanding, I draw this diagram:
You see, the handleTCPConn created two goroutines for transmitting two direction(left -> right; right -> left)'s traffic,
My questions are:
You see the code use sync.WaitGroup, if they only send left-> right traffic, there is no traffic in opposite direction, so the handleTCPConn will not end, right? if it is, the listenTCP for loop will create many of those handleTCPConn function calls, is there nothing wrong with this program?
Every time the handleTCPConn is used, it will create a TCP connection to the remote server.
remoteConn, err := conn.(*tproxy.Conn).DialOriginalDestination(false)
My question is still in question 1, you can see that the handleTCPConn transmit the traffic once in both directions, and then ends it, whether the TCP connection is closed when does handleTCPConn end?
if they only transmit part of the data of a file(as per the application layer view), whether it is closed too? (i mean, if A->B->C: part data , then C->B->A: ACK ) .
per the golang docs, https://pkg.go.dev/io#Copy
Copy copies from src to dst until either EOF is reached on src or an error occurs. It returns the number of bytes copied and the first error encountered while copying, if any.
So when you start this program up, it will sit there and wait for you to hit the 'proxy', and send your bytes from the source to the destination... when the destination responds it will copy all those bytes back. if the destination doesn't write any bytes and doesn't close the connection i believe it'll sit there forever, waiting for the far side to either close the socket or respond.
Same is true if you make this connection and the remote sides starts sending data (without a request first). If the "local" side never sends any bytes and doesn't close the connection this code would wait forever as well.
As long as the remote side closes the connection gracefully, this code should exit with "0" bytes received and no error. If the remote side sends a reset, you should get an error of some kind
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.
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.
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 have a Golang project that utilizes conn.SetDeadline(). If a an EOF error is thrown because a read timed out, does Go automatically close the connection?
I have a setup where I need to wait a certain amount of time on a network connection for output to arrive, and if the output doesn't arrive, then it has to send a QUIT command. I haven't designed the network application, so redesigning the protocol isn't an option
Ideally, when an EOF is thrown because of SetDeadline timing out, then I would want the goroutine to awaken, but not for the connection to close
Thank you for your help in advance!
Apparently it doesn't. My workaround (well, not workaround, but the correct way to do this) was like so
timeout := make(chan error)
buf := make([]byte, 32)
go func() {
_, err := conn.Read(buf)
timeout <- err
}()
select {
case time.After(time.Now() + 1000 * 1000 * 1000 * 5): // Wait for 5 seconds
// Timed out reading
go func() {
<-timeout // We have to read from the sem to prevent mem leaks
}()
case err := <-timeout:
// Successfully read
}