[C#,Python]WebSocketを試してみる。その3。

チャットソフト続き。

MessagePackデータの送受信部
その2の記事でデータモデルを作成しましたが、デタラメ書きました。
MessagePackのシリアライズはシリアライズ可能な型であれば、C#であろうが、Pythonであろうが構わないので、なんでもかんでもbyte[]にする必要はなかったのであります。

次のデータクラスのやりとりを考えます。

        /// <summary>
        /// 送信データヘッダー部
        /// </summary>
        public class CmdInfo : ExtensionsClass.ExtensionsClass.IMessage
        {
            /// <summary>
            /// MessagePackにpackするときに、変数の並びが辞書順に並び替えられるらしいので、変数名に注意する。
            /// </summary>
            public int _1CMDNO { get; set; }
            public string _2GUID { get; set; }
            public string _3TIME { get; set; }
            public byte[] _4SENDDATA { get; set; }
        }
        /// <summary>
        /// チャットメッセージ
        /// </summary>
        public class MessageInfo : ExtensionsClass.ExtensionsClass.IMessage
        {
            public int _1COLOR { get; set; }
            public string _2MESSAGESTRING { get; set; }
        }

※尚、C#のMessagePack実装ライブラリはシリアライズ処理時にクラスのプロパティ名を辞書順に並び替えてシリアライズしているようなので、辞書順のプロパティ名にしてあります。
※object <-> MessagePackの拡張メソッドをExtensionsClass.ExtensionsClass.IMessageインタフェースに適用しています。
C#送信部分ソース

            /// ヘッダーデータの生成
            var cmdif = new Interfaces.CmdInfo();
            cmdif._1CMDNO = (int)cmd;  /// cmd = 0x16とします。
            cmdif._2GUID = _guid.ToString();
            cmdif._3TIME = "";
                    
            var data = new Interfaces.MessageInfo();
            data._1COLOR = pictureBoxStringColor.BackColor.ToArgb();
            data._2MESSAGESTRING = textBoxMessage.Text;
            cmdif._4SENDDATA = data.Object2MessagePack();    /// byte[]メンバに自分で定義したクラスのMessagePackデータを載せる

            /// 送信データの作成
            var msgpackData = cmdif.Object2MessagePack();    /// 最終的にはbyte[]を生成させる

            /// ソケット送信
            _ws.SendMessage(msgpackData);

C#側送信データは各処理に応じてCmdInfo データクラスの_4SENDDATAに自前データのバイト配列を入れ子にしてサーバに送信します。
これにより、_4SENDDATAには色々な構造のデータを載せることが可能になります。

Python受信ソース

msg = ws.receive()
        
print "Receive Message"
        
if msg is None:
   break
        
msgpack_msg = msgpack.unpackb(msg)
        
cmd = msgpack_msg[0]
guid = msgpack_msg[1]
        
print "\tcmd = " + str(cmd)
print "\tmsg len = " + str(len(msg))

Python側受信部では受信時はbytearray型になり、MessagePackでデシリアライズするとstr型の配列として扱われます。
strにはunicode形式で入っています(確か)。このサーバは通信データの中身はコマンド番号ぐらいしか解析しないので良いのですが、str型のデータを操作する場合はちょっとめんどっちぃです。

Python側送信ソース

        sendmsg = []
        sendmsg.append(cmd)
        sendmsg.append(guid)
        sendmsg.append(timestr)
        sendmsg.append(msgpack_msg[3])
        sendmsg_pack = msgpack.packb( sendmsg )
        
        sendMessage(ws, str2bytearray(sendmsg_pack), SENDALL)

str2bytearray変換関数

def str2bytearray(strData):
    barray = bytearray()
    for x in strData:
        barray.append(x)
    return barray

sendMessage関数

def sendMessage(currentWs, msg, target, delclient = True):
    
    print "Send Message"
    
    delSockets = []
    
    i = 0
    memlen = len(_clientSockets)
    
    print "\tsend msg len  =" + str(len(msg))
    print "\tsend user len =" + str(memlen)
    
    while i < memlen:
        ws = _clientSockets[i]
        current = currentWs == ws
        
        if current == True and target == SENDOTHER:
            pass
        elif current == False and target == SENDONE:
            pass
        else:
            try:
                ws.send(msg)
            except:
                print "\tsend except"
                delSockets.append(ws)
            finally:
                if target == SENDONE:
                    break
        i += 1
    
    if delclient == False:
        return
    
    for x in delSockets:
        if _userInfo.has_key( id(x) ):
        
            sendmsg = []
            sendmsg.append(CMDLOGOUT)
            sendmsg.append( _userInfo[id(x)] )
            sendmsg.append( getTimeStr() )
            sendmsg_pack = msgpack.packb( sendmsg )
            sendMessage(x,str2bytearray(sendmsg_pack), SENDOTHER, False)
            
        remove_member(x)

ここでの送信処理は受信したメッセージに時間情報(getTimeStr()は時間情報の文字列を返す)を付加し、各クライアントに送信しています。
重要なポイントはmsgpack.packb()はstr型を返しますが、ソケット送信時はバイナリ(bytearray()型)でないと、クライアントが文字列としてデータを受信してしまうので、送信時はbytearray()にデータを変換してあげます。

C#側受信ソース

            /// ソケット受信時操作
            byte[] rcvData = e.BinaryData;    // ソケット受信データ

            /// コマンドヘッダー部取得
            var cmdif = rcvData.MessagePack2Object<Interfaces.CmdInfo>();

            string rcvMsg =
                string.Format("RcvCmd[{0}],GUID[{1}],TIME[{2}]",
                    Enum.GetName(typeof(Interfaces.COMMAND), cmdif._1CMDNO),
                    cmdif._2GUID,
                    cmdif._3TIME);
            
            Trace.WriteLine(rcvMsg);

          /// データ部分解
          var user = cmdif._4SENDDATA.MessagePack2Object<Interfaces.UserInfo>();

C#でのMessagePackデータの受信は拡張メソッドにより、簡単に解析可能な形になります。

その4へ続く。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


This blog is kept spam free by WP-SpamFree.