mirror of
https://github.com/injoyai/tdx.git
synced 2025-11-26 21:25:35 +08:00
能连接和通讯,还未解析数据
This commit is contained in:
94
client.go
94
client.go
@@ -1,26 +1,102 @@
|
||||
package tdx
|
||||
|
||||
import "net"
|
||||
import (
|
||||
"github.com/injoyai/base/maps/wait/v2"
|
||||
"github.com/injoyai/conv"
|
||||
"github.com/injoyai/ios"
|
||||
"github.com/injoyai/ios/client"
|
||||
"github.com/injoyai/ios/client/dial"
|
||||
"github.com/injoyai/logs"
|
||||
"github.com/injoyai/tdx/protocol"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Dial(addr string) (*Client, error) {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
func Dial(addr string, op ...client.Option) (*Client, error) {
|
||||
c, err := dial.TCP(addr, func(c *client.Client) {
|
||||
c.Logger.WithHEX() //以HEX显示
|
||||
c.SetOption(op...) //自定义选项
|
||||
//c.Event.OnReadFrom = protocol.ReadFrom //分包
|
||||
c.Event.OnDealMessage = handlerDealMessage //处理分包数据
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Client{
|
||||
conn: conn,
|
||||
}, nil
|
||||
|
||||
go c.Run()
|
||||
|
||||
cli := &Client{
|
||||
c: c,
|
||||
w: wait.New(time.Second * 2),
|
||||
}
|
||||
|
||||
err = cli.Connect()
|
||||
|
||||
return cli, err
|
||||
}
|
||||
|
||||
// handlerDealMessage 处理服务器响应的数据
|
||||
func handlerDealMessage(c *client.Client, msg ios.Acker) {
|
||||
|
||||
f, err := protocol.Decode(msg.Payload())
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
return
|
||||
}
|
||||
|
||||
_ = f
|
||||
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
c *client.Client
|
||||
w *wait.Entity
|
||||
msgID uint32
|
||||
}
|
||||
|
||||
func (this *Client) SendFrame(f protocol.Frame) (any, error) {
|
||||
this.msgID++
|
||||
f.MsgID = this.msgID
|
||||
if _, err := this.c.Write(f.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.w.Wait(conv.String(this.msgID))
|
||||
}
|
||||
|
||||
func (this *Client) Send(bs []byte) (any, error) {
|
||||
if _, err := this.c.Write(bs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.w.Wait(conv.String(this.msgID))
|
||||
}
|
||||
|
||||
func (this *Client) Write(bs []byte) (int, error) {
|
||||
return this.c.Write(bs)
|
||||
}
|
||||
|
||||
func (this *Client) Close() error {
|
||||
return this.conn.Close()
|
||||
return this.c.Close()
|
||||
}
|
||||
|
||||
func (this *Client) Connect() error {
|
||||
f := protocol.Frame{
|
||||
Control: 0x01,
|
||||
Type: protocol.Connect,
|
||||
Data: []byte{0x01},
|
||||
}
|
||||
_, err := this.Write(f.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
// GetSecurityList 获取市场内指定范围内的所有证券代码
|
||||
func (this *Client) GetSecurityList() {
|
||||
func (this *Client) GetSecurityList() (*SecurityListResp, error) {
|
||||
|
||||
f := protocol.Frame{
|
||||
Control: 0x01,
|
||||
Type: protocol.Connect,
|
||||
Data: nil,
|
||||
}
|
||||
|
||||
_, err := this.Write(f.Bytes())
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
53
example/test/main.go
Normal file
53
example/test/main.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"github.com/injoyai/goutil/g"
|
||||
"github.com/injoyai/ios/client"
|
||||
"github.com/injoyai/ios/server"
|
||||
"github.com/injoyai/ios/server/listen"
|
||||
"github.com/injoyai/logs"
|
||||
"github.com/injoyai/tdx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
c, err := tdx.Dial("124.71.187.122:7709")
|
||||
logs.PanicErr(err)
|
||||
|
||||
_ = c
|
||||
|
||||
/*
|
||||
发送:
|
||||
0c02000000011a001a003e05050000000000000002000030303030303101363030303038
|
||||
|
||||
接受:
|
||||
b1cb74001c00000000000d005100bd00789c6378c1cecb252ace6066c5b4898987b9050ed1f90cc5b74c18a5bc18c1b43490fecff09c81819191f13fc3c9f3bb169f5e7dfefeb5ef57f7199a305009308208e5b32bb6bcbf70148712002d7f1e13
|
||||
b1cb74000c02000000003e05ac00ac000102020000303030303031601294121a1c2d4eadabcf0ed412aae5fc01afb0024561124fbcc08301afa47900b2e3174100bf68871a4201b741b6144302bb09af334403972e96354504ac09b619560e00000000f8ff601201363030303038b60fba04060607429788a70efa04ada37ab2531c12974d91e7449dbc354184b6010001844bad324102b5679ea1014203a65abd8d0143048a6ba4dd01440587e101b3d2029613000000000000b60f
|
||||
*/
|
||||
bs, err := hex.DecodeString("0c02000000011a001a003e05050000000000000002000030303030303101363030303038")
|
||||
logs.PanicErr(err)
|
||||
_, err = c.Write(bs)
|
||||
logs.PanicErr(err)
|
||||
|
||||
select {}
|
||||
|
||||
}
|
||||
|
||||
func _listen() {
|
||||
listen.RunTCP(7709, func(s *server.Server) {
|
||||
s.SetClientOption(func(c *client.Client) {
|
||||
c.Logger.WithHEX()
|
||||
})
|
||||
})
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := binary.Write(buf, binary.LittleEndian, g.Map{
|
||||
"name": "名称",
|
||||
"age": 17,
|
||||
})
|
||||
logs.PrintErr(err)
|
||||
logs.Debug(buf.String())
|
||||
}
|
||||
69
go.mod
69
go.mod
@@ -1,3 +1,72 @@
|
||||
module github.com/injoyai/tdx
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/injoyai/base v1.0.18
|
||||
github.com/injoyai/conv v1.1.10
|
||||
github.com/injoyai/goutil v0.0.0-20241009040015-3d20afe3efe6
|
||||
github.com/injoyai/ios v0.0.3
|
||||
github.com/injoyai/logs v1.0.9
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/DrmagicE/gmqtt v0.5.0 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.8.1 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.349 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/creack/goselect v0.1.2 // indirect
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0 // indirect
|
||||
github.com/fatih/color v1.14.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect
|
||||
github.com/goburrow/serial v0.1.0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/gomodule/redigo v1.8.5 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/injoyai/io v0.1.8 // indirect
|
||||
github.com/jmespath/go-jmespath v0.3.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.7.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.10.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
|
||||
github.com/robertkrimen/otto v0.2.1 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.667 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.667 // indirect
|
||||
go.bug.st/serial v1.5.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3 // indirect
|
||||
google.golang.org/grpc v1.50.1 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
65
protocol/const.go
Normal file
65
protocol/const.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package protocol
|
||||
|
||||
const (
|
||||
Connect = 0x000d //建立连接
|
||||
Handshake = 0xdb0f //握手
|
||||
)
|
||||
|
||||
const (
|
||||
LOGIN_ONE = 0x000d //第一次登录
|
||||
LOGIN_TWO = 0x0fdb //第二次登录
|
||||
HEART = 0x0004 //心跳维持
|
||||
STOCK_COUNT = 0x044e //股票数目
|
||||
STOCK_LIST = 0x0450 //股票列表
|
||||
KMINUTE = 0x0537 //当天分时K线
|
||||
KMINUTE_OLD = 0x0fb4 //指定日期分时K线
|
||||
KLINE = 0x052d //股票K线
|
||||
BIDD = 0x056a //当日的竞价
|
||||
QUOTE = 0x053e //实时五笔报价
|
||||
QUOTE_SORT = 0x053e //沪深排序
|
||||
TRANSACTION = 0x0fc5 //分笔成交明细
|
||||
TRANSACTION_OLD = 0x0fb5 //历史分笔成交明细
|
||||
FINANCE = 0x0010 //财务数据
|
||||
COMPANY = 0x02d0 //公司数据 F10
|
||||
EXDIVIDEND = 0x000f //除权除息
|
||||
FILE_DIRECTORY = 0x02cf //公司文件目录
|
||||
FILE_CONTENT = 0x02d0 //公司文件内容
|
||||
)
|
||||
|
||||
const (
|
||||
KMSG_CMD1 = 0x000d // 建立链接
|
||||
KMSG_CMD2 = 0x0fdb // 建立链接
|
||||
KMSG_PING = 0x0015 // 测试连接
|
||||
KMSG_HEARTBEAT = 0xFFFF // 心跳(自定义)
|
||||
KMSG_SECURITYCOUNT = 0x044e // 证券数量
|
||||
KMSG_BLOCKINFOMETA = 0x02c5 // 板块文件信息
|
||||
KMSG_BLOCKINFO = 0x06b9 // 板块文件
|
||||
KMSG_COMPANYCATEGORY = 0x02cf // 公司信息文件信息
|
||||
KMSG_COMPANYCONTENT = 0x02d0 // 公司信息描述
|
||||
KMSG_FINANCEINFO = 0x0010 // 财务信息
|
||||
KMSG_HISTORYMINUTETIMEDATE = 0x0fb4 // 历史分时信息
|
||||
KMSG_HISTORYTRANSACTIONDATA = 0x0fb5 // 历史分笔成交信息
|
||||
KMSG_INDEXBARS = 0x052d // 指数K线
|
||||
KMSG_SECURITYBARS = 0x052d // 股票K线
|
||||
KMSG_MINUTETIMEDATA = 0x0537 // 分时数据
|
||||
KMSG_SECURITYLIST = 0x0450 // 证券列表
|
||||
KMSG_SECURITYQUOTES = 0x053e // 行情信息
|
||||
KMSG_TRANSACTIONDATA = 0x0fc5 // 分笔成交信息
|
||||
KMSG_XDXRINFO = 0x000f // 除权除息信息
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
KLINE_TYPE_5MIN = 0 // 5分钟K 线
|
||||
KLINE_TYPE_15MIN = 1 // 15分钟K 线
|
||||
KLINE_TYPE_30MIN = 2 // 30分钟K 线
|
||||
KLINE_TYPE_1HOUR = 3 // 1小时K 线
|
||||
KLINE_TYPE_DAILY = 4 // 日K 线
|
||||
KLINE_TYPE_WEEKLY = 5 // 周K 线
|
||||
KLINE_TYPE_MONTHLY = 6 // 月K 线
|
||||
KLINE_TYPE_EXHQ_1MIN = 7 // 1分钟
|
||||
KLINE_TYPE_1MIN = 8 // 1分钟K 线
|
||||
KLINE_TYPE_RI_K = 9 // 日K 线
|
||||
KLINE_TYPE_3MONTH = 10 // 季K 线
|
||||
KLINE_TYPE_YEARLY = 11 // 年K 线
|
||||
)
|
||||
106
protocol/frame.go
Normal file
106
protocol/frame.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/injoyai/base/bytes"
|
||||
"github.com/injoyai/base/g"
|
||||
"github.com/injoyai/conv"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// Prefix 固定帧头
|
||||
Prefix = 0x0c
|
||||
)
|
||||
|
||||
type Message interface {
|
||||
Bytes() g.Bytes
|
||||
}
|
||||
|
||||
/*
|
||||
Frame 数据帧
|
||||
0c 02189300 01 0300 0300 0d00 01
|
||||
0c 00000000 00 0200 0200 1500
|
||||
0c 01000000 01 0300 0300 0d00 01
|
||||
0c 01000000 01 0300 0300 0d00 01
|
||||
0c 02000000 01 1a00 1a00 3e05 050000000000000002000030303030303101363030303038
|
||||
|
||||
0c0100000001030003000d0001
|
||||
*/
|
||||
type Frame struct {
|
||||
MsgID uint32 //消息ID
|
||||
Control uint8 //控制码,这个还不知道怎么定义
|
||||
Type uint16 //请求类型,如建立连接,请求分时数据等
|
||||
Data []byte //数据
|
||||
}
|
||||
|
||||
func (this *Frame) Bytes() g.Bytes {
|
||||
length := uint16(len(this.Data) + 2)
|
||||
data := make([]byte, 12+len(this.Data))
|
||||
data[0] = Prefix
|
||||
copy(data[1:], Bytes(this.MsgID))
|
||||
data[5] = this.Control
|
||||
copy(data[6:], Bytes(length))
|
||||
copy(data[8:], Bytes(length))
|
||||
copy(data[10:], Bytes(this.Type))
|
||||
copy(data[12:], this.Data)
|
||||
return data
|
||||
}
|
||||
|
||||
func Bytes(n any) []byte {
|
||||
return bytes.Reverse(conv.Bytes(n))
|
||||
}
|
||||
|
||||
func Decode(bs []byte) (*Frame, error) {
|
||||
if len(bs) < 10 {
|
||||
return nil, errors.New("数据长度不足")
|
||||
}
|
||||
f := &Frame{}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// ReadFrom 这里的r推荐传入*bufio.Reader
|
||||
func ReadFrom(r io.Reader) ([]byte, error) {
|
||||
result := []byte(nil)
|
||||
b := make([]byte, 1)
|
||||
for {
|
||||
result = []byte(nil)
|
||||
|
||||
n, err := r.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n == 0 || b[0] != Prefix {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, b[0])
|
||||
|
||||
//读取9字节 消息ID+控制码+2个字节长度
|
||||
buf := make([]byte, 9)
|
||||
n, err = r.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 9 {
|
||||
continue
|
||||
}
|
||||
result = append(result, buf...)
|
||||
|
||||
//获取后续字节长度
|
||||
length := uint16(result[9])<<8 + uint16(result[10])
|
||||
buf = make([]byte, length)
|
||||
n, err = r.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != int(length) {
|
||||
continue
|
||||
}
|
||||
result = append(result, buf...)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
}
|
||||
25
protocol/frame_test.go
Normal file
25
protocol/frame_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFrame_Bytes(t *testing.T) {
|
||||
f := Frame{
|
||||
MsgID: 1,
|
||||
Control: 1,
|
||||
Type: Connect,
|
||||
Data: []byte{0x01},
|
||||
}
|
||||
hex := f.Bytes().HEX()
|
||||
t.Log(hex)
|
||||
if hex != "0c0100000001030003000d0001" {
|
||||
t.Error("编码错误")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
t.Log(hex.EncodeToString(Bytes(uint32(1))))
|
||||
t.Log(hex.EncodeToString(Bytes(uint16(0x0d00))))
|
||||
}
|
||||
4
protocol/model.go
Normal file
4
protocol/model.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package protocol
|
||||
|
||||
type SecurityListResp struct {
|
||||
}
|
||||
Reference in New Issue
Block a user