能连接和通讯,还未解析数据

This commit is contained in:
钱纯净
2024-10-13 23:23:34 +08:00
parent 04e405d3f1
commit 0464d8512f
7 changed files with 407 additions and 9 deletions

View File

@@ -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
View 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
View File

@@ -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
View 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
View 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
View 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
View File

@@ -0,0 +1,4 @@
package protocol
type SecurityListResp struct {
}