Problem: structure's field is not replaced with fresh value if it is zero after an rpc call.
Here is minimal code sample:
package main
import (
"fmt"
"log"
"net"
"net/rpc"
)
type RpcObject int
type Reply struct {
A int
}
func (*RpcObject) GetSlice(req int, reply *[]Reply) error {
*reply = []Reply{{0}, {158}}
return nil
}
func Serve() {
addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:12345")
if err != nil {
log.Fatal(err)
}
inbound, err := net.ListenTCP("tcp", addr)
if err != nil {
log.Fatal(err)
}
handler := new(RpcObject)
rpc.Register(handler)
rpc.Accept(inbound)
}
func main() {
go Serve()
var err error
var client *rpc.Client
for client, err = rpc.Dial("tcp", "localhost:12345"); err != nil; client, err = rpc.Dial("tcp", "localhost:12345") {
fmt.Println("connecting...")
}
reply := []Reply{{225}, {9946}}
client.Call("RpcObject.GetSlice", 0, &reply)
fmt.Println(reply)
}
Output:
[{225} {158}]
I assume it is a problem with gob format, however I am not sure. Does someone know how to fix this?
The application produced the expected results. The gob encoder does not transmit the first element in the reply slice because the element is the zero value. The decoder does not set the first element in main's reply slice because a value was not received for that element.
Decode to a zero value if you do not want to set defaults for values missing from the reply:
...
var reply []Reply
client.Call("RpcObject.GetSlice", 0, &reply)
...
Related
I want to get binance aggtrade data from websocket and this is their documentation (https://binance-docs.github.io/apidocs/spot/en/#live-subscribing-unsubscribing-to-streams). In this documentation there is a "m" parameter which describes trade is buy or sell. But this parameter is always true when I am getting data.
I am coding in Go and here is my code for getting data with websocket.
package main
import (
"encoding/json"
"fmt"
"github.com/gorilla/websocket"
)
type AggTrade struct {
S string `json:"s"`
P string `json:"p"`
M bool `json:"m"`
E int64 `json:"E"`
}
func main() {
conn, res, err := websocket.DefaultDialer.Dial("wss://stream.binance.com:9443/ws/btcusdt#aggTrade", nil)
fmt.Println(conn)
fmt.Println(res)
fmt.Println(err)
var aggTrade AggTrade
for {
_, message, readErr := conn.ReadMessage()
if readErr != nil {
fmt.Println(readErr)
return
}
json.Unmarshal(message, &aggTrade)
fmt.Println(aggTrade)
}
}
Is there a change that I am not aware of? This "m" parameter is always true
I am trying to stream JSON text from a websocket. However after an initial read I noticed that the stream seems to break/disconnect. This is from a Pleroma server (think: Mastodon). I am using the default Golang websocket library.
package main
import (
"bufio"
"fmt"
"log"
"golang.org/x/net/websocket"
)
func main() {
origin := "https://poa.st/"
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
s := bufio.NewScanner(ws)
for s.Scan() {
line := s.Text()
fmt.Println(line)
}
}
After the initial JSON text response, the for-loop breaks. I would expect it to send a new message every few seconds.
What might be causing this? I am willing to switch to the Gorilla websocket library if I can use it with bufio.
Thanks!
Although x/net/websocket connection has a Read method with the same signature as the Read method in io.Reader, the connection does not work like an io.Reader. The connection will not work as you expect when wrapped with a bufio.Scanner.
The poa.st endpoint sends a stream of messages where each message is a JSON document. Use the following code to read the messages using the Gorilla package:
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
defer ws.Close()
for {
_, p, err := ws.ReadMessage()
if err != nil {
log.Fatal(err)
}
// p is a []byte containing the JSON document.
fmt.Printf("%s\n", p)
}
The Gorilla package has a helper method for decoding JSON messages. Here's an example of how to use that method.
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
defer ws.Close()
for {
// The JSON documents are objects containing two fields,
// the event type and the payload. The payload is a JSON
// document itself.
var e struct {
Event string
Payload string
}
err := ws.ReadJSON(&e)
if err != nil {
log.Fatal(err)
}
// TODO: decode e.Payload based on e.Event
}
I am trying to create a tcp server/client connection in golang. I am sending a message length so the server will accept multiple messages until the message length is reached. When I try to read the message header to determine the message length, there is no output. The code seems to gets stuck in the server's tcpreader function's for loop when "conn.read(b)" is called. What might be causing this failure?
My server code:
package main
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"log"
"net"
"strings"
)
type hdr struct {
magicNum uint64
msgLen uint32
msgType uint16
padding uint16
}
func tcpReader(conn net.Conn) {
foundLength := false
var hdr struct {
magicNum uint64
msgLen uint32
msgType uint16
padding uint16
}
hdrSize := 16
fmt.Println("tcpReader() header size %v", hdrSize)
//localBuffer := new(bytes.Buffer)
defer conn.Close()
for {
if !foundLength {
fmt.Println("tcpReader() Getting the lenght of the message")
var b = make([]byte, hdrSize)
// where the code seems to get stuck
read, err := conn.Read(b)
if err != nil {
fmt.Println("Failed to read header size %v", hdrSize)
fmt.Println(err)
continue
}
fmt.Println("tcpReader() read header bytes %v %v x", read, string(b))
if read != hdrSize { //add logic check magic number etc
fmt.Println("invalid header")
continue
}
err = binary.Read(bytes.NewReader(b[:16]), binary.BigEndian, &hdr)
if err != nil {
log.Fatal(err)
}
fmt.Println("tcpReader() read header bytes %v", hdr.msgLen)
foundLength = true
// messageLength = int(binary.BigEndian.Uint64(b))
} else {
var message = make([]byte, hdr.msgLen)
read, err := conn.Read(message)
if err != nil {
fmt.Println(err)
continue
}
fmt.Println("Received bytes: %v", read)
if read != int(hdr.msgLen) {
//Add logic to append the read message and update the readSofarBytes etc
fmt.Println("invalid data")
continue
}
fmt.Println("Received:", string(message))
foundLength = false
}
}
}
func main() {
fmt.Println("Launching server...")
fmt.Println("Listen on port")
/* addr, err := net.ResolveTCPAddr("ip4", "127.0.0.1:8081")
if err != nil {
panic(err)
}*/
ln, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
fmt.Println("Accept connection on port")
for {
conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
fmt.Println("Calling handleConnection")
//go handleConnection(conn)
go tcpReader(conn)
}
}
My client code:
package main
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"net"
)
type hdr struct {
magicNum uint64
msgLen int
msgType uint16
padding uint16
msg string
}
func main() {
var bin_buf bytes.Buffer
msg := hdr{magicNum: 123456789,
msgLen: 60,
msgType: 2,
padding: 0,
msg: "Hello world this is tcp message with big header"}
msg.msgLen = int(len(msg.msg))
fmt.Printf("message length %v\n", msg.msgLen)
binary.Write(&bin_buf, binary.BigEndian, msg)
addr, _ := net.ResolveTCPAddr("tcp", ":8081")
conn, err := net.DialTCP("tcp", nil, addr)
if err != nil {
panic(err.Error())
}
// conn.Write(bin_buf.Bytes())
conn.Write(bin_buf.Bytes())
//conn.Write([]byte("Hello from Clinet\n"))
message, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Print(message)
conn.Close()
}
It is because you are not writing anything from the client side at all. You should never ignore error handling in golang.Change these lines and you'll see where the error is:
err := binary.Write(&bin_buf, binary.BigEndian, msg)
if err != nil {
panic(err.Error())
}
Client is writing 0 bytes to connection because there aren't any
panic: binary.Write: invalid type main.hdr
To complete Keser's answer, you can't use a struct whose fields have types without a fixed size. In this case your hdr struct has two fields that can't be written using binary.Write, the message field with type string and msgLen without specifying the int size. You will have to manually write the string message as it is up to the developer to figure out how to handle strings when writing it through a TCP connection. Additionally, all the fields of your struct are unexported, binary.Write will not be able to access them. Use an uppercase letter to export them.
If you try it, you'll know it.
func main() {
...
err := binary.Write(&bin_buf, binary.BigEndian, msg)
fmt.Println(err)
fmt.Println(string(bin_buf.Bytes()))
...
}
I've been trying to subscribe to this API feed: https://www.cryptofacilities.com/resources/hc/en-us/articles/360000578294-Fills
Whenever I try to subscribe, it looks like I'm able to send the payload with WRITEJSON, but what I receive from my Read method is
websocket: close 1006 (abnormal closure): unexpected EOF.
The full terminal message is:
2019/08/04 22:20:31 recv: {"event":"info","version":1}
2019/08/04 22:20:31 recv: {"event":"subscribed","feed":"heartbeat"}
2019/08/04 22:20:31 recv: {"event":"challenge","message":"c6e55c07-d07a-4560-9283-be75ee458433"}
^C2019/08/04 22:21:50 interrupt
2019/08/04 22:21:50 write close: write tcp 192.168.1.6:49624->104.16.51.17:443: write: broken pipe
I understand from here that the status code means that my client closed the connection. I'm unable to track down this problem.
I've tried running race detection, turning off firewall (I have a Mac, and tried to turn off the antivirus - found out it's built in. Would this be worth pursuing?) increasing the handshake timeout, handling the error with a close frame, a different OS, increasing the buffer size, and max message size. A lot of it in reference to this post:
https://github.com/gorilla/websocket/issues/321
This is my client:
package websocket
import (
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
)
var addr = "wss://www.cryptofacilities.com/ws/v1"
var Wait = 50000 * time.Second
var MaxMessageSize int64 = 100000
//WebSocket connection struct to pass into other methods
type WebSocket struct {
Conn *websocket.Conn
}
//Message represents the public server push messages
type Message struct {
Event string
Feed string
Message interface{}
}
type ChallengeSub struct {
Event string `json:"event"`
Message string `json:"message"`
}
type HeartBeat struct {
Event string
Message string
}
type FillSubscribe struct {
Event string `json:"event"`
Feed string `json:"feed"`
APIKey string `json:"api_key"`
OriginalChallenge string `json:"original_challenge"`
SignedChallenge string `json:"signed_challenge"`
}
//OpenWebSocket Connects to kraken API and returns a connection
func OpenWebSocket() (*WebSocket, error) {
conn, response, err := websocket.DefaultDialer.Dial(addr, nil)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusSwitchingProtocols {
return nil, fmt.Errorf("failed to upgrade protocol to websocket")
}
return &WebSocket{Conn: conn}, nil
}
//HeartBeat subscribes to the pingpong feed to keep the connection alive
func (c *WebSocket) HeartBeat() error {
ping := map[string]interface{}{
"event": "subscribe",
"feed": "heartbeat",
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(ping)
return err
}
func (c *WebSocket) Next() ([]byte, error) {
_, payload, err := c.Conn.ReadMessage()
return payload, err
}
//Challenge requests the UUID from kraken API for auth handshake
func (c *WebSocket) Challenge() error {
challengeRequest := map[string]interface{}{
"event": "challenge",
"api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(challengeRequest)
if err != nil {
log.Println("write:", err)
return err
}
return err
}
func (c *WebSocket) Decode(payload []byte) (string, string) { //Decode takes in a connection and reference to Message struct
var msg json.RawMessage
env := Message{
Message: &msg,
}
if err := json.Unmarshal([]byte(payload), &env); err != nil {
log.Fatal(err)
}
switch env.Event {
case "challenge":
var s ChallengeSub
if err := json.Unmarshal(msg, &s.Message); err != nil {
log.Fatal(err)
}
message, signed := c.Signature(s.Message)
c.FillSubscribe(message, signed)
return message, signed
case "info":
{
fmt.Println("Connected:", env.Event)
}
case "subscribed":
{
fmt.Println("Connected to Heartbeat")
}
default:
switch env.Feed {
case "heartbeat":
fmt.Println("Live")
}
}
return "No messages to Decode...", ""
}
func (c *WebSocket) Signature(message string) (string, string) {
secret64, _ := base64.StdEncoding.DecodeString("rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m")
hash := sha256.New()
hash.Write([]byte(message))
challenge256 := hash.Sum(nil)
hmacHash := hmac.New(sha512.New, secret64)
hmacHash.Write(challenge256)
secretChallenge := hmacHash.Sum(nil)
signed := base64.StdEncoding.EncodeToString(secretChallenge)
return message, signed
}
//FillSubscribe populates message struct and sends out the JSON message
func (c *WebSocket) FillSubscribe(challenge string, signed string) error {
fillMessage := map[string]interface{}{
"event": "subscribe",
"feed": "fills",
"api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
"original_challenge": challenge,
"signed_challenge": signed,
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(fillMessage)
if err != nil {
log.Println("write:", err)
return err
}
return err
}
Here is my main program:
package main
import (
"fmt"
"log"
"github.com/Mjavala/KrakenAPI/websocket"
)
var message string
var signed string
func main() {
ws, err := websocket.OpenWebSocket()
if err != nil {
log.Fatal(err)
}
ws.HeartBeat()
ws.Challenge()
fmt.Println(message, signed)
for {
// We first read in a raw message. An error here is a socket level
// error.
payload, err := ws.Next()
if err != nil {
log.Fatalf("socket error: %+v\n", err)
}
log.Printf("recv: %s", payload)
message, signed = ws.Decode(payload)
if err != nil {
log.Fatalf("decode error: %+v\n", err)
}
}
}
I believe that even if the API Keys are wrong a return message should still be sent, as per the API; but instead I get that 1006 close frame.
I'm new to WebSockets. I'm able to get the challenge messaged and heartbeat subscription from the API, the problem is specific to the fill subscription.
Also if anyone wants to replicate this, I can give the full code/Git link.
I need to correct and transform a function, to checking 2 addresses of btc balance, like (addr and addr1 ), and the return is json value, i need to specify im not know very well golang.. need help!
func balance(addr) {
var url = "https://bitcoin.toshi.io/api/v0/addresses/" + addr
response, _ := http.Get(url)
defer response.Body.Close()
contents, _ := ioutil.ReadAll(response.Body)
return contents
}
EDIT:
i need a function to get balances of 2 btc addresses, or to use this libraries btcwallet.
func GetAddressBalance(icmd btcjson.Cmd) (interface{}, *btcjson.Error)
func GetAddressesByAccount(icmd btcjson.Cmd) (interface{}, *btcjson.Error)
func GetBalance(icmd btcjson.Cmd) (interface{}, *btcjson.Error)
You were on the right track. The api returns a json. This can be unmarshalled into a struct (Address). That way you can access everything returned by the api. Do not forget to always check for error codes! Replace some_address with an actual address to get the balance.
package main
import (
"encoding/json"
"fmt"
"net/http"
)
func main() {
a, err := getAddress("some_address")
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Balance:", a.Balance)
}
type Address struct {
Hash string
Balance int
Received int
Send int
UnconfirmedBalance int `json:"unconfirmed_balance"`
UnconfirmedReceived int `json:"unconfirmed_received"`
UnconfirmedSent int `json:"unconfirmed_sent"`
}
func getAddress(addr string) (*Address, error) {
resp, err := http.Get("https://bitcoin.toshi.io/api/v0/addresses/" + addr)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Error: %v", http.StatusText(resp.StatusCode))
}
var address Address
decoder := json.NewDecoder(resp.Body)
if err := decoder.Decode(&address); err != nil {
return nil, err
}
return &address, nil
}