diff --git a/client.go b/client.go index fc22ac6..3588599 100644 --- a/client.go +++ b/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 } diff --git a/example/testlist/main.go b/example/testlist/main.go new file mode 100644 index 0000000..f228701 --- /dev/null +++ b/example/testlist/main.go @@ -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 {} +} diff --git a/go.mod b/go.mod index f17f952..f735343 100644 --- a/go.mod +++ b/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 diff --git a/protocol/const.go b/protocol/const.go index b5ed4fb..0e1876e 100644 --- a/protocol/const.go +++ b/protocol/const.go @@ -1,12 +1,9 @@ package protocol -const ( - Control = 0x01 -) - const ( TypeConnect = 0x000d //建立连接 TypeHandshake = 0xdb0f //握手 + TypeSecurityList = 0x0450 //获取股票代码 TypeSecurityQuote = 0x053e // 行情信息 ) diff --git a/protocol/frame.go b/protocol/frame.go index e536354..072d10a 100644 --- a/protocol/frame.go +++ b/protocol/frame.go @@ -7,6 +7,7 @@ import ( "github.com/injoyai/base/bytes" "github.com/injoyai/base/g" "github.com/injoyai/conv" + "github.com/injoyai/logs" "io" ) @@ -33,10 +34,10 @@ Frame 数据帧 0c0100000001030003000d0001 */ type Frame struct { - MsgID uint32 //消息ID - Control uint8 //控制码,这个还不知道怎么定义 - Type uint16 //请求类型,如建立连接,请求分时数据等 - Data []byte //数据 + MsgID uint32 //消息ID + Control Control //控制码,这个还不知道怎么定义 + Type uint16 //请求类型,如建立连接,请求分时数据等 + Data []byte //数据 } func (this *Frame) Bytes() g.Bytes { @@ -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 { diff --git a/protocol/model_quote.go b/protocol/model_quote.go index dad9114..8e6efd7 100644 --- a/protocol/model_quote.go +++ b/protocol/model_quote.go @@ -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}, } diff --git a/protocol/model_quote_test.go b/protocol/model_quote_test.go index 5fe015a..ec0fc1c 100644 --- a/protocol/model_quote_test.go +++ b/protocol/model_quote_test.go @@ -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()) +} diff --git a/protocol/types.go b/protocol/types.go index fc740fa..c6d575d 100644 --- a/protocol/types.go +++ b/protocol/types.go @@ -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) }