> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbitflare.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket 客户端

> 账户变更、日志、槽位与签名的实时订阅，支持自动重连。

## 安装

```bash theme={null}
go get github.com/orbitflare/orbitflare-sdk-go
```

```go theme={null}
import "github.com/orbitflare/orbitflare-sdk-go/ws"
```

## 构建客户端

```go theme={null}
import (
    "time"

    "github.com/orbitflare/orbitflare-sdk-go/orbitflare"
    "github.com/orbitflare/orbitflare-sdk-go/ws"
)

client, err := ws.NewBuilder().
    URL("ws://ny.rpc.orbitflare.com").
    FallbackURL("ws://fra.rpc.orbitflare.com").
    APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN").
    Retry(orbitflare.RetryPolicy{
        InitialDelay: 100 * time.Millisecond,
        MaxDelay:     30 * time.Second,
        Multiplier:   2.0,
        MaxAttempts:  0,
    }).
    PingInterval(10 * time.Second).
    MaxMissedPongs(3).
    Build(ctx)
```

最小化：

```go theme={null}
client, err := ws.NewBuilder().
    URL("ws://ny.rpc.orbitflare.com").
    Build(ctx)
```

注意 `.Build(ctx)` 会在返回前建立 WebSocket 连接，传入的 context 用于控制初始拨号。若连接失败，你会立即得到错误。之后客户端会一直运行，直到 `Close()`。

### 构建器方法

**`.URL(url)`** - 主 WebSocket 端点。回退读取 `ORBITFLARE_WS_URL` 环境变量。

```go theme={null}
.URL("ws://ny.rpc.orbitflare.com")
```

**`.URLs(urls...)`** - 一次设置主端点与回退端点。

```go theme={null}
.URLs("ws://ny.rpc.orbitflare.com", "ws://fra.rpc.orbitflare.com")
```

**`.FallbackURL(url)` / `.FallbackURLs(urls...)`** - 添加故障转移端点。重连时，SDK 会轮换尝试。

```go theme={null}
.FallbackURL("ws://fra.rpc.orbitflare.com")
```

**`.APIKey(key)`** - 许可证密钥。回退读取 `ORBITFLARE_LICENSE_KEY` 环境变量。在连接时注入 URL，绝不出现在日志或错误信息中。

```go theme={null}
.APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN")
```

**`.Retry(policy)`** - 重连退避。默认：100ms 初始、30s 上限、2 倍系数、无限次。

```go theme={null}
.Retry(orbitflare.RetryPolicy{
    InitialDelay: 200 * time.Millisecond,
    MaxDelay:     15 * time.Second,
    Multiplier:   2.0,
    MaxAttempts:  0,
})
```

**`.PingInterval(duration)`** - SDK 发送 WebSocket `Ping` 帧的频率。默认 10s。

```go theme={null}
.PingInterval(15 * time.Second)
```

**`.MaxMissedPongs(n)`** - 连接被判定为死连接前允许多少次未获响应的 ping。默认 3。

```go theme={null}
.MaxMissedPongs(5)
```

**`.Logger(logger)`** - 用于连接生命周期事件的 `*slog.Logger`。未设置时由 `ORBITFLARE_LOG` 控制。

## 可用订阅

### `SlotSubscribe(ctx)`

每当一个槽位被处理、确认或最终确定时触发。

```go theme={null}
sub, err := client.SlotSubscribe(ctx)
```

每条事件为带 `slot`、`parent` 与 `root` 字段的 JSON：

```json theme={null}
{"slot": 413014740, "parent": 413014739, "root": 413014708}
```

### `AccountSubscribe(ctx, address, commitment)`

当指定账户的数据发生变化时触发。

```go theme={null}
sub, err := client.AccountSubscribe(ctx, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "confirmed")
```

返回带 lamports、owner 与可执行标志的 base64 编码账户数据。

### `LogsSubscribe(ctx, mentions, commitment)`

对提及给定地址的交易触发。传入空切片以匹配所有交易。

```go theme={null}
sub, err := client.LogsSubscribe(ctx, []string{"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}, "confirmed")
```

所有日志：

```go theme={null}
sub, err := client.LogsSubscribe(ctx, nil, "confirmed")
```

### `SignatureSubscribe(ctx, signature, commitment)`

当交易达到给定承诺级别时触发一次。适用于确认你刚发送的交易。

```go theme={null}
sub, err := client.SignatureSubscribe(ctx, "5K8F2j...", "confirmed")
```

## 读取事件

所有订阅都返回一个 `*ws.Subscription`。调用 `Next(ctx)` 获取下一条事件：

```go theme={null}
for {
    event, ok := sub.Next(ctx)
    if !ok {
        break // context 被取消或客户端已关闭
    }
    fmt.Println(string(event))
}
```

`Next(ctx)` 返回 `(json.RawMessage, bool)`。当 `ok` 为 false 时订阅已结束（context 被取消或客户端已关闭）。

## 取消订阅

```go theme={null}
sub.Unsubscribe()
```

这会向服务器发送取消订阅消息。若你停止读取某订阅而未调用它，SDK 会检测到该孤立订阅并自动发送取消订阅。

## 多路订阅

所有订阅运行在单个 WebSocket 连接上。SDK 在内部将通知路由到正确的订阅。

```go theme={null}
slots, _ := client.SlotSubscribe(ctx)
usdc, _ := client.AccountSubscribe(ctx, "EPjFWdd5...", "confirmed")
logs, _ := client.LogsSubscribe(ctx, []string{"6EF8r..."}, "confirmed")
```

你可以在已有订阅运行时随时添加新的订阅。

## 重连

若连接断开，后台 goroutine 会以指数退避重连并自动重新订阅一切。你的 `Next(ctx)` 调用会照常工作，连接恢复后事件即恢复。

## 完整示例

一个订阅槽位更新与 USDC 账户变更、并打印实时信息流的监控程序。

```go theme={null}
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/orbitflare/orbitflare-sdk-go/ws"
)

func main() {
    ctx := context.Background()

    client, err := ws.NewBuilder().
        URL("ws://ny.rpc.orbitflare.com").
        Build(ctx)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    slots, err := client.SlotSubscribe(ctx)
    if err != nil {
        log.Fatal(err)
    }
    defer slots.Unsubscribe()

    fmt.Println("watching slots...")
    for {
        event, ok := slots.Next(ctx)
        if !ok {
            return
        }
        var slot struct {
            Slot   uint64 `json:"slot"`
            Parent uint64 `json:"parent"`
        }
        json.Unmarshal(event, &slot)
        fmt.Printf("slot %d (parent %d)\n", slot.Slot, slot.Parent)
    }
}
```
