Development diary: kbengine+unity+php to be a poker game-day2

Source: Internet
Author: User
Tags ack message queue pack

What to do, this is very embarrassing, why, because KBE for some reason let me give up the use of it so I intend to continue to update, say the reason:

In DAY1 I want KBE to be able to open an HTTP service and have the PHP side make a Web request to pass the message to the corresponding user, but this HTTP service I wrote, the function that sent the message is also written (spent a lot of time, KBE comments and documents are not many, In particular, KBE basehttpserver this Python library with another name, with Http.server import as only imported success) embarrassed is the HTTP service and the function of sending messages can not be put together:

1. Once a class does not inherit from Kbengine.base, then he cannot access almost all of Kbengine's static functions, properties, can not get to the corresponding user's mailbox completion message sent

2. Once you inherit kbegine.base, you can not do HTTP service, because your handler must inherit Basehandler, you can not inherit, And even if you go to Basehandler to visit Kbegine.base's mailbox and back to the dead logic.

3. The System Library HTTP service will block the process, this document is still written, but the replacement framework is too cumbersome, and debugging is too inconvenient, and the syntax is too familiar, and ... Although I would like to say 10,000 and, can only explain my incompetence ah ...

Of course, there are people in the Forum and the official website to respond to similar problems, such as third-party interface access to KBE member/attribute issues, but it seems that there is no ready-made solution, and finally ... I gave up.


And then I honestly wrote a message server (socket-based, with WPF. NET 4.5+) and message Protocol

The message protocol uses http://msgpack.org/basically support all languages, so in fact I this message server can serve any kind of client, regardless of your platform what language


Version 1.0 Agreement (not yet named) provisions:

1.BasePack represents the packet sent, Baseackpack represents the receipt packet, Baseackpack inherits from BasePack

2. Each pack length is 1024 bytes, and the No. 0 to 4th byte is converted to int to represent the pack type, BasePack and its subclasses from 1.2.3 ... Baseackpack and its subclasses from 1001,1002,1003...1010 ... (Consider negative numbers, in fact should also OK)

Why did you do that? It's strange here, you don't know which object the pack is, before you turn the 1024 bytes into an object with Msgpack, you can't unify a particular object, for example Loginpack has 2 more properties than BasePack. You do not know if it is a loginpack or a basepack before you can not open him, you can open any one of the possible errors (attributes are more or less, familiar with the iOS KVC should be very clear), so must first 4 bytes out, optional, the first 5~ 8 bytes to put the length (Mespack can be longer than the contents of the disassembly is not a problem), read 8 bytes and then read the remaining 1016 (certainly not necessarily each package must be 1024, can be larger, after all I have enough) a byte

Using System;//send Package namespace packs{//Basic Package public class Basepack<t> {public int packtype;        public int fromid;        public int toid;        public int messageId; Transfer basic package to bytes public byte[] Packtobytes () {var encode = MsgPack.Serialization.MessagePackSerializ Er.            Get<t> (); byte[] Packcontent = encode.            Packsingleobject (this);            byte[] Type = basepack.inttobytes (This.packtype);            byte[] len = basepack.inttobytes (packcontent.length);            int lenth = Packcontent.length;            byte[] Dest = new byte[1024]; First int space: type buffer.blockcopy (type, 0, dest, 0, type.            Length); Second int space: Length buffer.blockcopy (len, 0, dest, type. Length, Len.                        Length); Remaining space: Package content buffer.blockcopy (packcontent, 0, dest, type. Length+len.            Length, packcontent.length); Console.WriteLine ("Pack pack, type:" + this.packtype + "Length:" + packcontent.Length);        return dest; }//bytes back to Basic package public static T bytestopack (byte[] bytes) {var encode = Msgpack.serializa tion.            Messagepackserializer.get<t> (); return encode.        Unpacksingleobject (bytes);        }} public class Basepack:basepack<basepack> {public const int login_pack = 1;        public const int register_pack = 2;        public const int ping_pack = 3;        public const int pong_pack = 4;        public const int text_pack = 5;        public const int system_push_pack = 6;        public const int login_ack = 1001;        public const int register_ack = 1002;        public const int ping_ack = 1003;        public const int pong_ack = 1004;        public const int text_ack = 1005;        public const int system_push_ack = 1006;               public const int connected_ack = 1007;  /** * Converts an int value to a byte array of four bytes, this method applies to the order of (low in front, high in the rear). * @param value * The int value to convert * @return byte array */       public static byte[] Inttobytes (int value) {byte[] byte_src = new Byte[4];            BYTE_SRC[3] = (byte) ((Value & 0xff000000) >> 24);            BYTE_SRC[2] = (byte) ((Value & 0x00ff0000) >> 16);            BYTE_SRC[1] = (byte) ((Value & 0x0000FF00) >> 8);            Byte_src[0] = (byte) ((Value & 0x000000ff));        return BYTE_SRC;        }/** * Byte array to take an int value, this method applies to (low in front, high in the back) order. * * @param ary * byte array * @param offset * Starts from the offset bit of the array *            @return int Value */public static int bytestoint (byte[] ary, int offset) {int value; Value = (int) ((Ary[offset] & 0xFF) | ((Ary[offset + 1] << 8) & 0xff00) | ((Ary[offset + 2] <<) & 0xFF0000) |            ((Ary[offset + 3] <<) & 0xff000000));        return value; }   }//1. login package public class Loginpack:basepack<loginpack> {public string username;        public string Password;        Public Loginpack () {this.packtype = Basepack.login_pack;        }}//5. text package public class Textpack:basepack<textpack> {public string content;        public string Touser;        public string Fromuser;        Public Textpack () {this.packtype = Basepack.text_pack;        }}//6. System push Package public class Systempushpack:basepack<systempushpack> {public string content;          public string Touser;        Public Systempushpack () {this.packtype = Basepack.system_push_pack; }    }   }



3.server Terminal accept immediately after sending Connectpack, the client receives after send Connectackpack completes the connection

private void OnAccept ()    {while        (this.isserving)        {            //Async Accept Callback Connend            // Serversocket.beginaccept (New System.AsyncCallback (this. Connend), null);            Synchronous Accept            Socket clientsocket = serversocket.accept ();            Receiveobject obj = new Receiveobject ();            Obj.acceptclient = Clientsocket;            Clients. ADD (obj);            Thread receivethread = new Thread (onreceive);            Receivethread.start (obj);            Cthreads.add (ClientSocket.RemoteEndPoint.ToString (), receivethread);            Console.WriteLine ("New Client Connection:" + clientSocket.RemoteEndPoint.ToString ());            Baseackpack pack = new Baseackpack ();            Pack.packtype = Basepack.connected_ack;            Clientsocket.send (Pack. Packtobytes ());        }    }



4. The client sends Loginpack (because PHP has verified the username and password and generated token, so loginpack actually I did not write the logic of verifying the password, simply bind the user name, to receive the message), The server-side unpacking pack binds the user name to the client object, the contents of which are as follows:


Using System.net.sockets;public class receiveobject{public    Socket acceptclient;    Public byte[] buffer = new byte[1024];    public string userId;    public string userName;    public int roomid;    Public Receiveobject ()    {    }}


The entire processing function:

    private void OnReceive (object obj) {while (this.isserving) {receiveobject e = obj as Rec            Eiveobject;            Socket C = e.acceptclient;            E.buffer = new byte[1024];            To determine the package type, fix the package before the package int type = c.receive (e.buffer, 0, sizeof (Int32), socketflags.none);                if (type = = 0) {Console.WriteLine ("Client Disconnects:" + c.remoteendpoint.tostring ()); Clients.                RemoveAll ((receiveobject obj) = {return obj.acceptclient = = 0? true:false;}); Clients.                Remove (e);                Cthreads.remove (C.remoteendpoint.tostring ());                Thread.CurrentThread.Abort ();                Disconnect C.shutdown (Socketshutdown.both);                C.close ();            Break            } type = Basepack.bytestoint (e.buffer, 0);            Get packet size, fixed 2nd int int len = c.receive (e.buffer, 0, sizeof (Int32), socketflags.none); Len = basepack.byTestoint (e.buffer, 0);            int receivenumber = c.receive (e.buffer, 0, 1024-sizeof (Int32) * 2, Socketflags.none); Switch (type) {case Basepack.login_pack: {loginpack                        Lpack = Loginpack.bytestopack (E.buffer);                        Console.WriteLine ("Received login request, user name:" + lpack.username + "Password:" + Lpack.password);                        E.username = Lpack.username;                        Send Login ack loginackpack loginack = new Loginackpack ();                        Loginack.success = true;                    C.send (Loginack.packtobytes ());                } break; Case Basepack.text_pack: {//Processing message pack textpack pack = TEXTPA Ck.                        Bytestopack (E.buffer);                Handle BasePack Console.WriteLine ("Send to" + Pack.touser + "message:" + pack.content);        Find the user list<receiveobject> List = clients from the clients group.                        FindAll ((receiveobject o) = {return O.username = = Pack.touser? True:false;}); foreach (receiveobject target in list) {Target.acceptClient.Send (PACK.P                        Acktobytes ());                }} break; Case Basepack.text_ack: {//Processing message receipts textackpack pack = Tex                        Tackpack.bytestopack (E.buffer); Deletes the corresponding message pusher.                    Deletemessagebyid (Pack.messageid);                } break; Case Basepack.system_push_ack: {systempushackpack pack = Systempushackpack.byte                        Stopack (E.buffer); Deletes the corresponding message pusher.                    Deletemessagebyid (Pack.messageid);      }              Break            Default://Handle unknown package {} break; }        }    }

Send Message function, currently write 2 case reason: PHP side of the push type a lot of, I directly write in pushpack content inside, the client with JSON parsing open on the line, and then do a single chat text message sent, press group push not time to do:

    public void Sendmsg (string from, string to, string body, int type, int messageId) {//Find user Re from clients group Ceiveobject target = clients.        FindLast ((receiveobject o) = {return O.username = = to? True:false;});        if (target = = null) return; Push a message to the client//receive receipt before modifying the sent status to 1 Console.WriteLine ("Push Message to:" + to + "type:" + Type + "content:" + body + "ID:" + mes        Sageid); Switch (type) {//Push text message case Basepack.text_pack: {Textpac                    K Txtpack = new Textpack ();                    Txtpack.fromuser = from;                    Txtpack.touser = to;                    Txtpack.content = body;                    Txtpack.messageid = messageId;                Target.acceptClient.Send (Txtpack.packtobytes ());            } break; System Message Case Basepack.system_push_pack: {systempushpack txtpack = new Systemp             Ushpack ();       Txtpack.touser = to;                    Txtpack.content = body;                    Txtpack.messageid = messageId;                Target.acceptClient.Send (Txtpack.packtobytes ());            } break;        Default: {} break; }    }}


And then there's the message queue and the PHP "-" Call problem between C #


1. In strict accordance with the peer model and the PUBSUB model of Message Queuing, namely:

Peer Model: If the message recipient's username in the clients array, send the label immediately, otherwise save the database as offline message, waiting for the user to log in and then remove the user's offline message from the database to continue sending in memory until the corresponding type of ACK or Baseack ( The client's protocol is lower than the server side, and is completely removed from the database;

PubSub model: Regardless of how many (identical roomid tags) the message accepts in the clients array, 0 to the theoretical limit, immediately sent without receipt and immediately removed from memory and not stored in the database


2. Since PHP and C # programs are 2 different processes, it involves interprocess communication, and if the 2 programs are running on the same computer, the possible options are: Shared memory, local sockets, pipelines, etc.?? But it may be that we prefer web programs and messaging programs to be on the same computer, so other ways: share the same database connection, HTTP polling

Specific can be selected according to the situation, I have two kinds of writing here.

And my expectation is that PHP every insert a message, C # immediately push out, then C # do database polling or HTTP polling is actually OK, I only use a thread to do polling.


Finally, today write down the game side:

Finally can push all kinds of bags, start the game package, the card pack, the Victory package DAY1 has been described, is currently doing: The client card type check and each of the rounds of each round when the decision.

The rules of the game is the standard to run fast, that is, the player to get the Spades 3 first-round first-hand cards, not yet done, you can do after all players receive the start of the game package to do a simple check.

Pass the card directly call out the interface, pass an empty string, there is no active card and end each round of logic, do the end of each board logic, that is to determine the outcome.

Finally, a few tests, player ID 45 and player ID 50 played a game:





Development diary: kbengine+unity+php to be a poker game-day2

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.