2017-11-15 11 views
-1

면책 조항 : 내 C#을 내 C++ 난 내의 구성 요소에 대한 테스트 응용 프로그램을 작성하기 위해 C#으로 비동기 소켓 작업을 수행하는 방법을 배우려고 노력하고인터 로킹 대기 == 0?

만큼 좋은 꿈에도 생각하지 않습니다. 하여 TcpClient를 사용하여 내 이전의 시도는 실패로 끝났다 당신이 여기에에 뛰어난 질문을 읽을 수 있습니다 : 그 작업을 얻을 수 없었다

TcpClient.NetworkStream Async operations - Canceling/Disconnect

Detect errors with NetworkStream.WriteAsync

, 이후, 나는 Socket.BeginX 및 소켓을 사용하여 시도 .EndX 대신. 나는 훨씬 더 나아 갔다. 내 문제는 지금은 다시 종료하고 소켓에 가까운 호출하는, 분리 시간을 때 아래 목록에, 비동기 작업이 여전히 뛰어난 있다는 것입니다 그들은 개체 배치 예외가 발생합니다 또는 객체가 null의 예외로 설정됩니다.

내가 여기에 대한 유사한 게시물을 발견 :

After disposing async socket (.Net) callbacks still get called

그러나, 나는 그 대답을 허용하지 않습니다, 당신은 에 대한 예외를 사용하는 경우 것이 동작을 목적으로하기 때문에, 다음 1) 그들은 뛰어난 없습니다 2) 당신은 예외가 의도 한 경우에 발생 된 경우 말할 수 또는 당신이 실제로 소켓 이외의 배치 오브젝트, 또는 비동기 방식에서 null 참조를 사용하기 때문에가 발생 된 경우.

비동기 소켓 코드가있는 C++에서 인터록이있는 비동기 작업의 수를 추적하고 연결을 끊을 시간이 왔을 때 종료를 호출 한 다음 인터록이 0이 될 때까지 기다린 다음 내가 필요한 모든 회원을 닫고 파괴하라.

나는 다음과 같은 목록에 C#으로 완료되지 않은 모든 비동기 작업에 내 분리 방법에서 대기 가겠어요 어떻게

? 홈페이지

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

using log4net; 
using System.Net.Sockets; 
using System.Net; 

namespace IntegrationTests 
{ 
    public class Client2 
    { 
     class ReceiveContext 
     { 
      public Socket m_socket; 
      public const int m_bufferSize = 1024; 
      public byte[] m_buffer = new byte[m_bufferSize]; 
     } 

     private static readonly ILog log = LogManager.GetLogger("root"); 

     static private ulong m_lastId = 1; 

     private ulong m_id; 
     private string m_host; 
     private uint m_port; 
     private uint m_timeoutMilliseconds; 
     private string m_clientId; 
     private Socket m_socket; 
     private uint m_numOutstandingAsyncOps; 

     public Client2(string host, uint port, string clientId, uint timeoutMilliseconds) 
     { 
      m_id      = m_lastId++; 
      m_host     = host; 
      m_port     = port; 
      m_clientId    = clientId; 
      m_timeoutMilliseconds = timeoutMilliseconds; 
      m_socket     = null; 
      m_numOutstandingAsyncOps = 0; 
     } 

     ~Client2() 
     { 
      Disconnect(); 
     } 

     public void Connect() 
     { 
      IPHostEntry ipHostInfo = Dns.GetHostEntry(m_host); 
      IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray(); 
      IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray(); 
      IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], (int)m_port); 

      m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
      m_socket.ReceiveTimeout = (int)m_timeoutMilliseconds; 
      m_socket.SendTimeout = (int)m_timeoutMilliseconds; 

      try 
      { 
       m_socket.Connect(endpoint); 

       log.Info(string.Format("Connected to: {0}", m_socket.RemoteEndPoint.ToString())); 

       // Issue the next async receive 
       ReceiveContext context = new ReceiveContext(); 
       context.m_socket = m_socket; 
       m_socket.BeginReceive(context.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context); 
      } 
      catch (Exception e) 
      { 
       // Error 
       log.Error(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}" 
             , m_id, e.ToString())); 
      } 
     } 

     public void Disconnect() 
     { 
      if (m_socket != null) 
      { 
       m_socket.Shutdown(SocketShutdown.Both); 

       // TODO - <--- Error here in the callbacks where they try to use the socket and it is disposed 
       //  We need to wait here until all outstanding async operations complete 
       //  Should we use Interlocked to keep track of them and wait on it somehow? 
       m_socket.Close(); 
       m_socket = null; 
      } 
     } 

     public void Login() 
     { 
      string loginRequest = string.Format("loginstuff{0})", m_clientId); 
      var data = Encoding.ASCII.GetBytes(loginRequest); 

      m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket); 
     } 

     public void MakeRequest(string thingy) 
     { 
      string message = string.Format("requeststuff{0}", thingy); 
      var data = Encoding.ASCII.GetBytes(message); 

      m_socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), m_socket); 
     } 

     void OnReceive(IAsyncResult asyncResult) 
     { 
      ReceiveContext context = (ReceiveContext)asyncResult.AsyncState; 

      string data = null; 
      try 
      { 
       int bytesReceived = context.m_socket.EndReceive(asyncResult); 
       data = Encoding.ASCII.GetString(context.m_buffer, 0, bytesReceived); 

       ReceiveContext newContext = new ReceiveContext(); 
       newContext.m_socket = context.m_socket; 

       m_socket.BeginReceive(newContext.m_buffer, 0, ReceiveContext.m_bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext); 
      } 
      catch(SocketException e) 
      { 
       if(e.SocketErrorCode == SocketError.ConnectionAborted) // Check if we disconnected on our end 
       { 
        return; 
       } 
      } 
      catch (Exception e) 
      { 
       // Error 
       log.Error(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}" 
             , m_id, e.ToString())); 
      } 
     } 

     void OnSend(IAsyncResult asyncResult) 
     { 
      Socket socket = (Socket)asyncResult.AsyncState; 

      try 
      { 
       int bytesSent = socket.EndSend(asyncResult); 
      } 
      catch(Exception e) 
      { 
       log.Error(string.Format("Client #{0} Exception caught OnSend. Exception: {1}" 
             , m_id, e.ToString())); 
      } 
     } 
    } 
} 

:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

using log4net; 
using log4net.Config; 

namespace IntegrationTests 
{ 
    class Program 
    { 
     private static readonly ILog log = LogManager.GetLogger("root"); 

     static void Main(string[] args) 
     { 
      try 
      { 
       XmlConfigurator.Configure(); 
       log.Info("Starting Component Integration Tests..."); 

       Client2 client = new Client2("127.0.0.1", 24001, "MyClientId", 60000); 
       client.Connect(); 
       client.Login(); 
       client.MakeRequest("StuffAndPuff"); 

       System.Threading.Thread.Sleep(60000); // Sim work until user shutsdown 

       client.Disconnect(); 
      } 
      catch (Exception e) 
      { 
       log.Error(string.Format("Caught an exception in main. Exception: {0}" 
             , e.ToString())); 
      } 
     } 
    } 
} 

편집 :

여기 내 최선을 다해 EVK에 의해 제안 된 대답을 사용하여 내 추가 시도가있다. 내가 말할 수있는 한 잘 작동합니다. 이와

문제는 내가 기본적으로 인해 카운터 또는 소켓의 상태를 변경 할 수있는 모든 주위에 고정 할 수있는 요구 사항, 동기 호출에 비동기 있었다 모든 것을 만들어 같은 느낌이다. 다시 말하지만, 저는 C++에 비해 C#에 초보자입니다. 그래서 그의 대답을 해석하는 표를 완전히 놓친 경우를 지적하십시오.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace IntegrationTests 
{ 
    public class Client 
    { 
     class ReceiveContext 
     { 
      public const int  _bufferSize = 1024; 
      public byte[]  _buffer  = new byte[_bufferSize]; // Contains bytes from one receive 
      public StringBuilder _stringBuilder = new StringBuilder(); // Contains bytes for multiple receives in order to build message up to delim 
     } 

     private static readonly ILog _log = LogManager.GetLogger("root"); 

     static private ulong _lastId = 1; 
     private ulong _id; 

     protected string   _host; 
     protected int   _port; 
     protected int   _timeoutMilliseconds; 
     protected string   _sessionId; 
     protected Socket   _socket; 
     protected object   _lockNumOutstandingAsyncOps; 
     protected int   _numOutstandingAsyncOps; 
     private bool    _disposed = false; 

     public Client(string host, int port, string sessionId, int timeoutMilliseconds) 
     { 
      _id       = _lastId++; 
      _host      = host; 
      _port      = port; 
      _sessionId     = sessionId; 
      _timeoutMilliseconds  = timeoutMilliseconds; 
      _socket      = null; 
      _numOutstandingAsyncOps  = 0; 
      _lockNumOutstandingAsyncOps = new object(); 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 

     protected virtual void Dispose(bool disposing) 
     { 
      if(_disposed) 
      { 
       return; 
      } 

      if (disposing) 
      { 
       _socket.Close(); 
      } 

      _disposed = true; 
     } 

     public void Connect() 
     { 
      lock (_lockNumOutstandingAsyncOps) 
      { 
       IPHostEntry ipHostInfo = Dns.GetHostEntry(_host); 
       IPAddress[] ipV4Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray(); 
       IPAddress[] ipV6Addresses = ipHostInfo.AddressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).ToArray(); 
       IPEndPoint endpoint = new IPEndPoint(ipV4Addresses[0], _port); 

       _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
       _socket.ReceiveTimeout = _timeoutMilliseconds; 
       _socket.SendTimeout = _timeoutMilliseconds; 

       try 
       { 
        _socket.Connect(endpoint); 
       } 
       catch (Exception e) 
       { 
        // Error 
        Debug.WriteLine(string.Format("Client #{0} Exception caught OnConnect. Exception: {1}" 
              , _id, e.ToString())); 
        return; 
       } 

       Debug.WriteLine(string.Format("Client #{0} connected to: {1}", _id, _socket.RemoteEndPoint.ToString())); 

       // Issue the first async receive 
       ReceiveContext context = new ReceiveContext(); 

       ++_numOutstandingAsyncOps; 
       _socket.BeginReceive(context._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), context); 
      } 
     } 

     public void Disconnect() 
     { 
      if (_socket != null) 
      { 
       // We need to wait here until all outstanding async operations complete 
       // In order to avoid getting 'Object was disposed' exceptions in those async ops that use the socket 
       lock(_lockNumOutstandingAsyncOps) 
       { 
        Debug.WriteLine(string.Format("Client #{0} Disconnecting...", _id)); 

        _socket.Shutdown(SocketShutdown.Both); 

        while (_numOutstandingAsyncOps > 0) 
        { 
         Monitor.Wait(_lockNumOutstandingAsyncOps); 
        } 

        _socket.Close(); 
        _socket = null; 
       } 
      } 
     } 

     public void Login() 
     { 
      lock (_lockNumOutstandingAsyncOps) 
      { 
       if (_socket != null && _socket.Connected) 
       { 
        string loginRequest = string.Format("loginstuff{0}", _clientId); 
        var data = Encoding.ASCII.GetBytes(loginRequest); 

        Debug.WriteLine(string.Format("Client #{0} Sending Login Request: {1}" 
              , _id, loginRequest)); 

        ++_numOutstandingAsyncOps; 
        _socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket); 
       } 
       else 
       { 
        Debug.WriteLine(string.Format("Client #{0} Login was called, but Socket is null or no longer connected." 
              , _id)); 
       } 
      } 
     } 

     public void MakeRequest(string thingy) 
     { 
      lock (_lockNumOutstandingAsyncOps) 
      { 
       if (_socket != null && _socket.Connected) 
       { 
        string message = string.Format("requeststuff{0}", thingy); 
        var data = Encoding.ASCII.GetBytes(message); 

        Debug.WriteLine(string.Format("Client #{0} Sending Request: {1}" 
              , _id, message)); 

        ++_numOutstandingAsyncOps; 
        _socket.BeginSend(data, 0, data.Length, 0, new AsyncCallback(OnSend), _socket); 
       } 
       else 
       { 
        Debug.WriteLine(string.Format("Client #{0} MakeRequest was called, but Socket is null or no longer connected." 
              , _id)); 
       } 
      } 
     } 

     protected void OnReceive(IAsyncResult asyncResult) 
     { 
      lock (_lockNumOutstandingAsyncOps) 
      { 
       ReceiveContext context = (ReceiveContext)asyncResult.AsyncState; 

       string data = null; 

       try 
       { 
        int bytesReceived = _socket.EndReceive(asyncResult); 
        data = Encoding.ASCII.GetString(context._buffer, 0, bytesReceived); 

        // If the remote host shuts down the Socket connection with the Shutdown method, and all available data has been received, 
        // the EndReceive method will complete immediately and return zero bytes 
        if (bytesReceived > 0) 
        { 
         StringBuilder stringBuilder = context._stringBuilder.Append(data); 

         int index = -1; 
         do 
         { 
          index = stringBuilder.ToString().IndexOf("#"); 
          if (index != -1) 
          { 
           string message = stringBuilder.ToString().Substring(0, index + 1); 
           stringBuilder.Remove(0, index + 1); 

           Debug.WriteLine(string.Format("Client #{0} Received Data: {1}" 
                 , _id, message)); 
          } 
         } while (index != -1); 
        } 
       } 
       catch (SocketException e) 
       { 
        // Check if we disconnected on our end 
        if (e.SocketErrorCode == SocketError.ConnectionAborted) 
        { 
         // Ignore 
        } 
        else 
        { 
         // Error 
         Debug.WriteLine(string.Format("Client #{0} SocketException caught OnReceive. Exception: {1}" 
               , _id, e.ToString())); 
         Disconnect(); 
        } 
       } 
       catch (Exception e) 
       { 
        // Error 
        Debug.WriteLine(string.Format("Client #{0} Exception caught OnReceive. Exception: {1}" 
              , _id, e.ToString())); 
        Disconnect(); 
       } 
       finally 
       { 
        --_numOutstandingAsyncOps; 
        Monitor.Pulse(_lockNumOutstandingAsyncOps); 
       } 
      } 

      // Issue the next async receive 
      lock (_lockNumOutstandingAsyncOps) 
      { 
       if (_socket != null && _socket.Connected) 
       { 
        ++_numOutstandingAsyncOps; 

        ReceiveContext newContext = new ReceiveContext(); 
        _socket.BeginReceive(newContext._buffer, 0, ReceiveContext._bufferSize, SocketFlags.None, new AsyncCallback(OnReceive), newContext); 
       } 
      } 
     } 

     protected void OnSend(IAsyncResult asyncResult) 
     { 
      lock (_lockNumOutstandingAsyncOps) 
      { 
       try 
       { 
        int bytesSent = _socket.EndSend(asyncResult); 
       } 
       catch (Exception e) 
       { 
        Debug.WriteLine(string.Format("Client #{0} Exception caught OnSend. Exception: {1}" 
              , _id, e.ToString())); 
        Disconnect(); 
       } 
       finally 
       { 
        --_numOutstandingAsyncOps; 
        Monitor.Pulse(_lockNumOutstandingAsyncOps); 
       } 
      } 
     } 
    } 
} 
+2

참고. 당신은 당신의 객체에 파이널 라이저를 전혀 가지고 있지 않아야합니다. – Servy

+0

는 ['CountdownEvent'] (https://docs.microsoft.com/dotnet/api/system.threading.countdownevent) 당신의 친구입니다. 당신이 C++ 배경에서있는 한 –

+1

관리 파이 나라와 C++ 소멸자를 혼동하지 마십시오, Servy는 말에 추가합니다.일부 문서에서 C#이 구문을 빌리고 심지어 "destructor"라는 단어를 사용한다는 사실은 실제로 매우 불행합니다. 'IDisposable'는 C#이 결정 론적 정리에 사용하는 것이며 finalizer는 거의 필요하지 않습니다. 개체가 가비지 수집 될 때 관리되지 않는 리소스를 릴리스하기위한 최종 단초가됩니다. 일반적으로 이러한 개체는 이미 호출 된 Dispose를 호출해야합니다. finalizer가 표시되지 않음). –

답변

1

당신은 그것을 위해 Monitor.WaitMonitor.Pulse을 사용할 수 있습니다 : 파이 나라가 관리되지 않는 리소스가 아닌 관리 자원을 청소하는 것을

static int _outstandingOperations; 
static readonly object _lock = new object(); 
static void Main() { 
    for (int i = 0; i < 100; i++) { 
     var tmp = i; 
     Task.Run(() => 
     { 
      lock (_lock) { 
       _outstandingOperations++; 
      } 
      // some work 
      Thread.Sleep(new Random(tmp).Next(0, 5000)); 
      lock (_lock) { 
       _outstandingOperations--; 
       // notify condition might have changed 
       Monitor.Pulse(_lock); 
      } 
     }); 
    } 

    lock (_lock) { 
     // condition check 
     while (_outstandingOperations > 0) 
      // will wait here until pulsed, lock will be released during wait 
      Monitor.Wait(_lock); 
    } 
} 
+0

이것에 대한 시도와 그것에 관련된 질문을 편집하려고한다. –

+0

@ChristopherPisz 예를 들었을 때처럼 전체 작업을 잠글 필요가 없습니다. 내 대답처럼 - 늘리거나 줄이는 필드 주위에서만 - 스레드 수면을 모방하는 작업을 잠그지 않습니다.) 이를 별도의 메소드 (IncAnynsOperations/DecAsyncOperations)로 이동할 수 있습니다. – Evk

+0

비동기 메소드가 여전히 소켓에서 작동 중이므로 소켓의 상태도 잠겨 야하므로 disconnect가 호출 될 때 count의 증가 및 감소 주변에서만 잠기고 disconnect가 호출 될 때 동일한 예외가 발생합니다. –