2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
19 * Contains some contributions under the Thrift Software License.
20 * Please see doc/old-thrift-license.txt in the Thrift distribution for
24 /* only for silverlight */
28 using System.Net.Sockets;
31 using System.Threading;
33 namespace Thrift.Transport
35 public class TSilverlightSocket : TTransport
38 static ManualResetEvent readAsyncComplete = new ManualResetEvent(false);
39 public event EventHandler<SocketAsyncEventArgs> connectHandler = null;
41 // memory stream for write cache.
42 private MemoryStream outputStream = new MemoryStream();
44 private string host = null;
46 private int timeout = 0;
49 public TSilverlightSocket(string host, int port)
55 public TSilverlightSocket(string host, int port, int timeout)
59 this.timeout = timeout;
64 private void InitSocket()
66 // Create a stream-based, TCP socket using the InterNetwork Address Family.
67 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
68 socket.NoDelay = true;
95 public override bool IsOpen
104 return socket.Connected;
108 public override void Open()
112 throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
115 if (string.IsNullOrEmpty(host))
117 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
122 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
130 if (timeout == 0) // no timeout -> infinite
132 timeout = 10000; // set a default timeout for WP.
136 // Create DnsEndPoint. The hostName and port are passed in to this method.
137 DnsEndPoint hostEntry = new DnsEndPoint(this.host, this.port);
139 // Create a SocketAsyncEventArgs object to be used in the connection request
140 SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
141 socketEventArg.RemoteEndPoint = hostEntry;
143 // Inline event handler for the Completed event.
144 // Note: This event handler was implemented inline in order to make this method self-contained.
145 socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
147 if (connectHandler != null)
149 connectHandler(this, e);
153 // Make an asynchronous Connect request over the socket
154 socket.ConnectAsync(socketEventArg);
158 public override int Read(byte[] buf, int off, int len)
160 bool _timeout = true;
161 string _error = null;
166 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Socket is not open");
169 // Create SocketAsyncEventArgs context object
170 SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
171 socketEventArg.RemoteEndPoint = socket.RemoteEndPoint;
173 // Setup the buffer to receive the data
174 socketEventArg.SetBuffer(buf, off, len);
176 // Inline event handler for the Completed event.
177 // Note: This even handler was implemented inline in order to make
178 // this method self-contained.
179 socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
183 if (e.SocketError == SocketError.Success)
185 _recvBytes = e.BytesTransferred;
189 _error = e.SocketError.ToString();
192 readAsyncComplete.Set();
195 // Sets the state of the event to nonsignaled, causing threads to block
196 readAsyncComplete.Reset();
198 // Make an asynchronous Receive request over the socket
199 socket.ReceiveAsync(socketEventArg);
201 // Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
202 // If no response comes back within this time then proceed
203 readAsyncComplete.WaitOne(this.timeout);
207 throw new TTransportException(TTransportException.ExceptionType.TimedOut, "Socket recv timeout");
212 throw new TTransportException(TTransportException.ExceptionType.Unknown, _error);
218 public override void Write(byte[] buf, int off, int len)
220 outputStream.Write(buf, off, len);
223 private void beginFlush_Completed(object sender, SocketAsyncEventArgs e)
225 FlushAsyncResult flushAsyncResult = e.UserToken as FlushAsyncResult;
226 flushAsyncResult.UpdateStatusToComplete();
227 flushAsyncResult.NotifyCallbackWhenAvailable();
229 if (e.SocketError != SocketError.Success)
231 throw new TTransportException(TTransportException.ExceptionType.Unknown, e.SocketError.ToString());
235 public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
237 // Extract request and reset buffer
238 byte[] data = outputStream.ToArray();
240 FlushAsyncResult flushAsyncResult = new FlushAsyncResult(callback, state);
242 SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
243 socketEventArg.RemoteEndPoint = socket.RemoteEndPoint;
244 socketEventArg.UserToken = flushAsyncResult;
246 socketEventArg.Completed += beginFlush_Completed;
247 socketEventArg.SetBuffer(data, 0, data.Length);
249 socket.SendAsync(socketEventArg);
251 return flushAsyncResult;
254 public override void EndFlush(IAsyncResult asyncResult)
258 var flushAsyncResult = (FlushAsyncResult)asyncResult;
260 if (!flushAsyncResult.IsCompleted)
262 var waitHandle = flushAsyncResult.AsyncWaitHandle;
263 waitHandle.WaitOne();
267 if (flushAsyncResult.AsyncException != null)
269 throw flushAsyncResult.AsyncException;
274 outputStream = new MemoryStream();
278 // Copy from impl from THttpClient.cs
279 // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
280 class FlushAsyncResult : IAsyncResult
282 private volatile Boolean _isCompleted;
283 private ManualResetEvent _evt;
284 private readonly AsyncCallback _cbMethod;
285 private readonly object _state;
287 public FlushAsyncResult(AsyncCallback cbMethod, object state)
289 _cbMethod = cbMethod;
293 internal byte[] Data { get; set; }
294 internal Socket Connection { get; set; }
295 internal TTransportException AsyncException { get; set; }
297 public object AsyncState
299 get { return _state; }
302 public WaitHandle AsyncWaitHandle
304 get { return GetEvtHandle(); }
307 public bool CompletedSynchronously
309 get { return false; }
312 public bool IsCompleted
314 get { return _isCompleted; }
317 private readonly object _locker = new object();
319 private ManualResetEvent GetEvtHandle()
325 _evt = new ManualResetEvent(false);
335 internal void UpdateStatusToComplete()
337 _isCompleted = true; //1. set _iscompleted to true
342 _evt.Set(); //2. set the event, when it exists
347 internal void NotifyCallbackWhenAvailable()
349 if (_cbMethod != null)
356 public override void Close()
365 #region " IDisposable Support "
366 private bool _IsDisposed;
369 protected override void Dispose(bool disposing)
375 if (outputStream != null)
377 outputStream.Dispose();
382 ((IDisposable)socket).Dispose();