GoLang TCP Connection - Remote Network is down check - go

I am creating a GoLang application and clients are android phones. I am able to handle connections. If user closes the android application connection is dropped with EOF
My problem is, if client just turn off wifi network connection is still alive.
Here is my code
func main() {
fmt.Println("Starting server...")
connection, err := net.Listen("tcp", ":4406")
if err != nil {
fmt.Println(err)
}
defer connection.Close()
manager := ClientManager{
clients: make(map[*Client]bool),
broadcast: make(chan []byte),
register: make(chan *Client),
}
go manager.start()
for {
connection, _ := connection.Accept()
if err != nil {
fmt.Println(err)
}
client := &Client{socket: connection, data: make(chan []byte), uuid: connection.RemoteAddr().String()}
manager.register <- client
go manager.receive(client)
go handleConnection(client)
}
}
Handeling connections
func handleConnection(client *Client) {
conn := client.socket
defer conn.Close()
notify := make(chan error)
go func() {
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
notify <- err
return
}
if n > 0 {
fmt.Println("unexpected data: %s", buf[:n])
}
}
}()
for {
select {
case err := <-notify:
if err != nil {
fmt.Println("connection dropped message", err)
return
}
case <-time.After(time.Second * 1):
fmt.Println("timeout 1, still alive")
}
}
}
When remote wifi is off (cable removed) I want to disconnect the user. I tried to read a byte and every second and it is reading it. I sent a byte and it is sent as well.

Related

Running a consumer and api on port golang

I have a go api project where I also run a worker (RabbitMQ). I just discovered a problem that my worker and my http listen and serve do not work together. The moment I run the worker, the port of api is not reached.
Here is what my code looks like.
app.go
func (a *App) StartWorker() {
connection, err := amqp091.Dial(os.Getenv("AMQP_URL"))
if err != nil {
panic(err)
}
defer connection.Close()
consumer, err := events.NewConsumer(connection, database.GetDatabase(a.Database))
if err != nil {
panic(err)
}
consumer.Listen(os.Args[1:])
}
func (a *App) Run(addr string) {
logs := log.New(os.Stdout, "my-service", log.LstdFlags)
server := &http.Server{
Addr: addr,
Handler: a.Router,
ErrorLog: logs,
IdleTimeout: 120 * time.Second, // max time for connections using TCP Keep-Alive
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
if err := server.ListenAndServe(); err != nil {
logs.Fatal(err)
}
}()
// trap sigterm or interrupt and gracefully shutdown the server
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
signal.Notify(c, os.Kill)
sig := <-c
logs.Println("Recieved terminate, graceful shutdown", sig)
tc, _ := context.WithTimeout(context.Background(), 30*time.Second)
server.Shutdown(tc)
}
here is my
consumer.go
// NewConsumer returns a new Consumer
func NewConsumer(conn *amqp.Connection, db *mongo.Database) (Consumer, error) {
consumer := Consumer{
conn: conn,
db: db,
}
err := consumer.setup()
if err != nil {
return Consumer{}, err
}
return consumer, nil
}
// Listen will listen for all new Queue publications
// and print them to the console.
func (consumer *Consumer) Listen(topics []string) error {
ch, err := consumer.conn.Channel()
if err != nil {
return err
}
defer ch.Close()
if err != nil {
return err
}
msgs, err := ch.Consume("update.package.rating", "", true, false, false, false, nil)
if err != nil {
return err
}
forever := make(chan bool)
go func() {
for msg := range msgs {
switch msg.RoutingKey {
case "update.package.rating":
worker.RatePackage(packageRepo.NewPackagesRepository(consumer.db), msg.Body)
}
// acknowledege received event
log.Printf("Received a message: %s", msg.Body)
}
}()
log.Printf("[*] Waiting for message [Exchange, Queue][%s, %s]. To exit press CTRL+C", getExchangeName(), "update.package.rating")
<-forever
return nil
}
main.go
func main() {
start := app.App{}
start.StartApp()
start.StartWorker()
start.Run(":3006")
}
the port 3006 is not reached.
I am using gin-gonic to serve my http request.
Any help is welcomed.
I had a similar problem while using gin framework.Solved the issue by running my consumer inside a go routine.I invoked my consumer like below.
go notificationCallback.ConsumeBankTransaction()
and both the server and the rabbitmq consumer run seamlessly.Still monitoring performance to see if it is robust and resilient enough.

Go net.Conn.Write sending EOF error on TCP Proxy

Overview
I'm currently working on a simple TCP proxy to port forward a connection to an echo server that I've placed on a server in my house. I'm running the TCP proxy and client on the same machine, the echo server is the only program on another device.
The client is just a simple python script that declares and initializes a TCP socket and invokes a socket.send(b'testing some random byte message\n')
If I direct connect the client to the server, everything works fine but it's when I utilize the client/proxy connection when the server never receives the msg. In fact, the server outputs that the proxy and initiated a connection but only when I try to send a packet through from the client is when the server detects an io.EOF error.
Connections
server listening on : 192.168.1.253:6969
proxy listening on : 0.0.0.0:6969
proxy forwarding to server listener
client connecting on proxy listener
I've reduced the functionality to only have the proxy read and send once, then exit. Please excuse any remaining artifacts...
Proxy
func main() {
dstDone := make(chan struct{})
msgChannel := make(chan []byte)
go func(msgs <-chan []byte) {
dst, err := net.DialTimeout("tcp", "192.168.1.253:6969", 750*time.Millisecond)
if err != nil {
fmt.Println("Error connecting to dst -", err)
os.Exit(2)
}
dstWriter := bufio.NewWriter(dst)
n, err := dstWriter.Write(<-msgs)
if err != nil {
fmt.Println("Error writing to dst -", err)
os.Exit(1)
}
fmt.Println("Wrote ", n, "B to dst")
dstDone <- struct{}{}
}(msgChannel)
srcListener, err := net.Listen("tcp", ":6969")
if err != nil {
fmt.Println("Error listening for source -", err)
os.Exit(1)
}
srcConnection, err := srcListener.Accept()
if err != nil {
fmt.Println("Error accepting connection -", err)
os.Exit(1)
}
srcReader := bufio.NewReader(srcConnection)
msg, merr := srcReader.ReadBytes('\n')
if merr != nil {
fmt.Println("Error reading socket info -", err)
os.Exit(1)
}
fmt.Println("Message received [", msg, "]")
fmt.Println("Sending to dst")
msgChannel <- msg
<-dstDone
}
Server
// Start() is called in another go file's main which only consists of this call and
// initializes a channel for indicating when to quit.
func Start(serverDone *chan struct{}) {
listener, err := net.Listen("tcp", ":6969")
if err != nil {
fmt.Println("Could not listen:", err)
*serverDone <- struct{}{}
}
go func(serverDone *chan struct{}) {
buffer := make([]byte, 4096)
fmt.Println("-- Server Running --\nEnter 'Q' to quit.")
for {
s, err := os.Stdin.Read(buffer)
if err != nil {
fmt.Printf("%s\n", err)
break
}
if s > 2 {
fmt.Printf("Invalid Command\n")
} else {
if buffer[0] == 81 {
break
}
}
}
*serverDone <- struct{}{}
}(serverDone)
for {
connection, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
*serverDone <- struct{}{}
break
}
fmt.Printf("%v connected\n", connection.RemoteAddr())
go customBufferEcho(&connection)
}
}
func customBufferEcho(connection *net.Conn) {
defer (*connection).Close()
inBuffer := make([]byte, 4096)
outBuffer := make([]byte, 4096)
for {
fmt.Println("Waiting for msg...")
n, err := (*connection).Read(inBuffer[0:]) // tried only 'inBuffer' and did not work
if err == io.EOF {
fmt.Println("Client disconnected") // Server prints this msg on any msg sent from proxy
return
}
if err != nil {
fmt.Println("Error reading from connection |", err)
return
}
fmt.Println("-- MSG Received [ ", n, "B ]--\n", Dump(inBuffer, 4, 4))
outBuffer = inBuffer
(*connection).Write(outBuffer[0:n])
}
}
Wireshark Inspection

Go : Cancel Context inside a For loop

I am trying to create a UDP server in Golang to Listen at a port for eg. 1234. I have a client which sends the start/stop message to this server.
On receiving of message "start", the server will start sending random data to this client and on the stop, the server will stop sending to the client.
For this purpose, I am using context to create a goroutine to send the data and cancel it when it gets "stop".
The error I am getting is the program works fine for one client, but if I start the client again the data is not sent again.
Any help would be appreciated?
UDP server Code:
package main
import (
"context"
"fmt"
"math/rand"
"net"
"time"
)
func generateMessageToUDP(ctx context.Context, addr *net.UDPAddr) {
// stop writing to UDP
done := false
fmt.Println("Generating message to UDP client", addr)
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
fmt.Println("Error: ", err)
}
defer func(conn *net.UDPConn) {
err := conn.Close()
if err != nil {
fmt.Println("Error in closing the UDP Connection: ", err)
}
}(conn)
// write to address using UDP connection
go func() {
for i := 0; !done; i++ {
RandomInt := rand.Intn(100)
fmt.Println("Random Int: ", RandomInt)
_, err = conn.Write([]byte(fmt.Sprintf("%d", RandomInt)))
fmt.Println("Sent ", RandomInt, " to ", addr)
time.Sleep(time.Second * 1)
}
}()
<-ctx.Done()
fmt.Println("Stopping writing to UDP client", addr)
done = true
}
//var addr *net.UDPAddr
//var conn *net.UDPConn
func main() {
fmt.Println("Hi this is a UDP server")
udpServer, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 5010})
if err != nil {
fmt.Println("Error: ", err)
}
defer func(udpServer *net.UDPConn) {
err := udpServer.Close()
if err != nil {
fmt.Println("Error in closing the UDP Connection: ", err)
}
}(udpServer)
// create a buffer to read data into
buffer := make([]byte, 1024)
ctx, cancel := context.WithCancel(context.Background())
for {
// read the incoming connection into the buffer
n, addr, err := udpServer.ReadFromUDP(buffer)
fmt.Println("Recieved ", string(buffer[0:n]), " from ", addr)
if err != nil {
fmt.Println("Error: ", err)
}
fmt.Println("Received ", string(buffer[0:n]), " from ", addr)
if string(buffer[0:n]) == "stop" {
fmt.Println("Stopped listening")
cancel()
continue
} else if string(buffer[0:n]) == "start" {
// send a response back to the client
_, err = udpServer.WriteToUDP([]byte("Hi, I am a UDP server"), addr)
if err != nil {
fmt.Println("Error: ", err)
}
// start a routine to generate messages to the client
generateMessageToUDP(ctx, addr)
} else {
fmt.Println("Unknown command")
}
}
}
Client Code:
package main
import (
"fmt"
"net"
"time"
)
func main() {
fmt.Println("Hello, I am a client")
// Create a new client
localAddr, err := net.ResolveUDPAddr("udp", ":5011")
client3, err := net.DialUDP("udp", localAddr, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 5010})
if err != nil {
fmt.Println(err)
return
}
defer client3.Close()
_, err = client3.Write([]byte("start"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Message sent. Sleeping for 5 seconds")
time.Sleep(time.Second * 5)
fmt.Println("Sending stop message")
_, err = client3.Write([]byte("stop"))
if err != nil {
fmt.Println(err)
}
}
You must take care to what you are doing.
avoid data races (done variable is read/write by two different routines without synchronization mechanism) https://go.dev/doc/articles/race_detector
dont make a new dialer everytime the program start sending messages to a new client. This will open a new local address and use it to send it to the client. The client will receive messages from another address, which it should normally ignore, because it did not initiated any exchange with that remote.
dont mixup client lifetime span with the program context lifetime span. In the code provided a client sending a stop message will trigger the cancel function of the whole program, it will stop all clients. Make a new context for each client, derived from the program context, cancel the related client context upon receiving a stop message.
UDP conns are shared by all clients, they must not be stopped from listening incoming packets because the program is serving a client. IE the call to generateMessageToUDP should be executed into another routine.
Following is a revised version accounting for those comments.
A var peers map[string]peer is added to match a remote address with a context. The type peer is defined as struct {stop func();since time.Time}. Upon receiving a start message, the peer is added to the map with a derived context, pctx, pcancel := context.WithCancel(ctx). The new client is then served in a different routine, go generateMessageToUDP(pctx, udpServer, addr), which is bond to the newly created context and the server socket. Upon receiving a stop message, the program performs a lookup peer, ok := peers[addr.String()], it then cancels the associated peer context peer.stop(); delete(peers, addr.String()) and forgets the peer.
package main
import (
"context"
"fmt"
"math/rand"
"net"
"time"
)
func generateMessageToUDP(ctx context.Context, conn *net.UDPConn, addr *net.UDPAddr) {
fmt.Println("Generating message to UDP client", addr)
go func() {
for i := 0; ; i++ {
RandomInt := rand.Intn(100)
d := []byte(fmt.Sprintf("%d", RandomInt))
conn.WriteTo(d, addr)
time.Sleep(time.Second * 1)
}
}()
<-ctx.Done()
fmt.Println("Stopping writing to UDP client", addr)
}
//var addr *net.UDPAddr
//var conn *net.UDPConn
func main() {
fmt.Println("Hi this is a UDP server")
udpServer, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 5010})
if err != nil {
fmt.Println("Error: ", err)
}
defer func(udpServer *net.UDPConn) {
err := udpServer.Close()
if err != nil {
fmt.Println("Error in closing the UDP Connection: ", err)
}
}(udpServer)
// create a buffer to read data into
type peer struct {
stop func()
since time.Time
}
peers := map[string]peer{}
buffer := make([]byte, 1024)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for {
// read the incoming connection into the buffer
n, addr, err := udpServer.ReadFromUDP(buffer)
if err != nil {
fmt.Println("Error: ", err)
}
fmt.Println("Received ", string(buffer[0:n]), " from ", addr)
if string(buffer[0:n]) == "stop" {
fmt.Println("Stopped listening")
peer, ok := peers[addr.String()]
if !ok {
continue
}
peer.stop()
delete(peers, addr.String())
continue
} else if string(buffer[0:n]) == "start" {
peer, ok := peers[addr.String()]
if ok {
continue
}
pctx, pcancel := context.WithCancel(ctx)
peer.stop = pcancel
peer.since = time.Now()
peers[addr.String()] = peer
// send a response back to the client
_, err = udpServer.WriteToUDP([]byte("Hi, I am a UDP server"), addr)
if err != nil {
fmt.Println("Error: ", err)
}
// start a routine to generate messages to the client
go generateMessageToUDP(pctx, udpServer, addr)
} else if string(buffer[0:n]) == "ping" {
peer, ok := peers[addr.String()]
if !ok {
continue
}
peer.since = time.Now()
peers[addr.String()] = peer
} else {
fmt.Println("Unknown command")
}
for addr, p := range peers {
if time.Since(p.since) > time.Minute {
fmt.Println("Peer timedout")
p.stop()
delete(peers, addr)
}
}
}
}
-- go.mod --
module play.ground
-- client.go --
package main
import (
"fmt"
"log"
"net"
"time"
)
func main() {
fmt.Println("Hello, I am a client")
// Create a new client
localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:5011")
client3, err := net.DialUDP("udp", localAddr, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 5010})
if err != nil {
fmt.Println(err)
return
}
defer client3.Close()
var n int
n, err = client3.Write([]byte("start"))
if err != nil {
fmt.Println(err)
return
}
log.Println(n)
now := time.Now()
b := make([]byte, 2048)
for time.Since(now) < time.Second*10 {
n, addr, err := client3.ReadFrom(b)
fmt.Println(n, addr, err)
if err != nil {
fmt.Println(err)
continue
}
if addr.String() == "127.0.0.1:5010" {
m := b[:n]
fmt.Println("message:", string(m))
}
}
fmt.Println("Sending stop message")
_, err = client3.Write([]byte("stop"))
if err != nil {
fmt.Println(err)
}
}
In
go func() {
for i := 0; ; i++ {
RandomInt := rand.Intn(100)
d := []byte(fmt.Sprintf("%d", RandomInt))
conn.WriteTo(d, addr)
time.Sleep(time.Second * 1)
}
}()
I left as an exercise to the reader the writing of the missing select on the context channel to figure out if the routine should exit.
Okay, I did a simple hack on the server and added a label Start before creating a context and when I cancel the context, I addded goto label. This means when the task get cancelled it will again create the context and start doings its job

UDP server issues with multi ports connections

I have a UDP server that expose two ports, 8080 for normal data and 8082 for controll (keep alive signal). When I start the server one new instance of Server is created. It have a list of clients: a client is represented by a struct that contains two types of UDPConn, one for control and one for data. What is happening is when I run my Client.go I connect to server in both ports but, the server creates two clients instead of only one. Downward are part of my Server.go and Client.go. What can I do to create only one user for both connections?
Server.go
func main() {
server := NewLobby()
addressCntrl, err := net.ResolveUDPAddr(CONN_TYPE, CONN_CNTRL_PORT)
if err != nil {
log.Fatalf("Error resolving controller address: %s", err)
}
listenerCntrl, err := net.ListenUDP(CONN_TYPE, addressCntrl)
defer listenerCntrl.Close()
log.Printf("Listening controll on port %s", CONN_CNTRL_PORT)
addressData, err := net.ResolveUDPAddr(CONN_TYPE, CONN_DATA_PORT)
if err != nil {
log.Fatalf("Error Resolving data address: %s", err)
}
listenerData, err := net.ListenUDP(CONN_TYPE, addressData)
defer listenerData.Close()
log.Printf("Listening data on port %s", CONN_DATA_PORT)
for {
buffer = make([]byte, 1024)
listenerData.ReadFromUDP(buffer)
c := NewClient(*listenerData, *listenerCntrl)
server.Join(c)
}
}
NewServer (server.go)
func NewServer() *Server {
server := &Server{
clients: make([]*Client, 0),
chatRooms: make(map[string]*ChatRoom),
incoming: make(chan *Message),
join: make(chan *Client),
leave: make(chan *Client),
delete: make(chan *ChatRoom),
}
server.Listen()
return server
}
NewClient (server.go)
func NewClient(connData net.UDPConn, connCntrl net.UDPConn) *Client {
writerData := bufio.NewWriter(&connData)
readerData := bufio.NewReader(&connData)
writerCntrl := bufio.NewWriter(&connCntrl)
readerCntrl := bufio.NewReader(&connCntrl)
client := &Client{
name: CLIENT_NAME,
chatRoom: nil,
incoming: make(chan *Message),
outgoing: make(chan string),
connData: connData,
connCntrl: connCntrl,
readerData: readerData,
writerData: writerData,
readerCntrl: readerCntrl,
writerCntrl: writerCntrl,
}
log.Printf("New client connected")
client.Listen()
return client
}
Client.go
func main() {
server := NewServer()
addressCntrl, err := net.ResolveUDPAddr(CONN_TYPE, CONN_CNTRL_PORT)
if err != nil {
log.Fatalf("Error resolving controller address: %s", err)
}
listenerCntrl, err := net.ListenUDP(CONN_TYPE, addressCntrl)
defer listenerCntrl.Close()
log.Printf("Listening controll on port %s", CONN_CNTRL_PORT)
addressData, err := net.ResolveUDPAddr(CONN_TYPE, CONN_DATA_PORT)
if err != nil {
log.Fatalf("Error Resolving data address: %s", err)
}
listenerData, err := net.ListenUDP(CONN_TYPE, addressData)
defer listenerData.Close()
log.Printf("Listening data on port %s", CONN_DATA_PORT)
for {
buffer = make([]byte, 1024)
listenerData.ReadFromUDP(buffer)
c := NewClient(*listenerData, *listenerCntrl)
server.Join(c)
}
}
One example of output is bellow
Server output
2021/05/06 20:03:52 Listening controll on port :8082
2021/05/06 20:03:52 Listening data on port :8080
2021/05/06 20:03:56 New client connected
2021/05/06 20:03:58 New client connected
Client output
Welcome to the server! Type "/help" to get a list of commands.
Welcome to the server! Type "/help" to get a list of commands.
If needed my functions Read() and Write() of client is bellow.
Read (client.go)
func Read(connData net.UDPConn, connCntrl net.UDPConn) {
//Controller
go func() {
readerCntrl := bufio.NewReader(&connCntrl)
for {
str, _ := readerCntrl.ReadString('\n')
if strings.Compare(str, "timeout") == 0 {
os.Exit(1)
}
}
}()
//Data
reader := bufio.NewReader(&connData)
for {
str, err := reader.ReadString('\n')
if err != nil {
fmt.Printf(MSG_DISCONNECT)
wg.Done()
return
}
fmt.Print(str)
}
}
Write (client.go)
func Write(connData net.UDPConn, connCntrl net.UDPConn) {
//Controller
go func() {
writerCntrl := bufio.NewWriter(&connCntrl)
for range time.Tick(10 * time.Second) {
writerCntrl.WriteString("ka")
}
}()
//Data
reader := bufio.NewReader(os.Stdin)
writerData := bufio.NewWriter(&connData)
for {
input, err := reader.ReadString('\n')
if err != nil {
log.Fatalf("Error reading input: %s", err)
}
_, err = writerData.WriteString(input)
if err != nil {
log.Fatalf("Error writing input to server: %s", err)
}
err = writerData.Flush()
if err != nil {
log.Fatalf("Error flushing the input: %s", err)
wg.Done()
}
}
}

Closing a redis subscription and ending the go routine when websocket connection closes

I'm pushing events from a redis subscription to a client who is connected via websocket. I'm having trouble unsubscribing and exiting the redis go routine when the client disconnects the websocket.
Inspired by this post, here's what I have thus far. I'm able to receive subscription events and send messages to the client via websocket, but when the client closes the websocket and the defer close(done) code fires, my case b, ok := <-done: doesn't fire. It seems to be overloaded by the default case???
package api
import (
...
"github.com/garyburd/redigo/redis"
"github.com/gorilla/websocket"
)
func wsHandler(w http.ResponseWriter, r *http.Request) {
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
HandleError(w, err)
return
}
defer conn.Close()
done := make(chan bool)
defer close(done)
for {
var req WSRequest
err := conn.ReadJSON(&req)
if err != nil {
HandleWSError(conn, err)
return
}
defer conn.Close()
go func(done chan bool, req *WSRequest, conn *websocket.Conn) {
rc := redisPool.Get()
defer rc.Close()
psc := redis.PubSubConn{Conn: rc}
if err := psc.PSubscribe(req.chanName); err != nil {
HandleWSError(conn, err)
return
}
defer psc.PUnsubscribe()
for {
select {
case b, ok := <-done:
if !ok || b == true {
return
}
default:
switch v := psc.Receive().(type) {
case redis.PMessage:
err := handler(conn, req, v)
if err != nil {
HandleWSError(conn, err)
}
case redis.Subscription:
log.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
log.Printf("error in redis subscription; err:\n%v\n", v)
HandleWSError(conn, v)
default:
// do nothing...
log.Printf("unknown redis subscription event type; %s\n", reflect.TypeOf(v))
}
}
}
}(done, &req, conn)
}
}
Make these changes to break out of the read loop when done serving the websocket connection:
Maintain a slice of the Redis connections created for this websocket connection.
Unsubscribe all connections when done.
Modify the read loop to return when the subscription count is zero.
Here's the code:
func wsHandler(w http.ResponseWriter, r *http.Request) {
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
HandleError(w, err)
return
}
defer conn.Close()
// Keep slice of all connections. Unsubscribe all connections on exit.
var pscs []redis.PubSubConn
defer func() {
for _, psc := range rcs {
psc.Unsubscribe() // unsubscribe with no args unsubs all channels
}
}()
for {
var req WSRequest
err := conn.ReadJSON(&req)
if err != nil {
HandleWSError(conn, err)
return
}
rc := redisPool.Get()
psc := redis.PubSubConn{Conn: rc}
pscs = append(pscs, psc)
if err := psc.PSubscribe(req.chanName); err != nil {
HandleWSError(conn, err)
return
}
go func(req *WSRequest, conn *websocket.Conn) {
defer rc.Close()
for {
switch v := psc.Receive().(type) {
case redis.PMessage:
err := handler(conn, req, v)
if err != nil {
HandleWSError(conn, err)
}
case redis.Subscription:
log.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
if v.Count == 0 {
return
}
case error:
log.Printf("error in redis subscription; err:\n%v\n", v)
HandleWSError(conn, v)
default:
// do nothing...
log.Printf("unknown redis subscription event type; %s\n", reflect.TypeOf(v))
}
}
}(&req, conn)
}
}
The code in the question and this answer dial multiple Redis connections for each websocket client. A more typical and scalable approach is to share a single Redis pubsub connection across multiple clients. The typical approach may be appropriate for your application given the high-level description, but I am still unsure of what you are trying to do given the code in the question.

Resources