Can't run the go routines while building chat app with golang - go

I'm trying to build a chat app with golang. However, I've encountered a problem that if I wrap the code of the for loop into a Sender() function and using goroutines, the client will shut down immediately. But if I put it in the main function, it can run correctly.
Here is the client code.
package main
import (
"fmt"
"net"
"os"
"os/signal"
"syscall"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
var connection net.Conn
var err error
func main() {
SetupCloseHandler()
//establish connection
connection, err = net.Dial(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
panic(err)
}
//receive some data
go Receiver()
// go Sender()
for {
var input string
fmt.Print("Enter text: ")
fmt.Scan(&input)
_, err = connection.Write([]byte(input))
if err != nil {
panic(err)
}
}
}
// func Sender() {
// for {
// var input string
// fmt.Print("Enter text: ")
// fmt.Scan(&input)
// _, err = connection.Write([]byte(input))
// if err != nil {
// panic(err)
// }
// }
// }
func Receiver() {
for {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
panic(err)
}
fmt.Println("Received: ", string(buffer[:mLen]))
}
}
func SetupCloseHandler() {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
fmt.Println("\r- Ctrl+C pressed in Terminal")
os.Exit(0)
}()
}
Here is the server code.
package main
import (
"fmt"
"net"
"os"
"github.com/google/uuid"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "9988"
SERVER_TYPE = "tcp"
)
var clientMap = make(map[string]net.Conn)
func main() {
fmt.Println("Server Running...")
server, err := net.Listen(SERVER_TYPE, SERVER_HOST+":"+SERVER_PORT)
if err != nil {
fmt.Println("Error listening:", err.Error())
os.Exit(1)
}
defer server.Close()
fmt.Println("Listening on " + SERVER_HOST + ":" + SERVER_PORT)
fmt.Println("Waiting for client...")
for {
connection, err := server.Accept()
id := uuid.New().String()
clientMap[id] = connection
if err != nil {
fmt.Println("Error accepting: ", err.Error())
os.Exit(1)
}
fmt.Printf("client %s connected\n", id)
go processClient(connection, id)
}
}
func processClient(connection net.Conn, id string) {
defer connection.Close()
for {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
if err != nil {
fmt.Println("Error reading:", err.Error())
delete(clientMap, id)
connection.Close()
return
}
fmt.Println("Received: ", string(buffer[:mLen]))
for k, c := range clientMap {
if k != id {
c.Write(buffer[:mLen])
}
}
}
}

Related

Gorilla websocket is not closing

I want to open websocket server whenever i want to and close but server does not close after writing "exit" when i go back to my main() and i try to start again it fails saying "httpHandleFunc multiple registration '/' " how do i close websocket server permanently and go back to main and start server again like switch on/off.
Another issue i am facing is client side errors with Bad Handshake when using gorilla mux as a handler.
#Server-Side Code
https://go.dev/play/p/n_I4xzOomWz
package main
import (
"bufio"
"flag"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
)
const (
// Time allowed to write a message to the peer.
writeWait = 10 * time.Second
// Maximum message size allowed from peer.
maxMessageSize = 8192
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Time to wait before force close on connection.
closeGracePeriod = 2 * time.Second
)
var (
server http.Server
addr = "0.0.0.0:443"
upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
)
func ping(ws *websocket.Conn, done chan struct{}) {
ticker := time.NewTicker(pingPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
log.Println("ping:", err)
}
case <-done:
return
}
}
}
// write message
func pumpStdout(ws *websocket.Conn, done chan struct{}) {
defer func() {
}()
for {
text := ReadInput("User220 > ")
if text == "exit" {
break
}
ws.SetWriteDeadline(time.Now().Add(writeWait))
if err := ws.WriteMessage(websocket.TextMessage, []byte(text)); err != nil {
ws.Close()
log.Errorln(err)
break
}
}
close(done)
ws.SetWriteDeadline(time.Now().Add(writeWait))
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
time.Sleep(closeGracePeriod)
ws.Close()
}
// receive message
func pumpStdin(ws *websocket.Conn, done chan struct{}) {
defer ws.Close()
ws.SetReadLimit(maxMessageSize)
ws.SetReadDeadline(time.Now().Add(pongWait))
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-done:
break
case <-ticker.C:
_, message, err := ws.ReadMessage()
if err != nil {
ws.CloseHandler()
err := ws.UnderlyingConn().Close()
fmt.Println(err)
main()
}
if len(message) > 0 {
fmt.Printf("\r\n%sUser220 > ", message)
}
}
}
}
func WebsocketHandle(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatalln(err)
}
defer c.Close()
stdoutDone := make(chan struct{})
go pumpStdout(c, stdoutDone)
go ping(c, stdoutDone)
pumpStdin(c, stdoutDone)
}
func start_socket() {
flag.Parse()
log.Printf("listen at %s", addr)
// ----------------------
mux := mux.NewRouter()
mux.HandleFunc("/", WebsocketHandle)
//-----------------------
http.HandleFunc("/", WebsocketHandle)
server = http.Server{Addr: addr}
log.Fatalln(server.ListenAndServe())
}
func stop_server() {
err := server.Close()
if err != nil {
log.Fatalln(err)
}
}
func ReadInput(promt string) string {
fmt.Printf("\n%s", promt)
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
return strings.TrimSpace(input)
}
func main() {
for {
read := ReadInput("Main : ")
switch read {
case "start":
start_socket()
case "stop":
stop_server()
default:
fmt.Println("Not Sure")
}
}
}
Client side i am just polling and writing back the same message right now.
#Client-Side Code
https://go.dev/play/p/y4nUkvMFYec
package main
import (
"fmt"
"net/url"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
)
var addr = "localhost:443"
func main() {
dial := websocket.Dialer{}
u := url.URL{Scheme: "ws", Host: addr, Path: "/"}
log.Printf("connecting to %s", u.String())
c, _, err := dial.Dial(u.String(), nil)
if err != nil {
log.Fatalln(err)
}
defer c.Close()
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Fatalln(err)
}
fmt.Println("Recevied : " + string(message))
err = c.WriteMessage(websocket.TextMessage, message)
if err != nil {
log.Fatalln(err)
}
}
}

High CPU usage when reading /dev/gpiomem

Is there something in my Golang code that would be causing high cpu usage?
The code should read a button on the GPIO pin on a Raspberry Pi 4 (4GB) and when pressed send an REST post message to another program.
I tried using more sleep and no sleep, ive tried changing the code to use a falling edge detection method on the pin. Nothing has changed the high CPU usage.
import (
"fmt"
"github.com/stianeikeland/go-rpio"
"os"
"io/ioutil"
"net/http"
"strings"
"time"
)
func main() {
fmt.Println("Starting Stream Control 0.1")
pinOpenError := rpio.Open()
if pinOpenError != nil {
fmt.Println("Pin Open Error: ", pinOpenError.Error())
os.Exit(1)
}
pin := rpio.Pin(18)
pin.Input()
pin.PullUp()
var PID string
start := time.Now()
StreamRunning := false;
fmt.Println("Reading and Controlling")
for {
res := pin.Read()
if res == 0 {
if StreamRunning == false && time.Since(start).Seconds() > 10{
var StartStreamError error
PID, StartStreamError = StartStream()
if StartStreamError != nil {
fmt.Println("Start Stream Error: ", StartStreamError.Error())
}
fmt.Println("PID = " + PID)
start = time.Now()
StreamRunning = true;
} else {
if time.Since(start).Seconds() > 10 {
StopStream(PID)
StreamRunning = false;
start = time.Now()
}
}
}
time.Sleep(100)
}
}
func postHTTP(url string, requestString string) ([]byte, error){
var blankBody []byte
payload := strings.NewReader(requestString)
client := &http.Client{}
req, clientError := http.NewRequest("POST", url, payload)
if clientError != nil {
return blankBody, clientError
}
req.Header.Add("Content-Type", "application/json")
res, requestError := client.Do(req)
if requestError != nil {
return blankBody, requestError
}
defer res.Body.Close()
returnBody, returnError := ioutil.ReadAll(res.Body)
return returnBody, returnError
}
func StartStream()(string, error) {
fmt.Println("Start Stream")
response, StreamOnError := postHTTP("http://localhost:3000/startStream","{\"devicePath\":\"/dev/video0\",\"streamName\":\"LumiPi-003\"}")
if StreamOnError != nil {
fmt.Println("Stream On Error: ", StreamOnError.Error())
return "0", StreamOnError
}
splitstrings := strings.Split(string(response),",")
for i := range splitstrings {
if strings.Contains(splitstrings[i], "\"pid\":") {
linesplit := strings.Split(splitstrings[i],":")
return linesplit[1], nil
}
}
return "0", nil
}
func StopStream(PID string)(error) {
fmt.Println("Stop Stream")
command := "{\"pid\":" + PID + "}"
_, StreamOnError := postHTTP("http://localhost:3000/stopStream",command)
if StreamOnError != nil {
fmt.Println("Stream Off Error: ", StreamOnError.Error())
return StreamOnError
}
return nil
}
Picture below shows the affect time.sleep has on the program.
The time.Sleep() function was causing the PI to crash when rpio.Open() was still open.
Opening, reading the pins and closing allowed time.Sleep() to be used with a much larger delay which in turn dropped the cpu usage because its not constantly looping.
import (
"fmt"
"github.com/stianeikeland/go-rpio"
"os"
"io/ioutil"
"net/http"
"strings"
"time"
)
func main() {
fmt.Println("Starting Stream Control 0.1")
var PID string
start := time.Now()
StreamRunning := false;
fmt.Println("Reading and Controlling")
for {
pinOpenError := rpio.Open()
if pinOpenError != nil {
fmt.Println("Pin Open Error: ", pinOpenError.Error())
os.Exit(1)
}
pin := rpio.Pin(18)
pin.Input()
pin.PullUp()
res := pin.Read()
rpio.Close()
if res == 0 {
if StreamRunning == false && time.Since(start).Seconds() > 10{
var StartStreamError error
PID, StartStreamError = StartStream()
if StartStreamError != nil {
fmt.Println("Start Stream Error: ", StartStreamError.Error())
}
fmt.Println("PID = " + PID)
start = time.Now()
StreamRunning = true;
} else {
if time.Since(start).Seconds() > 10 {
StopStream(PID)
StreamRunning = false;
start = time.Now()
}
}
}
time.Sleep(100*time.Millisecond)
}
}
func postHTTP(url string, requestString string) ([]byte, error){
var blankBody []byte
payload := strings.NewReader(requestString)
client := &http.Client{}
req, clientError := http.NewRequest("POST", url, payload)
if clientError != nil {
return blankBody, clientError
}
req.Header.Add("Content-Type", "application/json")
res, requestError := client.Do(req)
if requestError != nil {
return blankBody, requestError
}
defer res.Body.Close()
returnBody, returnError := ioutil.ReadAll(res.Body)
return returnBody, returnError
}
func StartStream()(string, error) {
fmt.Println("Start Stream")
response, StreamOnError := postHTTP("http://localhost:3000/startStream","{\"devicePath\":\"/dev/video0\",\"streamName\":\"LumiPi-003\"}")
if StreamOnError != nil {
fmt.Println("Stream On Error: ", StreamOnError.Error())
return "0", StreamOnError
}
splitstrings := strings.Split(string(response),",")
for i := range splitstrings {
if strings.Contains(splitstrings[i], "\"pid\":") {
linesplit := strings.Split(splitstrings[i],":")
return linesplit[1], nil
}
}
return "0", nil
}
func StopStream(PID string)(error) {
fmt.Println("Stop Stream")
command := "{\"pid\":" + PID + "}"
_, StreamOnError := postHTTP("http://localhost:3000/stopStream",command)
if StreamOnError != nil {
fmt.Println("Stream Off Error: ", StreamOnError.Error())
return StreamOnError
}
return nil
}

Go udp connection dial once, and send many

please consider this code below, it is a simplified version of a service. I launch a no. of goroutines as needed during its lifetime, and as they go about doing things, they need to send udp messages to a set destination.
package main
import (
"fmt"
"log"
"net"
"time"
)
const (
udp_dest = "192.168.1.200:514"
)
func main() {
fmt.Println("Hello")
message := "this is a test"
log_message(&message)
go worker(1)
go worker(2)
go worker(3)
go worker(4)
time.Sleep(3009 * time.Second)
}
func log_message(message *string) {
RemoteAddr, err := net.ResolveUDPAddr("udp", udp_dest)
if err != nil {
//fmt.Println("Err, net.ResolveUDPAddr", err)
return
}
conn, err := net.DialUDP("udp", nil, RemoteAddr)
if err != nil {
return
}
udp_message := fmt.Sprintf("<30> %s", *message)
Bytes, _ := conn.Write([]byte(udp_message))
log.Printf("Sent %d Bytes to %s\n", Bytes, udp_dest)
}
func worker(tag int) {
i := 0
for {
worker_message := fmt.Sprintf("Some message from worker%d, loop: %d", tag, i)
log_message(&worker_message)
// do some work..
time.Sleep(300 * time.Second)
i += 1
}
}
In my log_message, everytime it gets called we're calling net.DialUDP which I feel is wasteful. I tried experimenting with global variables &net.UDPConn et al, but could not get to work.
Please show how to achieve/optimize this? There's only one UDP destination, and I'd like the daemon to Dial once at its start, and then just Write as needed.
Thanks!
here's what I got so far:
package main
import (
"fmt"
"log"
"net"
"time"
)
const (
udp_dest = "192.168.1.200:514"
)
var (
myconn *net.UDPConn
)
func main() {
fmt.Println("Hello")
message := "this is a test"
log_message(&message)
go worker(1)
go worker(2)
go worker(3)
go worker(4)
time.Sleep(3009 * time.Second)
}
func log_message(message *string) {
if myconn == nil {
fmt.Println("Setting up myconn!")
RemoteAddr, err := net.ResolveUDPAddr("udp", udp_dest)
if err != nil {
//fmt.Println("Err, net.ResolveUDPAddr", err)
return
}
myconn, err = net.DialUDP("udp", nil, RemoteAddr)
if err != nil {
return
}
}
udp_message := fmt.Sprintf("<30> %s", *message)
Bytes, _ := myconn.Write([]byte(udp_message))
log.Printf("Sent %d Bytes to %s\n", Bytes, udp_dest)
}
func worker(tag int) {
i := 0
for {
worker_message := fmt.Sprintf("Some message from worker%d, loop: %d", tag, i)
log_message(&worker_message)
// do some work..
time.Sleep(10 * time.Second)
i += 1
}
}
You are almost there. Move the setup code to a function and call it before starting the goroutines.
func main() {
if err := setupLog(); err != nil {
log.Fatal(err)
}
fmt.Println("Hello")
... same as before
}
func setupLog() error {
fmt.Println("Setting up myconn!")
RemoteAddr, err := net.ResolveUDPAddr("udp", udp_dest)
if err != nil {
return err
}
myconn, err = net.DialUDP("udp", nil, RemoteAddr)
return err
}
func log_message(message *string) {
udp_message := fmt.Sprintf("<30> %s", *message)
Bytes, _ := myconn.Write([]byte(udp_message))
log.Printf("Sent %d Bytes to %s\n", Bytes, udp_dest)
}
The code in the question does not work because there's a data race on myconn.

Golang: Recursive function for reconnecting a TCP client... Bad idea?

I have this working TCP client code. When it fails to Write or Read on the TCP connection, it creates a new connection with the recursive function tcpReconnect().
Is this safe or will it fill up the RAM? It is possible that it may be trying to reconnect over several days (weekend or holidays). This is code is part of a Driver that monitors the state of an industrial Machine.
Maybe there is a better solution for this problem. I was not able to find one.
PS: I don't like polling
package main
import (
"fmt"
"net"
"time"
)
var pollTime = 1000 //ms
var host = "127.0.0.1"
var port = "11000"
func main() {
finished := make(chan bool)
go Driver()
<-finished
}
func tcpReconnect() net.Conn {
newConn, err := net.Dial("tcp", host+":"+port)
if err != nil {
fmt.Println("Failed to reconnect:", err.Error())
time.Sleep(time.Millisecond * time.Duration(2000))
newConn = tcpReconnect()
}
return newConn
}
func Driver() {
var conn net.Conn
conn, err := net.Dial("tcp", host+":"+port)
if err != nil {
fmt.Println("Failed to initialize Connection, trying to reconnect:", err.Error())
conn = tcpReconnect()
}
for {
_, err = conn.Write([]byte("11|5546|STATUS" + "\r\n"))
if err != nil {
println("Write to server failed:", err.Error())
println("Trying reset the connection...")
conn = tcpReconnect()
}
var replyBuffer = make([]byte, 256)
_, err = conn.Read(replyBuffer)
if err != nil {
println("Read from server failed:", err.Error())
println("Trying reset the connection...")
conn = tcpReConnect()
}
var reply string
for i, val := range replyBuffer {
if val == 13 { //13 is CR and marks the end of the message
reply = string(replyBuffer[:i])
break
}
}
fmt.Printf("reply from server=%s\n", reply)
time.Sleep(time.Millisecond * time.Duration(pollTime))
}
}
This is what I came up with. Credits go to #tkausl and #ThunderCat
func Driver() {
for {
conn, err := net.Dial("tcp", host+":"+port)
if err != nil {
fmt.Println("Failed to connect:", err.Error())
fmt.Println("Trying reset the connection...")
time.Sleep(time.Millisecond * time.Duration(2000))
} else {
for {
_, err = conn.Write([]byte("11|5546|STATUS" + "\r\n"))
if err != nil {
fmt.Println("Write to server failed:", err.Error())
fmt.Println("Trying reset the connection...")
break
}
var replyBuffer = make([]byte, 256)
_, err = conn.Read(replyBuffer)
if err != nil {
fmt.Println("Read from server failed:", err.Error())
fmt.Println("Trying reset the connection...")
break
}
var reply string
for i, val := range replyBuffer {
if val == 13 { //13 is CR and marks the end of the message
reply = string(replyBuffer[:i])
break
}
}
fmt.Printf("reply from server=_%s_\n", reply)
time.Sleep(time.Millisecond * time.Duration(pollTime))
}
}
}
}

Golang Gorilla Websocket stops receiving information at 120 seconds

I'm currently trying to connect to the CEX.IO bitcoin exchange's websocket, but have been having issues not only with CEX.IO but with others too. All of my connections drop around the 120-second mark which makes me think there is some TTL problem going on. The Process() goroutine in the main package ends up just hanging and waiting for data from the readLoop which just stops receiving data. I've included some read-only API keys in the code so you can test if you'd like.
package main
import (
"fmt"
"bitbucket.org/tradedefender/cryptocurrency/exchange-connector/cexio"
"github.com/shopspring/decimal"
"encoding/json"
"time"
)
type OrderBook struct {
Asks []Ask
Bids []Bid
}
type Ask struct {
Rate decimal.Decimal
Amount decimal.Decimal
}
type Bid struct {
Rate decimal.Decimal
Amount decimal.Decimal
}
func main() {
cexioConn := new(cexio.Connection)
err := cexioConn.Connect()
if err != nil {
fmt.Errorf("error: %s", err.Error())
}
err = cexioConn.Authenticate("TLwYkktLf7Im6nqSKt6UO1IrU", "9ImOJcR7Qj3LMIyPCzky0D7WE")
if err != nil {
fmt.Errorf("error: %s", err.Error())
}
readChannel := make(chan cexio.IntraAppMessage, 25)
go cexioConn.ReadLoop(readChannel)
processor := Processor{
WatchPairs: [][2]string{
[2]string{
"BTC", "USD",
},
},
conn: cexioConn,
}
go processor.Process(readChannel)
// LOL
for {
continue
}
}
type Processor struct {
WatchPairs [][2]string
conn *cexio.Connection
}
func (p *Processor) Process(ch <-chan cexio.IntraAppMessage) {
p.conn.SubscribeToOrderBook(p.WatchPairs[0])
pingTimer := time.Now().Unix()
for {
fmt.Printf("(%v)\n", time.Now().Unix())
if (time.Now().Unix() - pingTimer) >= 10 {
fmt.Println("sending ping")
p.conn.SendPing()
pingTimer = time.Now().Unix()
}
readMsg := <- ch
output, _ := json.Marshal(readMsg.SocketMessage)
fmt.Println(string(output))
if readMsg.SocketMessage.Event == "ping" {
fmt.Println("sending pong")
p.conn.SendPong()
pingTimer = time.Now().Unix()
}
}
}
Below is the connector to the cexio websocket. Here is a link to their API: https://cex.io/websocket-api
package cexio
import (
"github.com/gorilla/websocket"
//"github.com/shopspring/decimal"
"github.com/satori/go.uuid"
"encoding/hex"
"encoding/json"
"crypto/hmac"
"crypto/sha256"
"bytes"
"strconv"
"time"
"fmt"
)
const Url = "wss://ws.cex.io/ws/"
type Connection struct {
conn *websocket.Conn
}
type IntraAppMessage struct {
SocketMessage GenericMessage
ProgramMessage ProgramMessage
}
type GenericMessage struct {
Event string `json:"e"`
Data interface{} `json:"data"`
Auth AuthData `json:"auth,omitempty"`
Ok string `json:"ok,omitempty"`
Oid string `json:"oid,omitempty"`
Time int64 `json:"time,omitempty"`
}
type ProgramMessage struct {
Error string
}
type AuthData struct {
Key string `json:"key"`
Signature string `json:"signature"`
Timestamp int64 `json:"timestamp"`
}
type OrderBookSubscribeData struct {
Pair [2]string `json:"pair"`
Subscribe bool `json:"subscribe"`
Depth int `json:"depth"`
}
func (c *Connection) SendPong() error {
pongMsg := GenericMessage{
Event: "pong",
}
err := c.conn.WriteJSON(pongMsg)
if err != nil {
return nil
}
deadline := time.Now().Add(15*time.Second)
err = c.conn.WriteControl(websocket.PongMessage, nil, deadline)
if err != nil {
return err
}
return nil
}
func (c *Connection) SendPing() error {
pingMsg := GenericMessage{
Event: "get-balance",
Oid: uuid.NewV4().String(),
}
err := c.conn.WriteJSON(pingMsg)
if err != nil {
return err
}
deadline := time.Now().Add(15*time.Second)
err = c.conn.WriteControl(websocket.PingMessage, nil, deadline)
if err != nil {
return err
}
return nil
}
func (c *Connection) Connect() error {
dialer := *websocket.DefaultDialer
wsConn, _, err := dialer.Dial(Url, nil)
if err != nil {
return err
}
c.conn = wsConn
//c.conn.SetPingHandler(c.HandlePing)
for {
_, msgBytes, err := c.conn.ReadMessage()
if err != nil {
c.Disconnect()
return err
}
fmt.Println(string(msgBytes))
var m GenericMessage
err = json.Unmarshal(msgBytes, &m)
if err != nil {
c.Disconnect()
return err
}
if m.Event != "connected" {
c.Disconnect()
return err
} else {
break
}
}
return nil
}
func (c *Connection) Disconnect() error {
return c.conn.Close()
}
func (c *Connection) ReadLoop(ch chan<- IntraAppMessage) {
for {
fmt.Println("starting new read")
_, msgBytes, err := c.conn.ReadMessage()
if err != nil {
ch <- IntraAppMessage{
ProgramMessage: ProgramMessage{
Error: err.Error(),
},
}
continue
}
var m GenericMessage
err = json.Unmarshal(msgBytes, &m)
if err != nil {
ch <- IntraAppMessage{
ProgramMessage: ProgramMessage{
Error: err.Error(),
},
}
continue
}
ch <- IntraAppMessage{
SocketMessage: m,
}
}
}
func CreateSignature(timestamp int64, key, secret string) string {
secretBytes := []byte(secret)
h := hmac.New(sha256.New, secretBytes)
var buffer bytes.Buffer
buffer.WriteString(strconv.FormatInt(timestamp, 10))
buffer.WriteString(key)
h.Write(buffer.Bytes())
return hex.EncodeToString(h.Sum(nil))
}
func (c *Connection) Authenticate(key, secret string) error {
timestamp := time.Now().Unix()
signature := CreateSignature(timestamp, key, secret)
var authMsg GenericMessage
authMsg.Event = "auth"
authMsg.Auth = AuthData{
Key: key,
Signature: signature,
Timestamp: timestamp,
}
err := c.conn.WriteJSON(authMsg)
if err != nil {
return err
}
for {
_, msgBytes, err := c.conn.ReadMessage()
if err != nil {
c.Disconnect()
return err
}
fmt.Println(string(msgBytes))
var m GenericMessage
err = json.Unmarshal(msgBytes, &m)
if err != nil {
c.Disconnect()
return err
}
if m.Event != "auth" && m.Ok != "ok" {
c.Disconnect()
return err
} else {
break
}
}
return nil
}
func (c *Connection) SubscribeToOrderBook(pair [2]string) error {
sendMsg := GenericMessage{
Event: "order-book-subscribe",
Data: OrderBookSubscribeData{
Pair: pair,
Subscribe: true,
Depth: 0,
},
Oid: uuid.NewV4().String(),
}
err := c.conn.WriteJSON(sendMsg)
if err != nil {
return err
}
return nil
}
func (c *Connection) GetBalance() error {
sendMsg := GenericMessage{
Event: "get-balance",
Oid: uuid.NewV4().String(),
}
err := c.conn.WriteJSON(sendMsg)
if err != nil {
return err
}
return nil
}
Solution was to remove the
for {
continue
}
at the end of the main function

Resources