• 周五. 12月 2nd, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

已知服务端数据包格式,客户端怎样用Java实现向服务器端(C#写的)接收和发送数据包(其他同理)

admin

11月 28, 2021

学习总结,转自http://bbs.csdn.net/topics/380199399

基于 TCP/IP协议

问:

请问客户端怎样用Java实现向服务器端(C#写的)接收和发送数据包(及客户端数据包怎么做),服务器端数据包如下:

复制代码
struct PacketHeader
{
/// 标志位
short    PacketFlag;

//Packet Length
/// 消息包长度(消息包头)
short    Length;
//Packet ID
union{
/// 消息ID
int     PacketID;//为了方便和别的程序通讯,消息id还是采用枚举定义,在编译期确定
/// 类型(0~9系统预留)
int     MsgType;
};
//CheckSum
/// 序列号
int     SerialNumber;
//Client or DispatcherID (special use)
union{
/// DispatcherID (special use)
int     DispatcherID;
struct {
short FrontEndID;
short ClientID;//On FrontEnd
};
};

PacketHeader()
:PacketFlag(PacketFlag_Normal)
,Length(sizeof(PacketHeader))
,PacketID(0)
,SerialNumber(0)
,DispatcherID(0)
{

}
public:
/// 消息包头的长度
static const int MsgHeaderLength = 16;//16
/// 处理 Msg 所需的最小长度
static const int MsgMustInfoLength = 4;//(ushort + ushort)
/// 最大Msg长度(包括消息包头)
static const int Max_Msg_Size = 32000;//<32K
/// 最小压缩长度
static const int Min_Compress_Size = 100;
/// 分割消息包的大小
static const int SubPacketLength = 1024;//分割消息包的大小


/// 添加标志位
void   AddFlag(enumMsgFlag flag) {PacketFlag |= flag;}
/// 获得消息类型
int    GetType(){return this->PacketID; }
/// 获得消息包长度
ushort GetLength(){return this->Length; }
};


/// 二进制消息包
struct NGA_CoreLib_Export Msg : public PacketHeader
{
/// 将Msg转换为Packet
/**
使用 TypedTLSMemory 保证线程安全
*/
static Packet* ToPacket(Msg*);//线程安全
/// 将Msg转换为Packet
/**
使用 TypedTLSMemory 保证线程安全
*/
Packet* ToPacket();//线程安全



};

struct SSLMSGSendPublicKey:public Msg
{
SSLMSGSendPublicKey()
{
this->PacketID = MSG_SSLSendPublicKey;
this->Length = sizeof(*this);
int keylen = sizeof(KeyData);
EncryptFlag = PacketFlag_Compress_Need_Checksum;
for(int i=0;i<keylen;i++){
BYTE temp = BYTE(rand()%256);
KeyData[i] = temp;

}
}
uint32 EncryptFlag;
BYTE  KeyData[128];
};

msg = buf[]


msg =   msg.id 
复制代码

回复:

TCP 的异构系统通信,只能通过字节流进行传递。
服务器端(C#写的)的数据结构是不能被JAVA客户端识别的。
在服务器端C#将数据结构转换成字符串,然后JAVA端通过输入流获取socket的字节码。
如:
                Socket socket=new Socket(“127.0.0.1”,4700);
        //向本机的4700端口发出客户请求
        BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
        //由系统标准输入设备构造BufferedReader对象
        PrintWriter os=new PrintWriter(socket.getOutputStream());
        //由Socket对象得到输出流,并构造PrintWriter对象
        BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //由Socket对象得到输入流,并构造相应的BufferedReader对象
        String readline;
        readline=sin.readLine(); //从系统标准输入读入一字符串
        while(!readline.equals(“bye”)){
        //若从标准输入读入的字符串为 “bye”则停止循环
          os.println(readline);
          //将从系统标准输入读入的字符串输出到Server
          os.flush();
          //刷新输出流,使Server马上收到该字符串
          System.out.println(“Client:”+readline);
          //在系统标准输出上打印读入的字符串
          System.out.println(“Server:”+is.readLine());
          //从Server读入一字符串,并打印到标准输出上
          readline=sin.readLine(); //从系统标准输入读入一字符串
        } //继续循环
        os.close(); //关闭Socket输出流
        is.close(); //关闭Socket输入流
        socket.close(); //关闭Socket


JAVA只能获取服务器C#端返回的字节流,然后通过字符串转换成JAVA对象。
对象数据格式转换字符串的方法,可以参照Json格式。也可以在服务器C#端将数据格式转换成XML格式的字符串。JAVA客户端调用后将XML格式的字符串解析成JAVA对象。

问:

首先谢谢你的回答,我也想到用数据流来操作了,但是服务器端现在写好的好像没有数据流相关操作,让我用数据包的方式跟服务器通信。服务器数据包代码如下:

复制代码
    /// <summary>
    /// 消息包
    /// 内存布局
    /// {
    ///     short  PacketFlag;//特殊的值
    ///     short  PacketLength;//消息包的大小,消息不能大于32k
    ///     int    PacketID;//消息包类型id;从这以后的数据可以加密
    ///     int    SerialNumber;//消息包序列号,网络连接每发一个消息包,这个值+1
    ///     int    DispatcherID;//发送者的id
    ///                         //所以消息头的大小是2+2+4+4+4 = 16
    /// }
    /// </summary>
    public class Packet
    {
        public const short MaxLength = 32000;//32k
        public const short InvalidLength = -1;
        public const short NeedCompressMinLength = 100;
        public const short NoCryptHeaderLength = 4; // 2+2

        //消息包标志
        public PacketFlag PacketFlag = PacketFlag.Normal;

        public  int PacketID;

        /// <summary>
        /// 消息序列号
        /// </summary>
        public int  SerialNumber;
        public int  DispatcherID;//if id <0 ,is -serverid
protected   PacketWriter m_writer;

        public Packet(int packetID)
:this(packetID,0)
        {
            
        }

public Packet(int packetID,int dispatcherid)
{
PacketID = packetID;
DispatcherID = dispatcherid;
            m_writer = new PacketWriter();/*PacketWriter.CreateInstance()*/;
}


        private byte[] m_databuffer; //已经有的数据的缓冲
        private int m_datalength; //已经有的数据的大小
        /// <summary>
        /// 消息总长度
        /// </summary>
        public int Length
        {
            get{
                if (m_databuffer == null)
                    return Packet.HeaderSize + m_writer.Length;
                else
                    return m_datalength + m_writer.Length;
            }
            set{
                if (m_databuffer == null)
                    return;
                m_datalength = value;
            }
        }

        internal byte[] DataBuffer{
            get{
                return m_databuffer;
            }
            set{
                m_databuffer = value;
                if (m_databuffer != null)
                    m_datalength = m_databuffer.Length;
            }
        }

public PacketWriter Writer{
get{
return m_writer;
}
}

        internal virtual byte[] ToArray()
        {
            short packet_length = (short)this.Length;
            bool needcreatebuffer = false;
            if (DataBuffer == null || m_writer.Length > 0)
                needcreatebuffer = true;
            if (needcreatebuffer)
            {
                byte[] newbuffer = new byte[packet_length];
                if (m_databuffer != null)
                {
                    Buffer.BlockCopy(m_databuffer, 0, newbuffer, 0, m_datalength);
                }
                m_databuffer = newbuffer;
            }
            //写消息头
            //ArrayUtility.SetShort(m_databuffer, (short)PacketFlag, 0);
            //ArrayUtility.SetShort(m_databuffer, packet_length, 2);
            //ArrayUtility.SetInt(m_databuffer, this.PacketID, 4);
            //ArrayUtility.SetInt(m_databuffer, this.SerialNumber, 8);
            //ArrayUtility.SetInt(m_databuffer, this.DispatcherID, 12);

            WriteHead();

            if (needcreatebuffer && m_writer.Length > 0)
            {
                System.Buffer.BlockCopy(m_writer.ToArray(), 0, m_databuffer,
                    packet_length - m_writer.Length, m_writer.Length);
                m_writer.Length = 0;
            }
            m_datalength = packet_length;

            return m_databuffer;
        }

        protected void WriteHead()
        {
            //写消息头
            ArrayUtility.SetShort(m_databuffer, (short)PacketFlag, 0);
            ArrayUtility.SetShort(m_databuffer, (short)this.Length, 2);
            ArrayUtility.SetInt(m_databuffer, this.PacketID, 4);
            ArrayUtility.SetInt(m_databuffer, this.SerialNumber, 8);
            ArrayUtility.SetInt(m_databuffer, this.DispatcherID, 12);
        }

        /// <summary>
        /// 消息头大小 2+2+4+4+4 = 16
        /// </summary>
        public static int HeaderSize
        {
            get
            {
                return 16;
            }
        }

        public void Reset()
        {
            DataBuffer = null;
            Writer.Clear();
        }

    }
复制代码


请问我用数据包的方式要怎样做呢?

如果不加包报头直接发数据流过去的话,服务器端解析出来不对。。。

回复:

public class Packet这个数据包对象是C#里面定义的,客户端当然无法解析。我的意思是用字符串来表示,如json格式的字符串,如:
“packet{MaxLength:32000;InvalidLength:-1;NeedCompressMinLength:100;NoCryptHeaderLength:4…}”
JAVA客户端系统拿到这个字符串后,通过字符串处理,把它转换成JAVA对象。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注