mirror of
https://github.com/injoyai/tdx.git
synced 2025-11-26 21:25:35 +08:00
开发
This commit is contained in:
45
client.go
45
client.go
@@ -1,7 +1,6 @@
|
||||
package tdx
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/injoyai/base/maps/wait/v2"
|
||||
"github.com/injoyai/conv"
|
||||
"github.com/injoyai/ios"
|
||||
@@ -22,13 +21,18 @@ func Dial(addr string, op ...client.Option) (cli *Client, err error) {
|
||||
cli.c, err = dial.TCP(addr, func(c *client.Client) {
|
||||
c.Logger.WithHEX() //以HEX显示
|
||||
c.SetOption(op...) //自定义选项
|
||||
//c.Event.OnReadFrom = protocol.ReadFrom //分包
|
||||
//c.AllReader = ios.NewAllReader(c.Reader.(io.Reader), protocol.ReadFrom) //分包
|
||||
c.Event.OnReadFrom = protocol.ReadFrom //分包
|
||||
c.Event.OnDealMessage = cli.handlerDealMessage //处理分包数据
|
||||
|
||||
logs.Debug("option")
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logs.Debug("run")
|
||||
logs.Debugf("%#v\n", cli.c.Event.OnReadFrom)
|
||||
go cli.c.Run()
|
||||
|
||||
err = cli.connect()
|
||||
@@ -55,16 +59,23 @@ func (this *Client) handlerDealMessage(c *client.Client, msg ios.Acker) {
|
||||
return
|
||||
}
|
||||
|
||||
var resp any
|
||||
switch f.Type {
|
||||
case protocol.TypeSecurityQuote:
|
||||
resp := protocol.MSecurityQuote.Decode(f.Data)
|
||||
logs.Debug(resp)
|
||||
this.w.Done(conv.String(f.MsgID), resp)
|
||||
return
|
||||
resp = protocol.MSecurityQuote.Decode(f.Data)
|
||||
|
||||
case protocol.TypeSecurityList:
|
||||
resp, err = protocol.MSecurityList.Decode(f.Data)
|
||||
|
||||
}
|
||||
|
||||
_ = f
|
||||
if err != nil {
|
||||
logs.Err(err)
|
||||
return
|
||||
}
|
||||
|
||||
logs.Debug(resp)
|
||||
this.w.Done(conv.String(f.MsgID), resp)
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +88,7 @@ func (this *Client) SendFrame(f *protocol.Frame) (any, error) {
|
||||
return this.w.Wait(conv.String(this.msgID))
|
||||
}
|
||||
|
||||
// Send 向服务发送数据,并等待响应数据
|
||||
func (this *Client) Send(bs []byte) (any, error) {
|
||||
if _, err := this.c.Write(bs); err != nil {
|
||||
return nil, err
|
||||
@@ -84,6 +96,7 @@ func (this *Client) Send(bs []byte) (any, error) {
|
||||
return this.w.Wait(conv.String(this.msgID))
|
||||
}
|
||||
|
||||
// Write 实现io.Writer,向服务器写入数据
|
||||
func (this *Client) Write(bs []byte) (int, error) {
|
||||
return this.c.Write(bs)
|
||||
}
|
||||
@@ -99,23 +112,13 @@ func (this *Client) connect() error {
|
||||
}
|
||||
|
||||
// GetSecurityList 获取市场内指定范围内的所有证券代码
|
||||
// 0c02000000011a001a003e05050000000000000002000030303030303101363030303038
|
||||
func (this *Client) GetSecurityList() (*protocol.SecurityListResp, error) {
|
||||
|
||||
f := protocol.Frame{
|
||||
Control: 0x01,
|
||||
Type: protocol.TypeConnect,
|
||||
Data: nil,
|
||||
}
|
||||
_ = f
|
||||
|
||||
bs, err := hex.DecodeString("0c02000000011a001a003e05050000000000000002000030303030303101363030303038")
|
||||
func (this *Client) GetSecurityList(exchange protocol.Exchange, starts ...uint16) (*protocol.SecurityListResp, error) {
|
||||
f := protocol.MSecurityList.Frame(exchange, starts...)
|
||||
result, err := this.SendFrame(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = this.Write(bs)
|
||||
return nil, err
|
||||
return result.(*protocol.SecurityListResp), nil
|
||||
|
||||
}
|
||||
|
||||
|
||||
21
example/testlist/main.go
Normal file
21
example/testlist/main.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/injoyai/logs"
|
||||
"github.com/injoyai/tdx"
|
||||
"github.com/injoyai/tdx/protocol"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c, err := tdx.Dial("124.71.187.122:7709")
|
||||
logs.PanicErr(err)
|
||||
|
||||
resp, err := c.GetSecurityList(protocol.ExchangeSH)
|
||||
logs.PrintErr(err)
|
||||
|
||||
for _, v := range resp.List {
|
||||
logs.Debugf("%#v\n", v)
|
||||
}
|
||||
|
||||
select {}
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -8,6 +8,7 @@ require (
|
||||
github.com/injoyai/goutil v0.0.0-20241009040015-3d20afe3efe6
|
||||
github.com/injoyai/ios v0.0.3
|
||||
github.com/injoyai/logs v1.0.9
|
||||
golang.org/x/text v0.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -59,7 +60,6 @@ require (
|
||||
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
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package protocol
|
||||
|
||||
const (
|
||||
Control = 0x01
|
||||
)
|
||||
|
||||
const (
|
||||
TypeConnect = 0x000d //建立连接
|
||||
TypeHandshake = 0xdb0f //握手
|
||||
TypeSecurityList = 0x0450 //获取股票代码
|
||||
TypeSecurityQuote = 0x053e // 行情信息
|
||||
)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/injoyai/base/bytes"
|
||||
"github.com/injoyai/base/g"
|
||||
"github.com/injoyai/conv"
|
||||
"github.com/injoyai/logs"
|
||||
"io"
|
||||
)
|
||||
|
||||
@@ -34,7 +35,7 @@ Frame 数据帧
|
||||
*/
|
||||
type Frame struct {
|
||||
MsgID uint32 //消息ID
|
||||
Control uint8 //控制码,这个还不知道怎么定义
|
||||
Control Control //控制码,这个还不知道怎么定义
|
||||
Type uint16 //请求类型,如建立连接,请求分时数据等
|
||||
Data []byte //数据
|
||||
}
|
||||
@@ -44,7 +45,7 @@ func (this *Frame) Bytes() g.Bytes {
|
||||
data := make([]byte, 12+len(this.Data))
|
||||
data[0] = Prefix
|
||||
copy(data[1:], Bytes(this.MsgID))
|
||||
data[5] = this.Control
|
||||
data[5] = this.Control.Uint8()
|
||||
copy(data[6:], Bytes(length))
|
||||
copy(data[8:], Bytes(length))
|
||||
copy(data[10:], Bytes(this.Type))
|
||||
@@ -108,6 +109,7 @@ func Decode(bs []byte) (*Response, error) {
|
||||
|
||||
// ReadFrom 这里的r推荐传入*bufio.Reader
|
||||
func ReadFrom(r io.Reader) (result []byte, err error) {
|
||||
logs.Debug("ReadFrom")
|
||||
prefix := make([]byte, 4)
|
||||
for {
|
||||
result = []byte(nil)
|
||||
@@ -132,6 +134,7 @@ func ReadFrom(r io.Reader) (result []byte, err error) {
|
||||
|
||||
//获取后续字节长度
|
||||
length := uint16(result[11])<<8 + uint16(result[10])
|
||||
logs.Debug("长度:", length)
|
||||
buf = make([]byte, length)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,13 +3,14 @@ package protocol
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/injoyai/conv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
MConnect = connect{}
|
||||
MSecurityQuote = securityQuote{}
|
||||
SecurityList = securityList{}
|
||||
MSecurityList = securityList{}
|
||||
)
|
||||
|
||||
type ConnectResp struct {
|
||||
@@ -20,7 +21,7 @@ type connect struct{}
|
||||
|
||||
func (connect) Frame() *Frame {
|
||||
return &Frame{
|
||||
Control: Control,
|
||||
Control: Control01,
|
||||
Type: TypeConnect,
|
||||
Data: []byte{0x01},
|
||||
}
|
||||
@@ -55,11 +56,12 @@ type Security struct {
|
||||
|
||||
type securityList struct{}
|
||||
|
||||
func (securityList) Frame() *Frame {
|
||||
func (securityList) Frame(exchange Exchange, starts ...uint16) *Frame {
|
||||
start := conv.DefaultUint16(0, starts...)
|
||||
return &Frame{
|
||||
Control: 0x01,
|
||||
Type: TypeConnect,
|
||||
Data: nil,
|
||||
Control: Control01,
|
||||
Type: TypeSecurityList,
|
||||
Data: []byte{exchange.Uint8(), 0x0, uint8(start), uint8(start >> 8)},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,11 +71,23 @@ func (securityList) Decode(bs []byte) (*SecurityListResp, error) {
|
||||
return nil, errors.New("数据长度不足")
|
||||
}
|
||||
|
||||
count := Uint16(bs[:2])
|
||||
resp := &SecurityListResp{
|
||||
Count: Uint16(bs[:2]),
|
||||
}
|
||||
bs = bs[2:]
|
||||
|
||||
_ = count
|
||||
for i := uint16(0); i < resp.Count; i++ {
|
||||
sec := &Security{
|
||||
Code: String(bs[:6]),
|
||||
VolUnit: Uint16(bs[6:8]),
|
||||
Name: string(UTF8ToGBK(bs[8:16])),
|
||||
PreClose: getVolume(Uint32(bs[16:20])),
|
||||
}
|
||||
bs = bs[20:]
|
||||
resp.List = append(resp.List, sec)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -139,7 +153,7 @@ type securityQuote struct{}
|
||||
|
||||
func (this securityQuote) Frame(m map[Exchange]string) (*Frame, error) {
|
||||
f := &Frame{
|
||||
Control: Control,
|
||||
Control: Control01,
|
||||
Type: TypeSecurityQuote,
|
||||
Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
}
|
||||
|
||||
@@ -42,3 +42,12 @@ func Test_getPrice(t *testing.T) {
|
||||
t.Log(getPrice([]byte{0x7f, 0x3f, 0x40, 0x3f, 0x01})) //预期-63
|
||||
t.Log(getPrice([]byte{0x2f, 0x3f, 0x40, 0x3f, 0x01})) //预期47
|
||||
}
|
||||
|
||||
/*
|
||||
0c000000000106000600500400000000
|
||||
0c020000000106000600500400000000
|
||||
*/
|
||||
func Test_securityList_Frame(t *testing.T) {
|
||||
f := MSecurityList.Frame(ExchangeSH, 0)
|
||||
t.Log(f.Bytes().HEX())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
package protocol
|
||||
|
||||
type Control uint8
|
||||
|
||||
func (this Control) Uint8() uint8 {
|
||||
return uint8(this)
|
||||
}
|
||||
|
||||
const (
|
||||
Control01 Control = 0x01 //好像都是01,暂时不知道啥含义
|
||||
)
|
||||
|
||||
type Exchange uint8
|
||||
|
||||
func (this Exchange) Uint8() uint8 { return uint8(this) }
|
||||
|
||||
Reference in New Issue
Block a user