Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
SocketContext.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using SiliconStudio.Core.Diagnostics;
4 #if !SILICONSTUDIO_PLATFORM_WINDOWS_RUNTIME
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Text;
10 using System.Net.Sockets;
11 using System.Net;
12 using System.Threading;
13 using System.Runtime.Serialization.Formatters.Binary;
14 using System.Threading.Tasks;
15 using System.Runtime.CompilerServices;
16 using System.Collections;
17 using SiliconStudio.Core.Serialization;
18 
19 #if NET45
20 using TaskEx = System.Threading.Tasks.Task;
21 #endif
22 
23 namespace SiliconStudio.Paradox.Engine.Network
24 {
25  // Temporary socket class
26  // TODO: redesign it, convert to SendAsync/ReceiveAsync, etc...
27  public class SocketContext
28  {
29  private TaskCompletionSource<bool> clientConnected;
30  private bool isServer;
31  private NetworkStream socketStream;
32  private BinaryReader socketBinaryReader;
33  private BinaryWriter socketBinaryWriter;
34  private readonly ManualResetEvent allDone = new ManualResetEvent(false);
35  private readonly Dictionary<int, TaskCompletionSource<SocketMessage>> packetCompletionTasks = new Dictionary<int, TaskCompletionSource<SocketMessage>>();
36 
37  Dictionary<Type, Tuple<Action<object>, bool>> packetHandlers = new Dictionary<Type, Tuple<Action<object>, bool>>();
38 
39  public Action<SocketContext> Connected;
40 
41  public void AddPacketHandler<T>(Action<T> handler, bool oneTime = false)
42  {
43  lock (packetHandlers)
44  {
45  packetHandlers.Add(typeof(T), Tuple.Create<Action<object>, bool>((obj) => handler((T)obj), oneTime));
46  }
47  }
48 
49  public void Send(object obj)
50  {
51  var memoryStream = new MemoryStream();
52  var binaryWriter = new BinarySerializationWriter(memoryStream);
53  binaryWriter.SerializeExtended(obj, ArchiveMode.Serialize, null);
54  var memoryBuffer = memoryStream.ToArray();
55 
56  socketBinaryWriter.Write(memoryBuffer.Length);
57 
58  // Chunk it into block of 1024 bytes (not sure why but had some problem when doing send bigger than 3k would end up in buffer filled with 0, maybe Mono issue?)
59  for (int i = 0; i < (memoryBuffer.Length + 1023) / 1024; ++i)
60  socketStream.Write(memoryStream.GetBuffer(), i * 1024, Math.Min(1024, memoryBuffer.Length - i * 1024));
61  }
62 
63  public void StartServer(int port)
64  {
65  new Thread(SafeAction.Wrap(() => ServerThread(port))).Start();
66  }
67 
68  public async Task StartClient(IPAddress address, int port)
69  {
70  clientConnected = new TaskCompletionSource<bool>();
71  new Thread(SafeAction.Wrap(() => ClientThread(address, port))).Start();
73  }
74 
76  {
77  var tcs = new TaskCompletionSource<SocketMessage>();
78  query.StreamId = SocketMessage.NextStreamId + (isServer ? 0x4000000 : 0);
79  lock (packetCompletionTasks)
80  {
81  packetCompletionTasks.Add(query.StreamId, tcs);
82  }
83  Send(query);
84  return await tcs.Task;
85  }
86 
87  async void ClientThread(IPAddress address, int port)
88  {
89  while (true)
90  {
91  try
92  {
93  var localEP = new IPEndPoint(address, port);
94 
95  var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
96  socket.NoDelay = true;
97  socket.Connect(localEP);
98 
99  SetSocketStream(new NetworkStream(socket));
100 
101  clientConnected.TrySetResult(true);
102  MessageLoop();
103  }
104  catch (Exception e)
105  {
106  Console.WriteLine("Error connecting: " + e);
107  }
108 
109  await TaskEx.Delay(1000);
110  }
111  }
112 
113  void ServerThread(int port)
114  {
115  //var ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
116  //var localEP = new IPEndPoint(ipHostInfo.AddressList[0], 11000);
117  var localEP = new IPEndPoint(IPAddress.Any, port);
118 
119  var listener = new Socket(localEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
120  listener.NoDelay = true;
121  try
122  {
123  listener.Bind(localEP);
124  listener.Listen(10);
125  while (true)
126  {
127  allDone.Reset();
128 
129  Console.WriteLine("Waiting for a connection...");
130  listener.BeginAccept(AcceptCallback, listener);
131 
132  allDone.WaitOne();
133  }
134  }
135  catch (Exception e)
136  {
137  Console.WriteLine(e.ToString());
138  }
139  }
140 
141  void AcceptCallback(IAsyncResult ar)
142  {
143  try
144  {
145  allDone.Set();
146 
147  var listener = (Socket)ar.AsyncState;
148  var handler = listener.EndAccept(ar);
149 
150  var clientSocketContext = new SocketContext();
151  clientSocketContext.SetSocketStream(new NetworkStream(handler));
152  clientSocketContext.isServer = true;
153 
154  if (Connected != null)
155  Connected(clientSocketContext);
156 
157  clientSocketContext.MessageLoop();
158  } catch (Exception e)
159  {
160  Console.WriteLine(e.ToString());
161  }
162  }
163 
164  void MessageLoop()
165  {
166  while (true)
167  {
168  //var obj = formatter.Deserialize(socketStream);
169  var remaining = socketBinaryReader.ReadInt32();
170  var buffer = new byte[remaining];
171  int offset = 0;
172  while (remaining > 0)
173  {
174  int read = socketStream.Read(buffer, offset, remaining);
175  remaining -= read;
176  offset += read;
177  }
178  var binaryReader = new BinarySerializationReader(new MemoryStream(buffer));
179  object obj = null;
180  binaryReader.SerializeExtended<object>(ref obj, ArchiveMode.Deserialize, null);
181  if (obj is SocketMessage)
182  {
183  var socketMessage = (SocketMessage)obj;
184  ProcessMessage(socketMessage);
185  }
186  bool handlerFound;
187  Tuple<Action<object>, bool> handler;
188  lock (packetHandlers)
189  {
190  handlerFound = packetHandlers.TryGetValue(obj.GetType(), out handler);
191 
192  // one-time handler
193  if (handlerFound && handler.Item2)
194  {
195  packetHandlers.Remove(obj.GetType());
196  }
197  }
198 
199  if (handlerFound)
200  {
201  handler.Item1(obj);
202  }
203  }
204  }
205 
206  void SetSocketStream(NetworkStream socketStream)
207  {
208  this.socketStream = socketStream;
209  socketBinaryReader = new BinaryReader(this.socketStream);
210  socketBinaryWriter = new BinaryWriter(this.socketStream);
211  }
212 
213  void ProcessMessage(SocketMessage socketMessage)
214  {
215  TaskCompletionSource<SocketMessage> tcs;
216  lock (packetCompletionTasks)
217  {
218  packetCompletionTasks.TryGetValue(socketMessage.StreamId, out tcs);
219  if (tcs != null)
220  packetCompletionTasks.Remove(socketMessage.StreamId);
221  }
222  if (tcs != null)
223  tcs.TrySetResult(socketMessage);
224  }
225  }
226 }
227 #endif
static ThreadStart Wrap(ThreadStart action, [CallerFilePath] string sourceFilePath="", [CallerMemberName] string memberName="", [CallerLineNumber] int sourceLineNumber=0)
Definition: SafeAction.cs:13
System.Threading.Tasks.Task Task
Implements SerializationStream as a binary writer.
Implements SerializationStream as a binary reader.
async Task< SocketMessage > SendReceiveAsync(SocketMessage query)
async Task StartClient(IPAddress address, int port)