学习总结,转自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对象。