Oto odpowiedź, która pomoże Ci zacząć - czyli bardziej początkujący niż mój blog post.
. NET ma wzorzec asynchroniczny, który obraca się wokół wywołania Początek * i Koniec *. Na przykład - BeginReceive
i EndReceive
. Niemal zawsze mają one swój odpowiednik inny niż asynchroniczny (w tym przypadku Receive
); i osiągnąć ten sam cel.
Najważniejszą rzeczą do zapamiętania jest to, że te gniazda nie tylko wywołują asynchroniczne połączenie - ujawniają coś, co nazywa się IOCP (IO Completion Ports, Linux/Mono ma te dwa, ale ja zapominam nazwę), co jest niezwykle ważne używać na serwerze; sednem tego, co robi IOCP, jest to, że aplikacja nie zużywa wątku podczas oczekiwania na dane.
Jak użyć deseniu Begin/End
Każdy Begin * metoda będzie mieć dokładnie 2 więcej argumentów w comparisson na to non-asynchroniczny odpowiednik.Pierwszy to AsyncCallback, drugi to obiekt. Te dwie rzeczy oznaczają: "tutaj jest metoda wywoływania, gdy skończysz" i "tutaj jest kilka danych, których potrzebuję w tej metodzie." Metoda, która zostanie wywołana, zawsze ma tę samą sygnaturę, w metodzie tej wywołuje się odpowiednik End *, aby uzyskać wynik, który byłby wynikiem synchronicznej synchronizacji. Tak więc na przykład:
private void BeginReceiveBuffer()
{
_socket.BeginReceive(buffer, 0, buffer.Length, BufferEndReceive, buffer);
}
private void EndReceiveBuffer(IAsyncResult state)
{
var buffer = (byte[])state.AsyncState; // This is the last parameter.
var length = _socket.EndReceive(state); // This is the return value of the method call.
DataReceived(buffer, 0, length); // Do something with the data.
}
Co się dzieje tutaj jest Net rozpoczyna czeka na dane z gniazda, tak szybko, jak to robi danych wywołuje EndReceiveBuffer
i przechodzi przez „niestandardowych danych” (w tym przypadku buffer
) do niego przez state.AsyncResult
. Gdy zadzwonisz pod numer EndReceive
, otrzymasz z powrotem długość otrzymanych danych (lub wyrzucisz wyjątek, jeśli coś nie działa).
Lepsze Wzór dla gniazd
Ta forma daje centralną obsługę błędów - może być stosowany wszędzie tam, gdzie wzór asynchroniczny owija strumieniowej jak „coś” (np TCP pojawia się w kolejności, w jakiej została wysłana , więc może być postrzegany jako obiekt Stream
).
private Socket _socket;
private ArraySegment<byte> _buffer;
public void StartReceive()
{
ReceiveAsyncLoop(null);
}
// Note that this method is not guaranteed (in fact
// unlikely) to remain on a single thread across
// async invocations.
private void ReceiveAsyncLoop(IAsyncResult result)
{
try
{
// This only gets called once - via StartReceive()
if (result != null)
{
int numberOfBytesRead = _socket.EndReceive(result);
if(numberOfBytesRead == 0)
{
OnDisconnected(null); // 'null' being the exception. The client disconnected normally in this case.
return;
}
var newSegment = new ArraySegment<byte>(_buffer.Array, _buffer.Offset, numberOfBytesRead);
// This method needs its own error handling. Don't let it throw exceptions unless you
// want to disconnect the client.
OnDataReceived(newSegment);
}
// Because of this method call, it's as though we are creating a 'while' loop.
// However this is called an async loop, but you can see it the same way.
_socket.BeginReceive(_buffer.Array, _buffer.Offset, _buffer.Count, SocketFlags.None, ReceiveAsyncLoop, null);
}
catch (Exception ex)
{
// Socket error handling here.
}
}
Przyjmowanie Wiele Połączenia
Co ogólnie zrobić, to napisać klasy, który zawiera gniazda itd. (Jak również swoją pętlę asynchroniczny) i utworzyć dla każdego klienta. Tak na przykład:
public class InboundConnection
{
private Socket _socket;
private ArraySegment<byte> _buffer;
public InboundConnection(Socket clientSocket)
{
_socket = clientSocket;
_buffer = new ArraySegment<byte>(new byte[4096], 0, 4096);
StartReceive(); // Start the read async loop.
}
private void StartReceive() ...
private void ReceiveAsyncLoop() ...
private void OnDataReceived() ...
}
Każde połączenie klient powinien być śledzone przez klasy serwera (tak, że można odłączyć je równo, gdy serwer zostanie zamknięty, a także poszukiwania/wyszukać je).
Bezwstydna wtyczka: http://jonathan.dickinsons.co.za/blog/2011/02/net-sockets-and-you/ - dotyka krótko pętli asynchronicznej; i zawiera prawdziwą implementację (nie powinieneś używać 'ThreadPool' tak, jak zasugerował @Jalal). –