├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md └── sproto-Unity ├── NetCore.cs ├── NetReceiver.cs └── NetSender.cs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Folder view configuration files 3 | .DS_Store 4 | Desktop.ini 5 | 6 | # Thumbnail cache files 7 | ._* 8 | Thumbs.db 9 | 10 | # Files that might appear on external disks 11 | .Spotlight-V100 12 | .Trashes 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 m2q1n9 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sproto-Unity 2 | ============ 3 | 4 | > A demo show how to use [sproto-Csharp](https://github.com/lvzixun/sproto-Csharp) in Unity. 5 | 6 | How to send RPC 7 | --------------- 8 | 9 | ```csharp 10 | SprotoType.Handshake.request req = new SprotoType.Handshake.request(); 11 | req.uid = uid; 12 | req.token = token; 13 | 14 | NetSender.Send(req, (_) => 15 | { 16 | SprotoType.Handshake.response rsp = _ as SprotoType.Handshake.response; 17 | if (rsp.result == 0) 18 | { 19 | } 20 | }); 21 | ``` 22 | 23 | How to recv RPC 24 | --------------- 25 | 26 | ```csharp 27 | SprotoTypeBase HeartbeatRsp(SprotoTypeBase _) 28 | { 29 | SprotoType.Heartbeat.request req = _ as SprotoType.Heartbeat.request; 30 | return null; // can return a response 31 | } 32 | 33 | NetReceiver.AddHandler(HeartbeatRsp); 34 | ``` 35 | -------------------------------------------------------------------------------- /sproto-Unity/NetCore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Net.Sockets; 5 | using System.Collections.Generic; 6 | using Sproto; 7 | using SprotoType; 8 | using UnityEngine; 9 | 10 | public delegate void SocketConnected(); 11 | 12 | public class NetCore 13 | { 14 | private static Socket socket; 15 | 16 | public static bool logined; 17 | public static bool enabled; 18 | 19 | private static int CONNECT_TIMEOUT = 3000; 20 | private static ManualResetEvent TimeoutObject; 21 | 22 | private static Queue recvQueue = new Queue(); 23 | 24 | private static SprotoPack sendPack = new SprotoPack(); 25 | private static SprotoPack recvPack = new SprotoPack(); 26 | 27 | private static SprotoStream sendStream = new SprotoStream(); 28 | private static SprotoStream recvStream = new SprotoStream(); 29 | 30 | private static ProtocolFunctionDictionary protocol = Protocol.Instance.Protocol; 31 | private static Dictionary sessionDict; 32 | 33 | private static AsyncCallback connectCallback = new AsyncCallback(Connected); 34 | private static AsyncCallback receiveCallback = new AsyncCallback(Receive); 35 | 36 | public static void Init() 37 | { 38 | byte[] receiveBuffer = new byte[1 << 16]; 39 | recvStream.Write(receiveBuffer, 0, receiveBuffer.Length); 40 | recvStream.Seek(0, SeekOrigin.Begin); 41 | 42 | sessionDict = new Dictionary(); 43 | } 44 | 45 | public static void Connect(string host, int port, SocketConnected socketConnected) 46 | { 47 | Disconnect(); 48 | 49 | socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 50 | socket.BeginConnect(host, port, connectCallback, socket); 51 | 52 | TimeoutObject = new ManualResetEvent(false); 53 | TimeoutObject.Reset(); 54 | 55 | if (TimeoutObject.WaitOne(CONNECT_TIMEOUT, false)) 56 | { 57 | Receive(); 58 | socketConnected(); 59 | } 60 | else 61 | { 62 | Debug.Log("Connect Timeout"); 63 | } 64 | } 65 | 66 | private static void Connected(IAsyncResult ar) 67 | { 68 | socket.EndConnect(ar); 69 | TimeoutObject.Set(); 70 | } 71 | 72 | public static void Disconnect() 73 | { 74 | if (connected) 75 | { 76 | socket.Close(); 77 | } 78 | } 79 | 80 | public static bool connected 81 | { 82 | get 83 | { 84 | return socket != null && socket.Connected; 85 | } 86 | } 87 | 88 | public static void Send(SprotoTypeBase rpc = null, long? session = null) 89 | { 90 | Send(rpc, session, protocol[typeof(T)]); 91 | } 92 | 93 | private static int MAX_PACK_LEN = (1 << 16) - 1; 94 | private static void Send(SprotoTypeBase rpc, long? session, int tag) 95 | { 96 | if (!connected || !enabled) 97 | { 98 | return; 99 | } 100 | 101 | package pkg = new package(); 102 | pkg.type = tag; 103 | 104 | if (session != null) 105 | { 106 | pkg.session = (long)session; 107 | sessionDict.Add((long)session, protocol[tag].Response.Value); 108 | } 109 | 110 | sendStream.Seek(0, SeekOrigin.Begin); 111 | int len = pkg.encode(sendStream); 112 | if (rpc != null) 113 | { 114 | len += rpc.encode(sendStream); 115 | } 116 | 117 | byte[] data = sendPack.pack(sendStream.Buffer, len); 118 | if (data.Length > MAX_PACK_LEN) 119 | { 120 | Debug.Log("data.Length > " + MAX_PACK_LEN + " => " + data.Length); 121 | return; 122 | } 123 | 124 | sendStream.Seek(0, SeekOrigin.Begin); 125 | sendStream.WriteByte((byte)(data.Length >> 8)); 126 | sendStream.WriteByte((byte)data.Length); 127 | sendStream.Write(data, 0, data.Length); 128 | 129 | try { 130 | socket.Send(sendStream.Buffer, sendStream.Position, SocketFlags.None); 131 | } 132 | catch (Exception e) { 133 | Debug.LogWarning(e.ToString()); 134 | } 135 | } 136 | 137 | private static int receivePosition; 138 | public static void Receive(IAsyncResult ar = null) 139 | { 140 | if (!connected) 141 | { 142 | return; 143 | } 144 | 145 | if (ar != null) 146 | { 147 | try { 148 | receivePosition += socket.EndReceive(ar); 149 | } 150 | catch (Exception e) { 151 | Debug.LogWarning(e.ToString()); 152 | } 153 | } 154 | 155 | int i = recvStream.Position; 156 | while (receivePosition >= i + 2) 157 | { 158 | int length = (recvStream[i] << 8) | recvStream[i+1]; 159 | 160 | int sz = length + 2; 161 | if (receivePosition < i + sz) 162 | { 163 | break; 164 | } 165 | 166 | recvStream.Seek(2, SeekOrigin.Current); 167 | 168 | if (length > 0) 169 | { 170 | byte[] data = new byte[length]; 171 | recvStream.Read(data, 0, length); 172 | recvQueue.Enqueue(data); 173 | } 174 | 175 | i += sz; 176 | } 177 | 178 | if (receivePosition == recvStream.Buffer.Length) 179 | { 180 | recvStream.Seek(0, SeekOrigin.End); 181 | recvStream.MoveUp(i, i); 182 | receivePosition = recvStream.Position; 183 | recvStream.Seek(0, SeekOrigin.Begin); 184 | } 185 | 186 | try { 187 | socket.BeginReceive(recvStream.Buffer, receivePosition, 188 | recvStream.Buffer.Length - receivePosition, 189 | SocketFlags.None, receiveCallback, socket); 190 | } 191 | catch (Exception e) { 192 | Debug.LogWarning(e.ToString()); 193 | } 194 | } 195 | 196 | public static void Dispatch() 197 | { 198 | package pkg = new package(); 199 | 200 | if (recvQueue.Count > 20) 201 | { 202 | Debug.Log("recvQueue.Count: " + recvQueue.Count); 203 | } 204 | 205 | while (recvQueue.Count > 0) 206 | { 207 | byte[] data = recvPack.unpack(recvQueue.Dequeue()); 208 | int offset = pkg.init(data); 209 | 210 | int tag = (int)pkg.type; 211 | long session = (long)pkg.session; 212 | 213 | if (pkg.HasType) 214 | { 215 | RpcReqHandler rpcReqHandler = NetReceiver.GetHandler(tag); 216 | if (rpcReqHandler != null) 217 | { 218 | SprotoTypeBase rpcRsp = rpcReqHandler(protocol.GenRequest(tag, data, offset)); 219 | if (pkg.HasSession) 220 | { 221 | Send(rpcRsp, session, tag); 222 | } 223 | } 224 | } 225 | else 226 | { 227 | RpcRspHandler rpcRspHandler = NetSender.GetHandler(session); 228 | if (rpcRspHandler != null) 229 | { 230 | ProtocolFunctionDictionary.typeFunc GenResponse; 231 | sessionDict.TryGetValue(session, out GenResponse); 232 | rpcRspHandler(GenResponse(data, offset)); 233 | } 234 | } 235 | } 236 | } 237 | 238 | } 239 | -------------------------------------------------------------------------------- /sproto-Unity/NetReceiver.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Sproto; 3 | 4 | public delegate SprotoTypeBase RpcReqHandler(SprotoTypeBase rpcReq); 5 | 6 | public class NetReceiver 7 | { 8 | private static ProtocolFunctionDictionary protocol = Protocol.Instance.Protocol; 9 | private static Dictionary rpcReqHandlerDict; 10 | 11 | public static void Init() 12 | { 13 | rpcReqHandlerDict = new Dictionary(); 14 | } 15 | 16 | public static void AddHandler(int tag, RpcReqHandler rpcReqHandler) 17 | { 18 | rpcReqHandlerDict.Add(tag, rpcReqHandler); 19 | } 20 | 21 | public static int AddHandler(RpcReqHandler rpcReqHandler) 22 | { 23 | int tag = protocol[typeof(T)]; 24 | AddHandler(tag, rpcReqHandler); 25 | return tag; 26 | } 27 | 28 | public static void RemoveHandler(int tag) 29 | { 30 | if (rpcReqHandlerDict.ContainsKey(tag)) 31 | { 32 | rpcReqHandlerDict.Remove(tag); 33 | } 34 | } 35 | 36 | public static void RemoveHandler() 37 | { 38 | RemoveHandler(protocol[typeof(T)]); 39 | } 40 | 41 | public static RpcReqHandler GetHandler(int tag) 42 | { 43 | RpcReqHandler rpcReqHandler; 44 | rpcReqHandlerDict.TryGetValue(tag, out rpcReqHandler); 45 | return rpcReqHandler; 46 | } 47 | 48 | public static RpcReqHandler GetHandler() 49 | { 50 | return GetHandler(protocol[typeof(T)]); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /sproto-Unity/NetSender.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Sproto; 3 | 4 | public delegate void RpcRspHandler(SprotoTypeBase rpcRsp); 5 | 6 | public class NetSender 7 | { 8 | private static long session; 9 | private static Dictionary rpcRspHandlerDict; 10 | 11 | public static void Init() 12 | { 13 | rpcRspHandlerDict = new Dictionary(); 14 | } 15 | 16 | public static void Send(SprotoTypeBase rpcReq = null, RpcRspHandler rpcRspHandler = null) 17 | { 18 | if (rpcRspHandler != null) 19 | { 20 | session++; 21 | AddHandler(session, rpcRspHandler); 22 | NetCore.Send(rpcReq, session); 23 | } 24 | else 25 | { 26 | NetCore.Send(rpcReq); 27 | } 28 | } 29 | 30 | private static void AddHandler(long session, RpcRspHandler rpcRspHandler) 31 | { 32 | rpcRspHandlerDict.Add(session, rpcRspHandler); 33 | } 34 | 35 | private static void RemoveHandler(long session) 36 | { 37 | if (rpcRspHandlerDict.ContainsKey(session)) 38 | { 39 | rpcRspHandlerDict.Remove(session); 40 | } 41 | } 42 | 43 | public static RpcRspHandler GetHandler(long session) 44 | { 45 | RpcRspHandler rpcRspHandler; 46 | rpcRspHandlerDict.TryGetValue(session, out rpcRspHandler); 47 | RemoveHandler(session); 48 | return rpcRspHandler; 49 | } 50 | 51 | } 52 | --------------------------------------------------------------------------------