I am writing a simple go program that connects with a socket server.
I am using a goroutine to continuously look for the incoming messages.
But inside the goroutine connection gets closed. Can someone suggest me what is happening?
Below is my test client
package main
import (
"fmt"
"net"
"os"
"encoding/json"
"bufio"
)
type test_user struct {
ID string
Conn net.Conn
Messages chan interface{}
}
func CreateTestUser(id string) test_user {
return test_user{id, nil, nil}
}
/*
1. Try connecting
2. Wait for connected packet
3. Send first packet (Init packet)
*/
func (user *test_user) Connect() {
conn, err := net.Dial(CONN_TYPE, CONN_HOST+":"+CONN_PORT)
if err != nil {
fmt.Println("Could not connect", err.Error())
os.Exit(1)
}
user.Conn = conn
user.Messages = make(chan interface{})
reader := bufio.NewReader(conn)
message, _ := reader.ReadBytes(DELIMITER)
user.SendMessage(InitPacket{TYPE_USER,user.ID})
//message, _ = reader.ReadBytes(DELIMITER)
//message, _ = reader.ReadBytes(DELIMITER) // If I uncomment these lines it will wait for new messages
//message, _ = reader.ReadBytes(DELIMITER)
go func() {
for {
var readErr error
message, readErr = reader.ReadBytes(DELIMITER) // this always returns error "Error on read read tcp 127.0.0.1:54694->127.0.0.1:3333: use of closed network connection"/
if readErr == nil {
var packet interface{}
error := json.Unmarshal(message, &packet)
if error != nil {
fmt.Println("Unable to parse message", string(message), error.Error())
}
user.Messages <- packet
} else {
fmt.Println("Error on read", readErr.Error())
}
}
}()
}
Can someone please help me with this. Also I am new to golang to please suggests improvements as well.
It'd be helpful to see a bit more of your code outside Connect.
Connect launches a new goroutine, but it doesn't wait for it - it returns immediately. Could the connection be closed after Connect returns?
Generally when you launch a goroutine to handle a connection, your "main" goroutine would keep waiting for other things - new connections and/or some handshake with the per-connection goroutines. I suspect this is not what's happening here.
Related
I've been trying to find a way to stop a listening server in Go gracefully. Because listen.Accept blocks it is necessary to close the listening socket to signal the end, but I can't tell that error apart from any other errors as the relevant error isn't exported.
Can I do better than this? See FIXME in the code below in serve()
package main
import (
"io"
"log"
"net"
"time"
)
// Echo server struct
type EchoServer struct {
listen net.Listener
done chan bool
}
// Respond to incoming connection
//
// Write the address connected to then echo
func (es *EchoServer) respond(remote *net.TCPConn) {
defer remote.Close()
_, err := io.Copy(remote, remote)
if err != nil {
log.Printf("Error: %s", err)
}
}
// Listen for incoming connections
func (es *EchoServer) serve() {
for {
conn, err := es.listen.Accept()
// FIXME I'd like to detect "use of closed network connection" here
// FIXME but it isn't exported from net
if err != nil {
log.Printf("Accept failed: %v", err)
break
}
go es.respond(conn.(*net.TCPConn))
}
es.done <- true
}
// Stop the server by closing the listening listen
func (es *EchoServer) stop() {
es.listen.Close()
<-es.done
}
// Make a new echo server
func NewEchoServer(address string) *EchoServer {
listen, err := net.Listen("tcp", address)
if err != nil {
log.Fatalf("Failed to open listening socket: %s", err)
}
es := &EchoServer{
listen: listen,
done: make(chan bool),
}
go es.serve()
return es
}
// Main
func main() {
log.Println("Starting echo server")
es := NewEchoServer("127.0.0.1:18081")
// Run the server for 1 second
time.Sleep(1 * time.Second)
// Close the server
log.Println("Stopping echo server")
es.stop()
}
This prints
2012/11/16 12:53:35 Starting echo server
2012/11/16 12:53:36 Stopping echo server
2012/11/16 12:53:36 Accept failed: accept tcp 127.0.0.1:18081: use of closed network connection
I'd like to hide the Accept failed message, but obviously I don't want to mask other errors Accept can report. I could of course look in the error test for use of closed network connection but that would be really ugly. I could set a flag saying I'm about to close and ignore errors if that was set I suppose - Is there a better way?
I would handle this by using es.done to send a signal before it closes the connection. In addition to the following code you'd need to create es.done with make(chan bool, 1) so that we can put a single value in it without blocking.
// Listen for incoming connections
func (es *EchoServer) serve() {
for {
conn, err := es.listen.Accept()
if err != nil {
select {
case <-es.done:
// If we called stop() then there will be a value in es.done, so
// we'll get here and we can exit without showing the error.
default:
log.Printf("Accept failed: %v", err)
}
return
}
go es.respond(conn.(*net.TCPConn))
}
}
// Stop the server by closing the listening listen
func (es *EchoServer) stop() {
es.done <- true // We can advance past this because we gave it buffer of 1
es.listen.Close() // Now it the Accept will have an error above
}
Check some "is it time to stop" flag in your loop right after the accept() call, then flip it from your main, then connect to your listening port to get server socket "un-stuck". This is very similar to the old "self-pipe trick".
Something among these lines might work in this case, I hope:
// Listen for incoming connections
func (es *EchoServer) serve() {
for {
conn, err := es.listen.Accept()
if err != nil {
if x, ok := err.(*net.OpError); ok && x.Op == "accept" { // We're done
log.Print("Stoping")
break
}
log.Printf("Accept failed: %v", err)
continue
}
go es.respond(conn.(*net.TCPConn))
}
es.done <- true
}
Here's a simple way that's good enough for local development.
http://www.sergiotapia.me/how-to-stop-your-go-http-server/
package main
import (
"net/http"
"os"
"github.com/bmizerany/pat"
)
var mux = pat.New()
func main() {
mux.Get("/kill", http.HandlerFunc(kill))
http.Handle("/", mux)
http.ListenAndServe(":8080", nil)
}
func kill(w http.ResponseWriter, r *http.Request) {
os.Exit(0)
}
I am getting back error websocket: close sent when I try to send data from my server side to client side(dashboard page). The error happen when I navigate to home page and back to dashboard page. Everything works fine initially with the Dashboard Page
My dashboard page code
let socket = new ReconnectingWebSocket("ws://127.0.0.1:8004/wsendpoint");
console.log("Attempting Connection...");
socket.onopen = () => {
console.log("Successfully Connected");
};
socket.onclose = event => {
console.log("Socket Closed Connection: ", event);
};
socket.onerror = error => {
console.log("Socket Error: ", error);
};
socket.onmessage = function (event) {
console.log("message received: " + event.data);
}
My Server side code (writer and reader
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
// upgrade this connection to a WebSocket connection
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("upgrade error %s", err)
}
defer ws.Close()
go writer(ws)
go reader(ws)
}
Writer, get data from a channel when new data come in from database
func writer(conn *websocket.Conn) {
for {
singleset := <-singleorder // get data from channel
jsonString, err := json.Marshal(singleset )
err = conn.WriteMessage(1, []byte(jsonString))
if err != nil {
log.Println(err)
}
}
Reader, read data from client side
func reader(conn *websocket.Conn) {
for {
_, p, err := conn.ReadMessage() //what is message type?
if err != nil {
log.Println("there is errors%s", err)
return
}
}
}
I also got error WebSocket is closed before the connection is established. and reconnecting-websocket.min.js:1 WebSocket connection to 'ws://127.0.0.1:8004/wsendpoint' failed: WebSocket is closed before the connection is established.
As you can see all the code are very simple because I just start to learn websocket and follow simple tutorial. I tried to search on web but webworker seems little bit complex for me and saw about the ping pong method but I am not sure if its still valid if I navigate. Can I establish websocket on home page so the connection is not closed ? Since I only have two pages on client side.
Thanks in advance for any guidance on how to deal with these situation!
The basic strategy for dealing with navigation away from the page, page close or errors is this: The client creates the websocket connection on page load. The server expects client connections to come and go and cleans up resources when the client connection errors.
Here's why you get the error 'websocket: close sent': When the user navigates away from the page, the browser sends a close message to the server as part of the websocket closing handshake. The Gorilla package responds to the close message by sending another close message back to the client. From that point on, the server cannot send messages on the connection. The connection returns an error from write methods after the close message is sent.
Close messages are returned as errors from the websocket read methods. To fix the problem, modify the code to handle errors in general. There's no need to handle the closing handshake specifically.
Here's the updated code. The wsEndpoint function creates a channel for the reader to signal the writer that the reader is done. The defer ws.Close() statement is removed because the reader and writer goroutines will take responsibility for closing the connection.
func wsEndpoint(w http.ResponseWriter, r *http.Request) {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
// upgrade this connection to a WebSocket connection
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("upgrade error %s", err)
}
done := make(chan struct{})
go writer(ws, done)
go reader(ws, done)
}
The reader closes the connection on return. It also closes the done channel to signal the writer that the reader is done.
func reader(conn *websocket.Conn, done chan struct{}) {
defer conn.Close()
defer close(done)
for {
_, p, err := conn.ReadMessage() //what is message type?
if err != nil {
log.Println("there is errors%s", err)
return
}
}
}
The writer also closes the connection on return. When the connection is closed, read on the connection immediately returns an error causing the read goroutine to complete. The writer waits on the done channel. Receive on the channel yields the zero value when the channel is closed by the reader. The writer returns on write errors instead of looping forever as in the question.
func writer(conn *websocket.Conn, done chan struct{}) {
defer conn.Close()
for {
select {
case <-done:
// the reader is done, so return
return
case singleset := <-singleorder: // get data from channel
jsonString, err := json.Marshal(singleset )
err = conn.WriteMessage(1, []byte(jsonString))
if err != nil {
log.Println(err)
return
}
}
}
}
An application should expect the connection to be closed for any number of reasons including the user navigating away from the page.
It's likely that the code that sends to channel singleorder needs to know that the connection was closed, but we cannot see that code here. I'll leave it to you to figure out how to handle that.
I'm writing a Go app which accepts a websocket connection, then starts:
listen goroutine which listens on the connection for client messages and sends response for the client based on the received message via channel to updateClient.
updateClient goroutine which writes to the connection.
processExternalData goroutine which receives data from message queue, sends the data to updateClient via a channel so that updateClient can update the client with the data.
I'm using gorilla library for websocket connections, and its read call is blocking. In addition, both its write and read methods don't support concurrent calls, which is the main reason I have the updateClient goroutine which is the single routine which calls write method.
The problem arises when I need to close the connection which can happen at least in 2 cases:
The client closed the connection or error occurred during read.
processExternalData finished, there's no more data to update the client and the connection should be closed.
So updateClient needs to somehow notify listen to quit and vice versa listen needs to somehow notify updateClient to quit. updateClient has a quit channel inside select but listen can't have select because it already has a for loop with blocking read call inside.
So what I did is I added isJobFinished field on the connection type which is a condition for for loop to work:
type WsConnection struct {
connection *websocket.Conn
writeChan chan messageWithCb
quitChan chan bool
isJobFinished bool
userID string
}
func processExternalData() {
// receive data from message queue
// send it to client via writeChan
}
func (conn *WsConnection) listen() {
defer func() {
conn.connection.Close()
conn.quitChan <- true
}()
// keep the loop for communication with client
for !conn.isJobFinished {
_, message, err := conn.connection.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
// convert message to type messageWithCb
switch clientMessage.MessageType {
case userNotFound:
conn.writeChan <- messageWithCb{
message: map[string]interface{}{
"type": user,
"payload": false,
},
}
default:
log.Printf("Unknown message type received: %v", clientMessage)
}
}
log.Println("end of listen")
}
func updateClient(w http.ResponseWriter, req *http.Request) {
upgrader.CheckOrigin = func(req *http.Request) bool {
return true
}
connection, err := upgrader.Upgrade(w, req, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
wsConn := &WsConnection{
connection: connection,
writeChan: make(chan messageWithCb),
quitChan: make(chan bool),
}
go wsConn.listen()
for {
select {
case msg := <-wsConn.writeChan:
err := connection.WriteJSON(msg.message)
if err != nil {
log.Println("connection.WriteJSON error: ", err)
}
if wsConn.isJobFinished {
if msg.callback != nil {
msg.callback() // sends on `wsConn.quitChan` from a goroutine
}
}
case <-wsConn.quitChan:
// clean up
wsConn.connection.Close()
close(wsConn.writeChan)
return
}
}
}
I'm wondering if a better pattern exists in Go for such cases. Specifically, I'd like to be able to have a quit channel inside listen as well so updateClient can notify it to quit instead of maintaining isJobFinished field. Also in this case there's no danger of not protecting isJobFinished field because only one method writes to it but if the logic gets more complicated then having to protect the field inside the for loop in listen will probably negatively impact the performance.
Also I can't close the quiteChan because both listen and updateClient use it and there's no way to know for them when it's closed by another one.
Close the connection to break the listen goroutine out of the blocking read call.
In updateClient, add a defer statement to close the connection and clean up other resources. Return from the function on any error or a notification from the quit channel:
updateClient(w http.ResponseWriter, req *http.Request) {
upgrader.CheckOrigin = func(req *http.Request) bool {
return true
}
connection, err := upgrader.Upgrade(w, req, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer connection.Close() // <--- Add this line
wsConn := &WsConnection{
connection: connection,
writeChan: make(chan messageWithCb),
quitChan: make(chan bool),
}
defer close(writeChan) // <-- cleanup moved out of loop below.
go wsConn.listen()
for {
select {
case msg := <-wsConn.writeChan:
err := connection.WriteJSON(msg.message)
if err != nil {
log.Println("connection.WriteJSON error: ", err)
return
}
case <-wsConn.quitChan:
return
}
}
}
In the listen function, loop until error reading the connection. Read on the connection returns immediately with an error when updateClient closes the connection.
To prevent listen from blocking forever in the case where updateClient returns first, close the quit channel instead of sending a value.
func (conn *WsConnection) listen() {
defer func() {
conn.connection.Close()
close(conn.quitChan) // <-- close instead of sending value
}()
// keep the loop for communication with client
for {
_, message, err := conn.connection.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
// convert message to type messageWithCb
switch clientMessage.MessageType {
case userNotFound:
conn.writeChan <- messageWithCb{
message: map[string]interface{}{
"type": user,
"payload": false,
},
}
default:
log.Printf("Unknown message type received: %v", clientMessage)
}
}
log.Println("end of listen")
}
The field isJobFinished is not needed.
One problem with the code in the question and in this answer is that close of writeChan is not coordinated with sends to the channel. I cannot comment on a solution to this problem without seeing the processExternalData function.
It may make sense to use a mutex instead of a goroutine to limit write concurrency. Again, the code in the processExternalData function is required to comment further on this topic.
I want to create one to one chat in revel framework but it gives error. Firstly work in revel chat according to demo but refreshing page did not work so I tried this method and dont know how to handle single chat.
Here is an error:
app server.go:2848: http: panic serving 127.0.0.1:50420: interface conversion: interface is nil, not io.Writer goroutine 166 [running]: net/http.(*conn).serve.func1(0xc4201d03c0)
my go code is where I handle ws root,single user chat need to db connection to. I'm using posgres for it
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var clients = make(map[*websocket.Conn]bool) // connected clients
var broadcast = make(chan Message) // broadcast channel
// Configure the upgrader
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// Define our message object
type Message struct {
Email string `json:"email"`
Username string `json:"username"`
Message string `json:"message"`
Created string `json:"created"`
}
func main() {
// Create a simple file server
fs := http.FileServer(http.Dir("public"))
http.Handle("/", fs)
// Configure websocket route
http.HandleFunc("/ws", handleConnections)
// Start listening for incoming chat messages
go handleMessages()
// Start the server on localhost port 8000 and log any errors
log.Println("http server started on :8090")
err := http.ListenAndServe(":8090", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
// Upgrade initial GET request to a websocket
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatal(err)
}
// Make sure we close the connection when the function returns
defer ws.Close()
// Register our new client
clients[ws] = true
for {
var msg Message
// Read in a new message as JSON and map it to a Message object
err := ws.ReadJSON(&msg)
if err != nil {
log.Printf("error: %v", err)
delete(clients, ws)
break
}
// Send the newly received message to the broadcast channel
broadcast <- msg
}
}
func handleMessages() {
for {
// Grab the next message from the broadcast channel
msg := <-broadcast
// Send it out to every client that is currently connected
for client := range clients {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(clients, client)
}
}
}
}
I think you use gorilla/websocket API incorrectly. You copied the echo example which, being a basic demo, is expected to handle a single ws connection only. Start with the chat example. Particularly pay attention to the fact that serveWs is a non-blocking call while your handleConnections is blocking, i.e. it never returns. Take a look here for a full-featured example of gorilla/websocket API use:
https://github.com/tinode/chat/blob/master/server/wshandler.go
As correctly pointed out by Cerise L, you most certainly have a race on your clients although I think it's unlikely to produce a panic. I think the most likely source of panic is a call to Upgrade on a closed http connection. It's impossible to say exactly because you did not post the full output of the panic.
I do have a process receiving data from second local process. I need to test if connection errors are handled well AND if it automatically reconnects and keep receiving data after a disconnection.
To do so I am trying to make it disconnect abruptly or put the TCP connection in an error state from a unit test.
As seen in this question to check if a connection is closed I am checking for data to come and test if it returns an error.
I am not sure how to:
close the connection ungracefully
make it be in an error state
This is the essence of my data receiver:
import (
"bufio"
"encoding/json"
"fmt"
"io"
"net"
)
type Message struct {
ID string `json:"id"`
}
func ReceiveData(listener Listener) {
var tcpConn net.Conn
var addr string = "127.0.0.1:9999"
tcpConn, err := net.Dial("tcp", addr)
socketReader := bufio.NewReader(tcpConn)
decoder := json.NewDecoder(socketReader)
for {
var msg Message
if err := decoder.Decode(&msg); err == io.EOF {
listener.ProcessUpdate(Message{}, fmt.Errorf("Received EOF"), nil)
tcpConn = nil
return
} else if err != nil {
listener.ProcessUpdate(Message{}, nil, fmt.Errorf("Error decoding message: %s", err.Error()))
tcpConn = nil
return
}
// process message
_ = msg
// Test disconnection
// This does not disconnect:
// tcpConn = nil
// This does but gracefully:
// tcpConn.Close()
}
I am not mocking the TCP connection as I'd like to try with the real data producer. If is needed I'll look at it.
A solution is to set a deadline to the TCP connection itself:
tcpConn.SetDeadline(time.Now())
Later this will trigger a timeout error which can be caught with:
err := decoder.Decode(&msg);
if err != nil {
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
fmt.Errorf("TCP timeout : %s", err.Error())
} else {
fmt.Errorf("Received error decoding message: %s", err.Error())
}
}