This commit is contained in:
injoyai
2024-10-16 16:14:33 +08:00
parent 68a04b7ed7
commit ad6225ac22
7 changed files with 233 additions and 35 deletions

View File

@@ -31,7 +31,11 @@ func Dial(addr string, op ...client.Option) (*Client, error) {
w: wait.New(time.Second * 2),
}
err = cli.Connect()
err = cli.connect()
if err != nil {
c.Close()
return nil, err
}
return cli, err
}
@@ -55,7 +59,7 @@ type Client struct {
msgID uint32
}
func (this *Client) SendFrame(f protocol.Frame) (any, error) {
func (this *Client) SendFrame(f *protocol.Frame) (any, error) {
this.msgID++
f.MsgID = this.msgID
if _, err := this.c.Write(f.Bytes()); err != nil {
@@ -79,8 +83,8 @@ func (this *Client) Close() error {
return this.c.Close()
}
func (this *Client) Connect() error {
f := protocol.NewConnect()
func (this *Client) connect() error {
f := protocol.MConnect.Frame()
_, err := this.Write(f.Bytes())
return err
}
@@ -91,7 +95,7 @@ func (this *Client) GetSecurityList() (*protocol.SecurityListResp, error) {
f := protocol.Frame{
Control: 0x01,
Type: protocol.Connect,
Type: protocol.TypeConnect,
Data: nil,
}
_ = f
@@ -105,3 +109,16 @@ func (this *Client) GetSecurityList() (*protocol.SecurityListResp, error) {
return nil, err
}
// GetSecurityQuotes 获取盘口五档报价
func (this *Client) GetSecurityQuotes(m map[protocol.Exchange]string) (*protocol.SecurityQuotesResp, error) {
f, err := protocol.MSecurityQuote.Frame(m)
if err != nil {
return nil, err
}
result, err := this.SendFrame(f)
if err != nil {
return nil, err
}
return result.(*protocol.SecurityQuotesResp), nil
}

View File

@@ -7,6 +7,7 @@ func (this Exchange) Uint8() uint8 { return uint8(this) }
const (
ExchangeSH Exchange = iota //上海交易所
ExchangeSZ //深圳交易所
ExchangeBJ //北京交易所
)
const (
@@ -14,9 +15,9 @@ const (
)
const (
Connect = 0x000d //建立连接
Handshake = 0xdb0f //握手
SecurityQuote = 0x053e // 行情信息
TypeConnect = 0x000d //建立连接
TypeHandshake = 0xdb0f //握手
TypeSecurityQuote = 0x053e // 行情信息
)
const (

View File

@@ -54,9 +54,9 @@ func (this *Frame) Bytes() g.Bytes {
type Response struct {
Prefix uint32 //未知,猜测是帧头
Control uint8 //未知,猜测是响应的控制码
I2 uint8 //未知,猜测是响应的控制码
MsgID uint32 //消息ID
I3 uint8 //未知,猜测是响应的控制码
Control uint8 //未知,猜测是响应的控制码
Type uint16 //响应类型,对应请求类型,如建立连接,请求分时数据等
ZipLength uint16 //数据长度
Length uint16 //未压缩长度
@@ -73,9 +73,9 @@ func Decode(bs []byte) (*Response, error) {
}
resp := &Response{
Prefix: Uint32(bs[:4]),
Control: bs[4],
I2: bs[4],
MsgID: Uint32(bs[5:9]),
I3: bs[9],
Control: bs[9],
Type: Uint16(bs[10:12]),
ZipLength: Uint16(bs[12:14]),
Length: Uint16(bs[14:16]),
@@ -86,16 +86,18 @@ func Decode(bs []byte) (*Response, error) {
return nil, fmt.Errorf("压缩数据长度不匹配,预期%d,得到%d", resp.ZipLength+16, len(bs))
}
//进行数据解压
if resp.ZipLength != resp.Length {
r, err := zlib.NewReader(bytes.NewReader(resp.Data))
if err != nil {
return nil, err
}
defer r.Close()
resp.Data, err = io.ReadAll(r)
if err != nil {
return nil, err
}
}
if int(resp.Length) != len(resp.Data) {
return nil, fmt.Errorf("解压数据长度不匹配,预期%d,得到%d", resp.Length, len(resp.Data))

View File

@@ -9,7 +9,7 @@ func TestFrame_Bytes(t *testing.T) {
f := Frame{
MsgID: 1,
Control: 1,
Type: Connect,
Type: TypeConnect,
Data: []byte{0x01},
}
hex := f.Bytes().HEX()
@@ -47,7 +47,7 @@ func TestDecode(t *testing.T) {
t.Log(string(UTF8ToGBK(resp.Data[68:])))
t.Log(DecodeConnect(resp.Data))
t.Log(MConnect.Decode(resp.Data))
//result, err := DecodeSecurityList(resp.Data)
//if err != nil {

View File

@@ -1,12 +1,31 @@
package protocol
import "errors"
import (
"errors"
bytes2 "github.com/injoyai/base/bytes"
)
var (
MConnect = connect{}
MSecurityQuote = securityQuote{}
SecurityList = securityList{}
)
type ConnectResp struct {
Info string
}
func DecodeConnect(bs []byte) (*ConnectResp, error) {
type connect struct{}
func (connect) Frame() *Frame {
return &Frame{
Control: Control,
Type: TypeConnect,
Data: []byte{0x01},
}
}
func (connect) Decode(bs []byte) (*ConnectResp, error) {
if len(bs) < 68 {
return nil, errors.New("数据长度不足")
}
@@ -14,6 +33,12 @@ func DecodeConnect(bs []byte) (*ConnectResp, error) {
return &ConnectResp{Info: string(UTF8ToGBK(bs[68:]))}, nil
}
/*
*/
type SecurityListResp struct {
Count uint16
List []*Security
@@ -27,7 +52,17 @@ type Security struct {
PreClose float64
}
func DecodeSecurityList(bs []byte) (*SecurityListResp, error) {
type securityList struct{}
func (securityList) Frame() *Frame {
return &Frame{
Control: 0x01,
Type: TypeConnect,
Data: nil,
}
}
func (securityList) Decode(bs []byte) (*SecurityListResp, error) {
if len(bs) < 2 {
return nil, errors.New("数据长度不足")
@@ -41,18 +76,75 @@ func DecodeSecurityList(bs []byte) (*SecurityListResp, error) {
}
func NewConnect() *Frame {
return &Frame{
Control: Control,
Type: Connect,
Data: []byte{0x01},
}
/*
*/
type SecurityQuotesResp []*SecurityQuote
type PriceLevel struct {
Price float64
Vol int
}
func NewSecurityQuotes(m map[Exchange]string) (*Frame, error) {
type SecurityQuote struct {
Market uint8 // 市场
Code string // 代码
Active1 uint16 // 活跃度
Price float64 // 现价
LastClose float64 // 昨收
Open float64 // 开盘
High float64 // 最高
Low float64 // 最低
ServerTime string // 时间
ReversedBytes0 int // 保留(时间 ServerTime)
ReversedBytes1 int // 保留
Vol int // 总量
CurVol int // 现量
Amount float64 // 总金额
SVol int // 内盘
BVol int // 外盘
ReversedBytes2 int // 保留
ReversedBytes3 int // 保留
BidLevels []PriceLevel
AskLevels []PriceLevel
Bid1 float64
Ask1 float64
BidVol1 int
AskVol1 int
Bid2 float64
Ask2 float64
BidVol2 int
AskVol2 int
Bid3 float64
Ask3 float64
BidVol3 int
AskVol3 int
Bid4 float64
Ask4 float64
BidVol4 int
AskVol4 int
Bid5 float64
Ask5 float64
BidVol5 int
AskVol5 int
ReversedBytes4 uint16 // 保留
ReversedBytes5 int // 保留
ReversedBytes6 int // 保留
ReversedBytes7 int // 保留
ReversedBytes8 int // 保留
Rate float64 // 涨速
Active2 uint16 // 活跃度
}
type securityQuote struct{}
func (this securityQuote) Frame(m map[Exchange]string) (*Frame, error) {
f := &Frame{
Control: Control,
Type: SecurityQuote,
Type: TypeSecurityQuote,
Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
}
@@ -68,3 +160,28 @@ func NewSecurityQuotes(m map[Exchange]string) (*Frame, error) {
return f, nil
}
/*
Decode
b1cb74000c02000000003e05af00af000136020000303030303031320bb2124c56105987e6d10cf212b78fa801ae01293dc54e8bd740acb8670086ca1e0001af36ba0c4102b467b6054203a68a0184094304891992114405862685108d0100000000e8ff320b0136303030303859098005464502468defd10cc005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909
*/
func (this securityQuote) Decode(bs []byte) (*SecurityQuotesResp, error) {
//前2字节是什么?
bs = bs[2:]
number := Uint16(bs[:2])
bs = bs[2:]
for i := uint16(0); i < number; i++ {
sec := SecurityQuote{
Market: bs[0],
Code: string(UTF8ToGBK(bytes2.Reverse(bs[1:7]))),
Active1: Uint16(bs[7:9]),
}
_ = sec
}
return nil, nil
}

View File

@@ -1,6 +1,7 @@
package protocol
import (
"encoding/hex"
"testing"
)
@@ -9,7 +10,7 @@ import (
0c02000000011a001a003e05050000000000000002000030303030303101363030303038
*/
func TestNewSecurityQuotes(t *testing.T) {
f, err := NewSecurityQuotes(map[Exchange]string{
f, err := MSecurityQuote.Frame(map[Exchange]string{
ExchangeSH: "000001",
ExchangeSZ: "600008",
})
@@ -19,3 +20,20 @@ func TestNewSecurityQuotes(t *testing.T) {
}
t.Log(f.Bytes().HEX())
}
func Test_securityQuote_Decode(t *testing.T) {
s := "b1cb74000c02000000003e05af00af000136020000303030303031320bb2124c56105987e6d10cf212b78fa801ae01293dc54e8bd740acb8670086ca1e0001af36ba0c4102b467b6054203a68a0184094304891992114405862685108d0100000000e8ff320b0136303030303859098005464502468defd10cc005bed2668e05be15804d8ba12cb3b13a0083c3034100badc029d014201bc990384f70443029da503b7af074403a6e501b9db044504a6e2028dd5048d050000000000005909"
bs, err := hex.DecodeString(s)
if err != nil {
t.Error(err)
return
}
f, err := Decode(bs)
if err != nil {
t.Error(err)
return
}
t.Log(hex.EncodeToString(f.Data))
//SecurityQuote.Decode(f.Data)
}

View File

@@ -9,6 +9,10 @@ import (
"io"
)
func String(bs []byte) string {
return string(bytes2.Reverse(bs))
}
func Bytes(n any) []byte {
return bytes2.Reverse(conv.Bytes(n))
}
@@ -27,3 +31,42 @@ func UTF8ToGBK(text []byte) []byte {
content, _ := io.ReadAll(decoder)
return bytes.ReplaceAll(content, []byte{0x00}, []byte{})
}
func getprice(b []byte, pos *int) int {
/*
0x7f与常量做与运算实质是保留常量转换为二进制形式的后7位数既取值区间为[0,127]
0x3f与常量做与运算实质是保留常量转换为二进制形式的后6位数既取值区间为[0,63]
0x80 1000 0000
0x7f 0111 1111
0x40 100 0000
0x3f 011 1111
*/
posByte := 6
bData := b[*pos]
data := int(bData & 0x3f)
bSign := false
if (bData & 0x40) > 0 {
bSign = true
}
if (bData & 0x80) > 0 {
for {
*pos += 1
bData = b[*pos]
data += (int(bData&0x7f) << posByte)
posByte += 7
if (bData & 0x80) <= 0 {
break
}
}
}
*pos++
if bSign {
data = -data
}
return data
}