+ static void SetSocketOption_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error)
+ {
+ bool release = false;
+ try {
+ safeHandle.DangerousAddRef (ref release);
+ SetSocketOption_internal (safeHandle.DangerousGetHandle (), level, name, obj_val, byte_val, int_val, out error);
+ } finally {
+ if (release)
+ safeHandle.DangerousRelease ();
+ }
+ }
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error);
+
+#endregion
+
+#region IOControl
+
+ public int IOControl (int ioctl_code, byte [] in_value, byte [] out_value)
+ {
+ if (is_disposed)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ int error;
+ int result = IOControl_internal (safe_handle, ioctl_code, in_value, out_value, out error);
+
+ if (error != 0)
+ throw new SocketException (error);
+ if (result == -1)
+ throw new InvalidOperationException ("Must use Blocking property instead.");
+
+ return result;
+ }
+
+ public int IOControl (IOControlCode ioControlCode, byte[] optionInValue, byte[] optionOutValue)
+ {
+ return IOControl ((int) ioControlCode, optionInValue, optionOutValue);
+ }
+
+ static int IOControl_internal (SafeSocketHandle safeHandle, int ioctl_code, byte [] input, byte [] output, out int error)
+ {
+ bool release = false;
+ try {
+ safeHandle.DangerousAddRef (ref release);
+ return IOControl_internal (safeHandle.DangerousGetHandle (), ioctl_code, input, output, out error);
+ } finally {
+ if (release)
+ safeHandle.DangerousRelease ();
+ }
+ }
+
+ /* See Socket.IOControl, WSAIoctl documentation in MSDN. The common options between UNIX
+ * and Winsock are FIONREAD, FIONBIO and SIOCATMARK. Anything else will depend on the system
+ * except SIO_KEEPALIVE_VALS which is properly handled on both windows and linux. */
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ extern static int IOControl_internal (IntPtr sock, int ioctl_code, byte [] input, byte [] output, out int error);
+
+#endregion
+
+#region Close
+
+ public void Close ()
+ {
+ linger_timeout = 0;
+ Dispose ();
+ }
+
+ public void Close (int timeout)
+ {
+ linger_timeout = timeout;
+ Dispose ();
+ }
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern static void Close_internal (IntPtr socket, out int error);
+
+#endregion
+
+#region Shutdown
+
+ public void Shutdown (SocketShutdown how)
+ {
+ ThrowIfDisposedAndClosed ();
+
+ if (!is_connected)
+ throw new SocketException (10057); // Not connected
+
+ int error;
+ Shutdown_internal (safe_handle, how, out error);
+
+ if (error != 0)
+ throw new SocketException (error);
+ }
+
+ static void Shutdown_internal (SafeSocketHandle safeHandle, SocketShutdown how, out int error)
+ {
+ bool release = false;
+ try {
+ safeHandle.DangerousAddRef (ref release);
+ Shutdown_internal (safeHandle.DangerousGetHandle (), how, out error);
+ } finally {
+ if (release)
+ safeHandle.DangerousRelease ();
+ }
+ }
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ internal extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
+
+#endregion
+
+#region Dispose
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (is_disposed)
+ return;
+
+ is_disposed = true;
+ bool was_connected = is_connected;
+ is_connected = false;
+
+ if (safe_handle != null) {
+ is_closed = true;
+ IntPtr x = Handle;
+
+ if (was_connected)
+ Linger (x);
+
+ safe_handle.Dispose ();
+ }
+ }
+
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ void Linger (IntPtr handle)
+ {
+ if (!is_connected || linger_timeout <= 0)
+ return;
+
+ /* We don't want to receive any more data */
+ int error;
+ Shutdown_internal (handle, SocketShutdown.Receive, out error);
+
+ if (error != 0)
+ return;
+
+ int seconds = linger_timeout / 1000;
+ int ms = linger_timeout % 1000;
+ if (ms > 0) {
+ /* If the other end closes, this will return 'true' with 'Available' == 0 */
+ Poll_internal (handle, SelectMode.SelectRead, ms * 1000, out error);
+ if (error != 0)
+ return;
+ }
+
+ if (seconds > 0) {
+ LingerOption linger = new LingerOption (true, seconds);
+ SetSocketOption_internal (handle, SocketOptionLevel.Socket, SocketOptionName.Linger, linger, null, 0, out error);
+ /* Not needed, we're closing upon return */
+ //if (error != 0)
+ // return;
+ }
+ }
+
+#endregion
+