unity客户端与java服务器利用thrift通信初试

背景

公司做的是手游,用的是unity客户端语言为c#,服务器为java,客户端已基本做完,服务器刚招的人

第一天

一开始服务器给了个ip和port,我就开始连接,我直接用原生socket直接socket.Send(byte[] a),发现不管怎么发对方都接受不到,于是开始想不会它那边没开吧,于是ping ip,发现有数据包说明是通的,可是怎么判断判断给我的端口是否可用,在同事的帮助下,在控制面板中的打开或关闭Windows功能中打开telnet客户端,于是telnet ip port,有返回值,奇怪了。
在接下来的一天中我试了在我代码中找问题,可是一无所获,接下来放清明了。

第二天

一开始老板开会说我这里要抓紧,赶紧弄,给我四天接好,心中呵呵。

服务器端说他那边一切正常,用模拟的可以跑通,我是在无语了,接下来他说他那边接收到是个NetMessage消息体,而且只能接受这个。然后我去看了消息体

[Serializable]
    public class NetMessage
    {
        private IMessageType messageType;
        private byte[] body;
        public IMessageType getMessageType()
        {
            return messageType;
        }
        public void setMessageType(IMessageType messageType)
        {
            this.messageType = messageType;
        }

        public byte[] getBody()
        {
            return body;
        }
        public void setBody(byte[] body)
        {
            this.body = body;
        }
    }

我现在要发送一个类过去,这个类继承自thrift中的TBase,这个不用看,它是thrift自动生成的

public partial class CS_Login : TBase
{
  private string _userName;
  private string _password;
  private LoginType _loginType;

  public string UserName
  {
    get
    {
      return _userName;
    }
    set
    {
      __isset.userName = true;
      this._userName = value;
    }
  }

  public string Password
  {
    get
    {
      return _password;
    }
    set
    {
      __isset.password = true;
      this._password = value;
    }
  }

  /// <summary>
  /// 
  /// <seealso cref="LoginType"/>
  /// </summary>
  public LoginType LoginType
  {
    get
    {
      return _loginType;
    }
    set
    {
      __isset.loginType = true;
      this._loginType = value;
    }
  }


  public Isset __isset;
  #if !SILVERLIGHT
  [Serializable]
  #endif
  public struct Isset {
    public bool userName;
    public bool password;
    public bool loginType;
  }

  public CS_Login() {
  }

  public void Read (TProtocol iprot)
  {
    TField field;
    iprot.ReadStructBegin();
    while (true)
    {
      field = iprot.ReadFieldBegin();
      if (field.Type == TType.Stop) { 
        break;
      }
      switch (field.ID)
      {
        case 1:
          if (field.Type == TType.String) {
            UserName = iprot.ReadString();
          } else { 
            TProtocolUtil.Skip(iprot, field.Type);
          }
          break;
        case 2:
          if (field.Type == TType.String) {
            Password = iprot.ReadString();
          } else { 
            TProtocolUtil.Skip(iprot, field.Type);
          }
          break;
        case 3:
          if (field.Type == TType.I32) {
            LoginType = (LoginType)iprot.ReadI32();
          } else { 
            TProtocolUtil.Skip(iprot, field.Type);
          }
          break;
        default: 
          TProtocolUtil.Skip(iprot, field.Type);
          break;
      }
      iprot.ReadFieldEnd();
    }
    iprot.ReadStructEnd();
  }

  public void Write(TProtocol oprot) {
    TStruct struc = new TStruct("CS_Login");
    oprot.WriteStructBegin(struc);
    TField field = new TField();
    if (UserName != null && __isset.userName) {
      field.Name = "userName";
      field.Type = TType.String;
      field.ID = 1;
      oprot.WriteFieldBegin(field);
      oprot.WriteString(UserName);
      oprot.WriteFieldEnd();
    }
    if (Password != null && __isset.password) {
      field.Name = "password";
      field.Type = TType.String;
      field.ID = 2;
      oprot.WriteFieldBegin(field);
      oprot.WriteString(Password);
      oprot.WriteFieldEnd();
    }
    if (__isset.loginType) {
      field.Name = "loginType";
      field.Type = TType.I32;
      field.ID = 3;
      oprot.WriteFieldBegin(field);
      oprot.WriteI32((int)LoginType);
      oprot.WriteFieldEnd();
    }
    oprot.WriteFieldStop();
    oprot.WriteStructEnd();
  }

  public override string ToString() {
    StringBuilder sb = new StringBuilder("CS_Login(");
    sb.Append("UserName: ");
    sb.Append(UserName);
    sb.Append(",Password: ");
    sb.Append(Password);
    sb.Append(",LoginType: ");
    sb.Append(LoginType);
    sb.Append(")");
    return sb.ToString();
  }
}

首先要把这个类序列化为byte[]类型,我请求看看服务器端的序列化工具,得到的回答是apache.thrift.TSerializer.serialize(TBase base),我问有c#客户端的没有,说没有,我去网上查,得知thrift是facebook开发的可伸缩的跨语言服务开发框架,我查啊查,找到dem0(http://www.cnblogs.com/liping13599168/archive/2011/09/15/2176836.html),可是缺少dll,我自己下了个工程把里面的dll导出到我的项目中,发送过去对方还是接受不到,我没办法了。

第三天

我又开始怀疑服务器了,于是他另开了个服务器,这次可以接收到的是byte[],我试着用原生看的socket发,也没有收到值,疯了,看了网上有关java与c#通信的注意事项后,在所发字符串后加了“/n”换行符,这次终于接受到了,总之这次是我搞过做蛋疼的事了。那么问题来了,之前的那台服务器也是因为同样的问题么,试过之后还是不行。
接下来我受不了服务器了,我要求服务器告诉我,你是怎么把NetMessage发出去的,于是他去翻看框架的东东了,我已经不想搞了,菜鸟对菜鸟,连你妹啊,说好的有人搭框架的,说好你妹的。
接下来服务器告诉我,要先发NetMessage的为int类型的type,再发body,我和他商量觉得我先来接受他的东东,我只要能解出来,在反着来就ok,
这次我从字节开始读,我不信还不行,于是
byte[] bytes = new byte[4096];
                    int rev = socket.Receive(bytes);

 public NetMessage Read(byte[] bytes)
        {
            byte[] lengthBytes = SubByteArray(bytes, 0, 4);
            byte[] headBytes = SubByteArray(bytes, 4, 4);
            
            Debug.Log("lengthBytes==" + BitConverter.ToInt32(lengthBytes, 0));
            Debug.Log("headBytes==" + BitConverter.ToInt32(headBytes, 0));

            int bodyLength = BitConverter.ToInt32(lengthBytes, 0) - 4;
            byte[] bodyBytes = SubByteArray(bytes, 8, bodyLength);           
           
            NetMessage netMessage = new NetMessage();
            netMessage.setMessageType((IMessageType)BitConverter.ToInt32(headBytes, 0));
            netMessage.setBody(bodyBytes);
            return netMessage;
        }

起初这个读的方法,解出来的数字不对,于是我打印了每一个byte和服务器的对,发现它那边竟然有负数,我这边的没有,问题就出在这个地方?我查了发现java的int32为有符号的整数,而c#中为无符号整数,于是

if (BitConverter.IsLittleEndian) {
                Array.Reverse(headBytes);
                Array.Reverse(lengthBytes);
}

搞定,搞定的只是数据头,读到的32,而不在type正常的取值范围,我交抢了,你妈玩我呢,服务器又一次开始看底层这次我和他一起,发现正确的格式应该是length+type+body,这次就解释通了,可我怎么把body解出来呢,反序列话工具怎么搞,我试啊试的又一天结束了。

第四天

我想不就是一个发序列化工具么,我看看能不能把java的jar转为c#的dll,发现网上竟然有可行的办法,我去试,导出的dll,序列化的方法apache.thrift.TSerializer.serialize(TBase base),与我这边c#的TBase竟不是同一个基类,我觉得可能是命名空间的问题,可是仔细一看发现不是,c#中的TBase只是apache.thrift.TBase得子集。这时我不干了,我干不了了,我能力有限,我毕业不到一年,更是缺少底层经验。
后来发现根本没人在乎我会不会,我只要做就好了。
我在服务器的指导下去看thrift框架中java是如何序列化和反序列化的,发现要解出来必须依赖于框架中的方法,我自己必须用thrift的通信框架。
我重新换回thrift通讯框架,但是我不知道,怎么解,于是又遇到了之前的问题,那个读的方法是不是服务器那边写好的,自动生成的呢,我再一次去看上面的demo,想照着他的方法,自己写一个读的方法(这时思路就错了,demo中直接对数据读写,而我们这里对数据进行了包装),我试到天黑也没试出来。

第五天

服务器那边理清了,受不了我这边了,开始帮我研究客户端,终于让他写出了读的方法,我读出来了。
public class MyTProtocol : TProtocolDecorator
    {

        private TProtocol tprotocol;

        public MyTProtocol(TProtocol tprotocol) : base(tprotocol){
            this.tprotocol = tprotocol;
        }
    }

TProtocolDecorator是个抽象类,于是自己写个MyTProtocol来调用TProtocolDecorator中的读写方法来操作TProtocol,得到TProtocol中的数据, 看完这个我万分惭愧,自己的东东让服务器的来搞定,于是自己去看thrift框架,看了半天还只是会用而已,只知道了个大概,想想也是人家写的时候肯定时间不短。
该自己序列化了,去研究了对应java中的方法,发现要先得到一个TProtocol把继承子TBase中的各种类线写入 TBase.write(TProtocol),然后再从TProtocol中得到byte[]
   public class TSerializer
    {
       private TProtocol protocol_;

       private static MemoryStream InputStream = new MemoryStream();

       private static MemoryStream OutputStream = new MemoryStream();

        private TStreamTransport transport = new TStreamTransport(InputStream, OutputStream);

       public TSerializer(){
           TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();
           protocol_ = factory.GetProtocol(transport);
       }


        public byte[] serialize(TBase mBase){

            transport.Open ();

            byte[] bytes = null;

            mBase.Write(protocol_);

            int byteLength = (int)OutputStream.Length;

            bytes=new byte[byteLength];


            OutputStream.Position = 0;
            int Count= OutputStream.Read(bytes, 0, byteLength);

            transport.Close ();    

            return bytes;
        }
}

问题还在,服务器无法解析body部分。

第六天

我试了各种方式,还是无法解析,得到的结果是body部分的byte[]有错位,他读到的body前多了个body的长度,其他的都对。

怎么找也找不到,终于我的unity每次运行就卡死,不知所踪,后来发现只要一读int i = tpd.ReadI32();就阻塞,有是半天时间找问题,后来发现服务器把原来发给我的数据停掉了,呵呵,终于体会到阻塞的威力。

过了会服务器小哥不干了,说搞不定,辞职了,不到两周时间。

总结

再失败也应有所收获
1、java端与c#通信在发送字符串的时候,如果读不到加个“/n”
2、telnet客户端检测远程ip的端口
3、java断与c#通信在发送字符串的时候,注意int类型,两种语言不同
4、很多时候看源码(分析问题)很容易找到解决问题的方式
5、有一个猪一样的老板兼上司,不愁项目不死,是时候找下家了
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值