Vì vậy, đây là câu trả lời sẽ giúp bạn bắt đầu - cấp độ mới bắt đầu hơn số blog post của tôi.
.Net có mẫu không đồng bộ xoay quanh cuộc gọi Bắt đầu * và Kết thúc *. Ví dụ: BeginReceive
và EndReceive
. Họ gần như luôn luôn có đối tác không đồng bộ của họ (trong trường hợp này là Receive
); và đạt được mục tiêu tương tự. Điều quan trọng nhất cần nhớ là những ổ cắm làm nhiều hơn là chỉ làm cho cuộc gọi không đồng bộ - chúng lộ ra một cái gì đó gọi là IOCP (Cổng IO hoàn thành, Linux/Mono có hai nhưng tôi quên tên) đó là cực kỳ quan trọng để sử dụng trên máy chủ; Điểm mấu chốt của những gì IOCP làm là ứng dụng của bạn không tiêu thụ một luồng trong khi nó chờ dữ liệu.
Làm thế nào để sử dụng Các Begin/End Pattern
Mỗi Begin * phương pháp sẽ có chính xác 2 nhiều tranh cãi trong comparisson để nó không async đối tác.Đầu tiên là một AsyncCallback, thứ hai là một đối tượng. Ý nghĩa của hai điều này là "đây là một phương thức để gọi khi bạn hoàn thành" và "đây là một số dữ liệu tôi cần bên trong phương thức đó". Phương thức được gọi luôn có cùng chữ ký, bên trong phương thức này bạn gọi là đối tác End * để có được kết quả nếu bạn đã thực hiện nó một cách đồng bộ. Vì vậy, ví dụ:
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.
}
gì xảy ra ở đây là Net bắt đầu chờ đợi dữ liệu từ các ổ cắm, ngay khi nó nhận dữ liệu mà họ gọi EndReceiveBuffer
và đi qua 'dữ liệu tùy chỉnh' (trong trường hợp này buffer
) để nó qua số state.AsyncResult
. Khi bạn gọi EndReceive
, nó sẽ cung cấp cho bạn độ dài của dữ liệu đã nhận được (hoặc ném một ngoại lệ nếu có điều gì đó không thành công). Pattern
tốt hơn cho Sockets
Hình thức này sẽ cung cấp cho bạn xử lý lỗi trung tâm - nó có thể được sử dụng bất cứ nơi nào mô hình async kết thúc tốt đẹp một 'điều' dòng giống (ví dụ TCP đến theo thứ tự nó đã được gửi , vì vậy nó có thể được xem như một đối tượng 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.
}
}
Chấp nhận Nhiều Connections
gì bạn thường làm là viết một lớp có chứa ổ cắm vv (cũng như vòng lặp async của bạn) của bạn và tạo một cho mỗi khách hàng. Vì vậy, ví dụ:
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() ...
}
Mỗi kết nối khách hàng nên được theo dõi bởi lớp máy chủ của bạn (để bạn có thể ngắt kết nối chúng sạch khi máy chủ tắt, cũng như tìm kiếm/tìm họ).
Phích cắm không biết xấu hổ: http://jonathan.dickinsons.co.za/blog/2011/02/net-sockets-and-you/ - nó chạm nhanh vào vòng lặp async; và có triển khai thực sự (bạn không nên sử dụng 'ThreadPool' như @Jalal được đề xuất). –