C# Асинхронные сокеты

355
11 января 2018, 19:26

Изучаю пример от MSDN:

Server

class Server  {
    private int m_numConnections;
    private int m_receiveBufferSize;
    BufferManager m_bufferManager;
    const int opsToPreAlloc = 2; 
    Socket listenSocket;
    SocketAsyncEventArgsPool m_readWritePool;
    int m_totalBytesRead; 
    int m_numConnectedSockets;
    Semaphore m_maxNumberAcceptedClients;
    public Server(int numConnections, int receiveBufferSize) {
      m_totalBytesRead = 0;
      m_numConnectedSockets = 0;
      m_numConnections = numConnections;
      m_receiveBufferSize = receiveBufferSize;
      m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc,
          receiveBufferSize);
      m_readWritePool = new SocketAsyncEventArgsPool(numConnections);
      m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
    }
    public void Init()  {
      m_bufferManager.InitBuffer();
      SocketAsyncEventArgs readWriteEventArg;
      for (int i = 0; i < m_numConnections; i++) {
        readWriteEventArg = new SocketAsyncEventArgs();
        readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
        readWriteEventArg.UserToken = new AsyncUserToken();
        m_bufferManager.SetBuffer(readWriteEventArg);
        m_readWritePool.Push(readWriteEventArg);
      }
    }
    public void Start(IPEndPoint localEndPoint) {
      listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
      listenSocket.Bind(localEndPoint);
      listenSocket.Listen(100);
      StartAccept(null);
    }
    public void StartAccept(SocketAsyncEventArgs acceptEventArg) {
      if (acceptEventArg == null) {
        acceptEventArg = new SocketAsyncEventArgs();
        acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
      }
      else {
        acceptEventArg.AcceptSocket = null;
      }
      m_maxNumberAcceptedClients.WaitOne();
      bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
      if (!willRaiseEvent) {
        ProcessAccept(acceptEventArg);
      }
    }
    void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) {
      ProcessAccept(e);        
    }
    private void ProcessAccept(SocketAsyncEventArgs e) {
      Interlocked.Increment(ref m_numConnectedSockets);
      SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop();
      ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket;
      bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
      if (!willRaiseEvent) {
        ProcessReceive(readEventArgs);
      }
      StartAccept(e);
    }
    void IO_Completed(object sender, SocketAsyncEventArgs e) {
      switch (e.LastOperation) {
        case SocketAsyncOperation.Receive:
          ProcessReceive(e);
          break;
        case SocketAsyncOperation.Send:
          ProcessSend(e);
          break;
        default:
          throw new ArgumentException("The last operation completed on the socket was not a receive or send");
      }
    }
    private void ProcessReceive(SocketAsyncEventArgs e) {
      AsyncUserToken token = (AsyncUserToken)e.UserToken;
      if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) {
        Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred);
        e.SetBuffer(e.Offset, e.BytesTransferred);
        bool willRaiseEvent = token.Socket.SendAsync(e);
        if (!willRaiseEvent) {
          ProcessSend(e);
        }
      }
      else {
        CloseClientSocket(e);
      }
    }
    private void ProcessSend(SocketAsyncEventArgs e) {
      if (e.SocketError == SocketError.Success) {
        AsyncUserToken token = (AsyncUserToken)e.UserToken;
        bool willRaiseEvent = token.Socket.ReceiveAsync(e);
        if (!willRaiseEvent) {
          ProcessReceive(e);
        }
      }
      else {
        CloseClientSocket(e);
      }
    }
    private void CloseClientSocket(SocketAsyncEventArgs e) {
      AsyncUserToken token = e.UserToken as AsyncUserToken;
      try {
        token.Socket.Shutdown(SocketShutdown.Send);
      }
      catch (Exception) { }
      token.Socket.Close();
      Interlocked.Decrement(ref m_numConnectedSockets);
      m_maxNumberAcceptedClients.Release();
      m_readWritePool.Push(e);
    }
  }

SocketAsyncEventArgsPool

class SocketAsyncEventArgsPool {
    Stack<SocketAsyncEventArgs> m_pool;
    public SocketAsyncEventArgsPool(int capacity) {
        m_pool = new Stack<SocketAsyncEventArgs>(capacity);
    }
    public void Push(SocketAsyncEventArgs item) {
      if (item == null) { throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null"); }
      lock (m_pool) {
        m_pool.Push(item);
      }
    }
    public SocketAsyncEventArgs Pop() {
      lock (m_pool) {
        return m_pool.Pop();
      }
    }
    public int Count {
      get { return m_pool.Count; }
    }
  }

BufferManager

class BufferManager {
    int m_numBytes;          
    byte[] m_buffer;               
    Stack<int> m_freeIndexPool;    
    int m_currentIndex;
    int m_bufferSize;
    public BufferManager(int totalBytes, int bufferSize) {
      m_numBytes = totalBytes;
      m_currentIndex = 0;
      m_bufferSize = bufferSize;
      m_freeIndexPool = new Stack<int>();
    }
    public void InitBuffer() {
      m_buffer = new byte[m_numBytes];
    }
    public bool SetBuffer(SocketAsyncEventArgs args) {
      if (m_freeIndexPool.Count > 0) {
        args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
      }
      else {
        if ((m_numBytes - m_bufferSize) < m_currentIndex) {
          return false;
        }
        args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
        m_currentIndex += m_bufferSize;
      }
      return true;
    }
    public void FreeBuffer(SocketAsyncEventArgs args) {
      m_freeIndexPool.Push(args.Offset);
      args.SetBuffer(null, 0, 0);
    }
  }
 internal class AsyncUserToken {
    public System.Net.Sockets.Socket Socket { get; set; }
 }

Смортю здесь используют Stack, у меня вопрос, как работать с каждым сокетом отдельно? Как идентифицировать соединение и например отсоединить? Буду рад помощи

Цикл

public void sendToAll() { 
  foreach(SocketAsyncEventArgs eventargs in m_pool) { 
    AsyncUserToken token = eventargs.UserToken as AsyncUserToken; 
    token.Socket.Send(Encoding.ASCII.GetBytes("Test")); 
  } 
}

Answer 1

Не работал с сокетами в C#, но там как и везде обязан быть некий идентификатор соединения, доступный из сохраняемого списка и при каждой обработке сообщения.

В приведённом коде на такой идентификатор смахивает поле UserToken объекта SocketAsyncEventArgs. Это значение можно легко получать (оно есть в аргументах всех методов работы с клиентами), и с его же помощью можно манипулировать соединением, отправлять сообщения.

MSDN говорит, что класс Socket имеет метод Close() для отключения соединения. Объект UserToken как раз имеет соответствующее поле Socket. И оно используется в методе CloseClientSocket, который, в свою очередь, и производит нужное Вам действие — отключает клиента, при этом, вежливо его уведомляя и удаляя соединение из списка SocketAsyncEventArgsPool.

READ ALSO
WCF, WCF Data Services - понимание

WCF, WCF Data Services - понимание

Привет! Изучаю службы WCF, море информации в короткие сроки и накопилось множество вопросов, которые надо уложить в головеИспользую EF 6 (Database...

196
В ком. порт приходят нe те значения

В ком. порт приходят нe те значения

Мне необходимо собрать некую программу для управления светом и тд

228
Как в С# получить ответ от метода из Objective-C

Как в С# получить ответ от метода из Objective-C

Создаю игру в Unity3D, есть модуль на Objective-C, который возвращает bool

234
Управляемая vs неуправляемая память

Управляемая vs неуправляемая память

Возможно, это глупый вопросЧитая книгу Рихтера CLR via C#, в главе 14 наткнулся на неуправляемую память

239