import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
/**
* MDC VSS连接客户端
* Vendor Supplied System 接入用户服务器(简称“VSS”)
*/
public class SjsVss {
/**
* 登录消息
*
* @return
*/
private static byte[] getLogonMsg() {
byte[] msgTypeArr = toHH(1);
byte[] msgLengthArr = toHH(20 + 20 + 4 + 16 + 32);
byte[] senderIDArr = getLogonBytes("VSS", 20);
byte[] targetIDArr = getLogonBytes("VDE", 20);
byte[] xintiao = toHH(30);//心跳
byte[] mimaArr = getLogonBytes("", 16); //api模式才填, vss模式空着
byte[] xieyiArr = getLogonBytes("1.02", 32);
byte[] msgHeadBody = byteMerger(msgTypeArr, msgLengthArr);
msgHeadBody = byteMerger(msgHeadBody, senderIDArr);
msgHeadBody = byteMerger(msgHeadBody, targetIDArr);
msgHeadBody = byteMerger(msgHeadBody, xintiao);
msgHeadBody = byteMerger(msgHeadBody, mimaArr);
msgHeadBody = byteMerger(msgHeadBody, xieyiArr);
byte[] check = toHH(GenerateCheckSum(msgHeadBody));
byte[] r = byteMerger(msgHeadBody, check);
return r;
}
private static byte[] hexStringToByteArr(String s) {
if (s == null || s.equals("")) {
return null;
}
s = s.replace(" ", "");
byte[] baKeyword = new byte[s.length() / 2];
for (int i = 0; i < baKeyword.length; i++) {
try {
baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
} catch (Exception e) {
e.printStackTrace();
}
}
return baKeyword;
}
/**
* 发送数据转换时按高字节序列转换
* int 转 byte[] 高字节在前(高字节序)
*
* @param n
* @return
*/
public static byte[] toHH(int n) {
byte[] b = new byte[4];
b[3] = (byte) (n & 0xff);
b[2] = (byte) (n >> 8 & 0xff);
b[1] = (byte) (n >> 16 & 0xff);
b[0] = (byte) (n >> 24 & 0xff);
return b;
}
/**
* byte数组合并
*
* @param bt1
* @param bt2
* @return
*/
public static byte[] byteMerger(byte[] bt1, byte[] bt2) {
byte[] bt3 = new byte[bt1.length + bt2.length];
System.arraycopy(bt1, 0, bt3, 0, bt1.length);
System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
return bt3;
}
private static byte[] getLogonBytes(String s, int len) {
byte[] vssCs = s.getBytes();
byte[] senderIDArr = new byte[len];
for (int i = 0; i < senderIDArr.length; i++) {
senderIDArr[i] = 32;//空的字符串默认必须填 32(空格)
}
System.arraycopy(vssCs, 0, senderIDArr, 0, vssCs.length);
return senderIDArr;
}
/**
* byte数组转hex
*
* @param bytes
* @return
*/
public static String byteToHex(byte[] bytes) {
String strHex = "";
StringBuilder sb = new StringBuilder("");
for (int n = 0; n < bytes.length; n++) {
strHex = Integer.toHexString(bytes[n] & 0xFF);
sb.append((strHex.length() == 1) ? "0" + strHex : strHex); // 每个字节由两个字符表示,位数不够,高位补0
}
return sb.toString().trim();
}
/**
* 心跳消息
*
* @return
*/
private static byte[] getHeartbeatMsg() {
byte[] msgTypeArr = toHH(3);
byte[] msgLengthArr = toHH(0);
byte[] headAndLenArr = byteMerger(msgTypeArr, msgLengthArr);
byte[] check = toHH(GenerateCheckSum(headAndLenArr));
byte[] r = byteMerger(headAndLenArr, check);
return r;
}
/**
* 生成消息尾校验
*
* @param bytes
* @return
*/
public static int GenerateCheckSum(byte[] bytes) {
int sum = 0;
for (int b : bytes) {
sum += (int) b;
}
return sum % 256;
}
/**
* 读数据线程处理
*/
static class ReadThread extends Thread {
InputStream readStream;
public ReadThread(InputStream readStream) {
this.readStream = readStream;
}
@Override
public void run() {
try {
while (true) {
//消息类型
byte[] msgTypeBs = new byte[4];
readStream.read(msgTypeBs, 0, msgTypeBs.length);
int msgTypeInt = ByteBuffer.wrap(msgTypeBs).getInt();
//消息长度
byte[] msgLenBs = new byte[4];
readStream.read(msgLenBs, 0, msgLenBs.length);
int msgLengthInt = ByteBuffer.wrap(msgLenBs).getInt();
//消息体
byte[] msgBodyBs = new byte[msgLengthInt];
readStream.read(msgBodyBs, 0, msgBodyBs.length);
//消息尾,校验
byte[] msgCheckBs = new byte[4];
readStream.read(msgCheckBs, 0, msgCheckBs.length);
/**
* 根据不同类别解析消息体
* 表 4-13-0 快照行情数据类别列表
* 行情类别
* MDStreamID
* 说明 消息类型 有无扩展
* 字段
* 010 现货(股票,基金等)集中竞价交易快照行情 300111 Y
* 040 期权集中竞价交易快照行情
* 020 债券通用质押式回购匹配交易快照行情 300211 Y
* 030 债券分销快照行情
* 410 债券现券交易快照行情
* 060 以收盘价交易的盘后定价大宗交易快照行情 300611 Y
* 061 以成交量加权平均价交易的盘后定价大宗交
* 易快照行情
* 工程技术标准 深圳证券交易所 Binary 行情数据接口规范
* 第 16 页 共 37 页
* 370 盘后定价交易快照行情 303711 Y
* 630 港股实时行情 306311 Y
* 900 指数快照行情 309011 Y
* 910 成交量统计指标快照行情 309111 Y
* 920 国证指数快照行情 309011 Y
*/
System.out.println("msgTypeInt:" + msgTypeInt);
System.out.println("msgLengthInt:" + msgLengthInt);
System.out.println("msgBody:" + new String(msgBodyBs));
if (msgTypeInt == 1) {
//返回登录消息,啥也不干
} else if (msgTypeInt == 300111) {
/**
* 4.5.4 快照行情
* 表 4-13 MDGW 发布快照行情消息定义
* 域名 字段描述
* Standard Header
* 消息头
* MsgType=3xxx11
* OrigTime 数据生成时间 int64
* ChannelNo 频道代码 int16
* MDStreamID 行情类别 char[3]
* SecurityID 证券代码 char[8]
* SecurityIDSource 证券代码源 char[4]
* TradingPhaseCode 产品所处的交易阶段代码 char[8]
* PrevClosePx 昨收价
* NumTrades 成交笔数
* TotalVolumeTrade 成交总量
* TotalValueTrade 成交总金额
* Extend Fields 各业务扩展字段
*/
{
//数据生成时间 int64
byte[] OrigTimeArr = new byte[8];
System.arraycopy(msgBodyBs, 0, OrigTimeArr, 0, OrigTimeArr.length);
long OrigTime = ByteBuffer.wrap(OrigTimeArr).getLong();//20211118085506000
System.out.println(OrigTime);
}
{
//ChannelNo 频道代码 int16
/**
* 集中竞价交易
* 快照行情
* 股票 101x 行情快照消息(300111)
* 基金 102x
* 可转债 103x
* 权证 104x
* 期权 105x
* 集中竞价交易
* 逐笔行情
* 股票 201x 逐笔委托消息(300192)
* 逐笔成交消息(300191) 基金 202x
*/
byte[] ChannelNoArr = new byte[2];
System.arraycopy(msgBodyBs, 8, ChannelNoArr, 0, ChannelNoArr.length);
short ChannelNo = ByteBuffer.wrap(ChannelNoArr).getShort();
System.out.println(ChannelNo);
}
{
//MDStreamID 行情类别 char[3]
byte[] MDStreamIDArr = new byte[3];
System.arraycopy(msgBodyBs, 8 + 2, MDStreamIDArr, 0, MDStreamIDArr.length);
String MDStreamID = new String(MDStreamIDArr, "UTF-8");//010 现货(股票,基金等)集中竞价交易快照行情
System.out.println(MDStreamID);
}
{
//SecurityID 证券代码 char[8]
byte[] SecurityIDArr = new byte[8];
System.arraycopy(msgBodyBs, 8 + 2 + 3, SecurityIDArr, 0, SecurityIDArr.length);
String SecurityID = new String(SecurityIDArr, "UTF-8");
System.out.println(SecurityID);
}
{
/**
* SecurityIDSource 证券代码源 char[4]
* 证券代码源
* 102=深圳证券交易所
* 103=香港交易所
*/
byte[] SecurityIDSourceArr = new byte[4];
System.arraycopy(msgBodyBs, 8 + 2 + 3 + 8, SecurityIDSourceArr, 0, SecurityIDSourceArr.length);
String SecurityIDSource = new String(SecurityIDSourceArr, "UTF-8");//
System.out.println(SecurityIDSource);
}
} else {
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
public static void main(String[] args) {
try {
// {
// var socket = new Socket("192.168.20.11", 9230);
// var outStream = socket.getOutputStream();
// outStream.write(getLogonMsg());
// outStream.flush();
// }
//发登录消息
Socket socket = new Socket("192.168.20.11", 9130);
OutputStream outStream = socket.getOutputStream();
outStream.write(getLogonMsg());
outStream.flush();
//线程读消息
ReadThread readThread = new ReadThread(socket.getInputStream());
readThread.start();
//定时发心跳
while (true) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
outStream.write(getHeartbeatMsg());
outStream.flush();
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
深圳证券交易所Binary行情数据,MDC_VSS_DEMO数据接收示例代码
于 2023-05-18 17:46:08 首次发布