i have a problem with my golang server in which i'm using websockets.
The server opens the connection and the client could connect to it, but the problem is that when the server starts sending the data to the client, the client connection is closed after a small period of time. i suggest that the problem is with the server and not with the client because i tried to connect to the server with another web client, and it's the same issue. I didn't understand the cause ! Can someone help me?
server.go:
func Echo(ws *websocket.Conn) {
fmt.Println("Echoing")
for {
msg := MessageReceived{Name: "OrderCommand", Nbmsg: 3}
if err := websocket.JSON.Send(ws, msg); err != nil {
fmt.Println("Can't send")
break
}
//os.Exit(0)
}
}
func checkError(err error) {
if err != nil {
Log("Fatal error ", err.Error())
os.Exit(1)
}
}
func main() {
http.HandleFunc("/save", saveHandler)
http.Handle("/", websocket.Handler(Echo))
err:= http.ListenAndServe(":8081", nil)
checkError(err)
}
and client.go:
import (
"code.google.com/p/go.net/websocket"
"fmt"
"log"
)
func main() {
origin := "http://localhost/"
url := "ws://localhost:8081/echo"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
var msg = make([]byte, 512)
var n int
if n, err = ws.Read(msg); err != nil {
log.Fatal(err)
}
fmt.Printf("Received: %s.\n", msg[:n])
}
Your problem, as others have pointed out, is that you must receive a message as well.
Currently, when someone connects, your program will step into the for-loop and start bombarding the client with messages. Probably not the intended behaviour of an echo server.
First you want to Receive a message, then Send a reply:
func Echo(ws *websocket.Conn) {
fmt.Println("Echoing")
msg := new(MessageReceived)
for {
// The server blocks here until a message from the client is received
websocket.JSON.Receive(ws, &msg)
fmt.Printf("Received message: %+v\n", msg)
// Reencode the same message and send it back
if err := websocket.JSON.Send(ws, msg); err != nil {
fmt.Println("Can't send echo")
break
}
}
}
A full working version can be found at Playground: http://play.golang.org/p/nQ3fJ5Nb0I
Since it uses websockets, you must compile it on your local computer.
Why using ws.Read when you can use websocket.JSON.Receive to deserialize the message?
Here are the server: http://play.golang.org/p/NZ6VJ4daGm
and the client: http://play.golang.org/p/rkJVKGhrGk (that I have changed to receive 10 messages before exiting).
The string "Can't send" will be printed by the server once the client closes the websocket connection.
Related
I am learning from the book An Introduction to Programming in Go by Caleb Doxsey
In chapter 13 about servers we are given the code:
package main
import (
"encoding/gob"
"fmt"
"net"
)
func server() {
// listen on a port
ln, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Println("server, Listen", err)
return
}
for {
// accept a connection
c, err := ln.Accept()
if err != nil {
fmt.Println("server, Accept", err)
continue
}
// handle the connection
go handleServerConnection(c)
}
}
func handleServerConnection(c net.Conn) {
// receive the message
var msg string
err := gob.NewDecoder(c).Decode(&msg)
if err != nil {
fmt.Println("handleServerConnection", err)
} else {
fmt.Println("Received", msg)
}
c.Close()
}
func client() {
// connect to the server
c, err := net.Dial("tcp", "127.0.0.1:9999")
if err != nil {
fmt.Println("client, Dial", err)
return
}
// send the message
msg := "Hello World"
fmt.Println("Sending", msg)
err = gob.NewEncoder(c).Encode(msg)
if err != nil {
fmt.Println("client, NewEncoder", err)
}
c.Close()
}
func main() {
go server()
go client()
var input string
fmt.Scanln(&input)
}
Running this code I almost always receive:
client, Dial dial tcp 127.0.0.1:9999: connect: connection refused
But sometimes I receive:
Sending Hello World
Received Hello World
I have also discovered if I run just run server separately from client, and then run client on a separate file, it works as intended. Why is that?
Listen and Dial are called concurrently, and you can't predict which one executes first. If Dial executes before Listen then there is obviously nothing listening yet and that produces the error.
Call Listen in main, before starting the goroutines:
func main() {
ln, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Fatal("server, Listen", err)
}
go server(ln)
go client()
var input string
fmt.Scanln(&input)
}
func server(ln net.Listener) {
for {
// accept a connection
c, err := ln.Accept()
if err != nil {
fmt.Println("server, Accept", err)
continue
}
// handle the connection
go handleServerConnection(c)
}
}
I'm trying to write a simple go websocket server with gorilla/websocket
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
if os.Getenv("env") == "development" {
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Websocket error: %s", err)
return
}
defer conn.Close()
// Register client
clients[conn] = true
for {
message := message.Message{}
_, msg, err := conn.ReadMessage()
if err != nil {
log.Printf("Websocket error: %s", err)
return
}
res, _ = json.Marshal(context.Game)
// Send to every client that is currently connected
for client := range clients {
err := client.WriteMessage(websocket.TextMessage, res)
if err != nil {
// Remove connection
client.Close()
delete(clients, client)
}
}
}
})
the _, msg, err := conn.ReadMessage() line is throwing an error and closing the websocket, but I'm not sure why.
The error is close 1006 (abnormal closure): unexpected EOF. How can I prevent this?
The error indicates that the peer closed the connection without sending a close message. The RFC calls this "abnormal closure", but the error is normal to receive.
Use IsUnexpectedCloseError to filter out expected close errors. The Gorilla Chat Example shows how use the function (view code here).
The application in the question has a data race on clients as pointed out by Adrian. The Gorilla Chat Example shows how to maintain a map of clients without a data race (hub).
I have a secure websocket server running on localhost:443/server-demo ( jetty websocket server).
Now I am writing a go client that can communicate with the websocket server. I am able to connect to the websocket server using right certificates. Here is the sample code.
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
)
func main() {
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
conn, err := tls.Dial("tcp", "127.0.0.1:443", &config)
if err != nil {
log.Fatalf("client: dial: %s", err)
}
defer conn.Close()
log.Println("client: connected to: ", conn.RemoteAddr())
state := conn.ConnectionState()
for _, v := range state.PeerCertificates {
fmt.Println(x509.MarshalPKIXPublicKey(v.PublicKey))
fmt.Println(v.Subject)
}
log.Println("client: handshake: ", state.HandshakeComplete)
log.Println("client: mutual: ", state.NegotiatedProtocolIsMutual)
message := "Hello\n"
n, err := io.WriteString(conn, message)
if err != nil {
log.Fatalf("client: write: %s", err)
}
log.Printf("client: wrote %q (%d bytes)", message, n)
reply := make([]byte, 256)
n, err = conn.Read(reply)
log.Printf("client: read %q (%d bytes)", string(reply[:n]), n)
log.Print("client: exiting")
}
The above code throws this error:
"HTTP/1.1 400 No URI\r\nContent-Type: text/html;charset=iso-8859-1\r\nContent-Length: 49\r\nConnection: close\r\nServer: Jetty(9.4.19.v20190610)\r\n\r\n<h1>Bad Message 400</h1><pre>reason: No URI</pre>" (188 bytes)
My question is after making the connection how can I send message to particular URI? i.e I want to send a message to wss://localhost:443/server-demo.
The code in a question does not establish a WebSocket connection to the server.
To establish the WebSocket connection, the application must write a WebSocket handshake to conn and receive the handshake response. See the RFC for the details.
Most applications use a websocket package than handles all of these details. The gorilla/websocket package is a popular choice.
This code should get you started with gorilla:
cert, err := tls.LoadX509KeyPair("nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin.pem", "nifi-1.10.0-bin/nifi-1.10.0/extras/gen-certs/certs/admin-key.pem")
if err != nil {
log.Fatalf("server: loadkeys: %s", err)
}
config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
d := websocket.Dialer{
TLSClientConfig: &config,
}
c, _, err := d.Dial("wss://localhost:443/server-demo", nil)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Use `c` to send and receive messages
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())
}
}
I believe that this question is almost the same with this.
But I use websocket in Echo framework instead of Gorilla. So I think the approach will be different.
Echo does provide the example. But it only shows how to connect with single client. When there are more than one client, the other clients don't receive message from server.
How do I make the server broadcasts message to all connected clients?
The accepted answer from referred link says that I have to use connection pool to broadcast messages to all connections. How can I do this in Echo framework?
You indeed need to follow the same principle of connection pool.
Here is the Echo example, with a very basic implementation of a pool:
package main
import (
"fmt"
"sync"
"github.com/labstack/echo"
mw "github.com/labstack/echo/middleware"
"golang.org/x/net/websocket"
)
var connectionPool = struct {
sync.RWMutex
connections map[*websocket.Conn]struct{}
}{
connections: make(map[*websocket.Conn]struct{}),
}
func main() {
e := echo.New()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Static("/", "public")
e.WebSocket("/ws", func(c *echo.Context) (err error) {
ws := c.Socket()
connectionPool.Lock()
connectionPool.connections[ws] = struct{}{}
defer func(connection *websocket.Conn){
connectionPool.Lock()
delete(connectionPool.connections, connection)
connectionPool.Unlock()
}(ws)
connectionPool.Unlock()
msg := ""
for {
if err = websocket.Message.Receive(ws, &msg); err != nil {
return err
}
err = sendMessageToAllPool(msg)
if err != nil {
return err
}
fmt.Println(msg)
}
return err
})
e.Run(":1323")
}
func sendMessageToAllPool(message string) error {
connectionPool.RLock()
defer connectionPool.RUnlock()
for connection := range connectionPool.connections {
if err := websocket.Message.Send(connection, message); err != nil {
return err
}
}
return nil
}