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