비동기 소켓을 효과적으로 구현하려면 각 소켓에 1 개 이상의 SocketAsyncEventArgs가 필요합니다. 각 SocketAsyncEventArgs의 byte [] 버퍼에도 문제가 있습니다. 요컨대, 관리되는 네이티브 전환 (전송/수신)이 발생할 때마다 바이트 버퍼가 고정됩니다. 필요에 따라 SocketAsyncEventArgs 및 바이트 버퍼를 할당하면 조각화 및 GC가 고정 된 메모리를 압축 할 수 없기 때문에 많은 클라이언트에서 OutOfMemoryExceptions를 실행할 수 있습니다.
가장 좋은 방법은 응용 프로그램이 처음 시작될 때 많은 바이트와 SocketAsyncEventArgs를 할당하는 SocketBufferPool 클래스를 만드는 것입니다. 이렇게하면 고정 된 메모리가 인접하게됩니다. 그런 다음 필요에 따라 풀에서 버퍼를 단순히 다시 사용하십시오.
실제로 리소스 배포를 관리하기 위해 SocketAsyncEventArgs 및 SocketBufferPool 클래스 주위에 래퍼 클래스를 만드는 것이 가장 좋습니다.일례로서
은 여기 BeginReceive 메소드에 대한 코드는 다음
private void BeginReceive(Socket socket)
{
Contract.Requires(socket != null, "socket");
SocketEventArgs e = SocketBufferPool.Instance.Alloc();
e.Socket = socket;
e.Completed += new EventHandler<SocketEventArgs>(this.HandleIOCompleted);
if (!socket.ReceiveAsync(e.AsyncEventArgs)) {
this.HandleIOCompleted(null, e);
}
}
및 여기 HandleIOCompleted 방법이다 : 위의 코드를 올릴 것이다 TcpSocket 클래스에 포함
private void HandleIOCompleted(object sender, SocketEventArgs e)
{
e.Completed -= this.HandleIOCompleted;
bool closed = false;
lock (this.sequenceLock) {
e.SequenceNumber = this.sequenceNumber++;
}
switch (e.LastOperation) {
case SocketAsyncOperation.Send:
case SocketAsyncOperation.SendPackets:
case SocketAsyncOperation.SendTo:
if (e.SocketError == SocketError.Success) {
this.OnDataSent(e);
}
break;
case SocketAsyncOperation.Receive:
case SocketAsyncOperation.ReceiveFrom:
case SocketAsyncOperation.ReceiveMessageFrom:
if ((e.BytesTransferred > 0) && (e.SocketError == SocketError.Success)) {
this.BeginReceive(e.Socket);
if (this.ReceiveTimeout > 0) {
this.SetReceiveTimeout(e.Socket);
}
} else {
closed = true;
}
if (e.SocketError == SocketError.Success) {
this.OnDataReceived(e);
}
break;
case SocketAsyncOperation.Disconnect:
closed = true;
break;
case SocketAsyncOperation.Accept:
case SocketAsyncOperation.Connect:
case SocketAsyncOperation.None:
break;
}
if (closed) {
this.HandleSocketClosed(e.Socket);
}
SocketBufferPool.Instance.Free(e);
}
DataReceived & DataSent 이벤트입니다. 주목해야 할 한 가지는 SocketAsyncOperation.ReceiveMessageFrom : block; 소켓에 오류가 없으면 다른 BeginReceive()가 즉시 시작되어 풀에서 다른 SocketEventArgs를 할당합니다.
또 다른 중요한 메모는 HandleIOComplete 메서드에 설정된 SocketEventArgs SequenceNumber 속성입니다. 비동기 요청은 대기열에있는 순서대로 완료되지만 다른 스레드 경쟁 조건의 영향을받을 수 있습니다. DataReceived 이벤트를 발생시키기 전에 코드가 BeginReceive를 호출하기 때문에 BeginReceive를 호출 한 후 이벤트를 재개하기 전에 원본 IOCP를 처리하는 스레드가 차단되고 두 번째 비동기 수신이 새 스레드에서 완료되면 DataReceived 이벤트가 먼저 발생합니다. 이것은 매우 드문 경우이지만 발생할 수 있으며 SequenceNumber 속성은 소비하는 응용 프로그램에 데이터가 올바른 순서로 처리되도록하는 기능을 제공합니다.
다른주의해야 할 영역 중 하나는 비동기 전송입니다. 종종 비동기 전송 요청은 동기식으로 완료되며 호출이 동기식으로 완료되면 SendAsync는 false를 반환하고 성능을 심각하게 저하시킬 수 있습니다. IOCP에서 다시 발생하는 비동기 호출의 추가 오버 헤드는 실제로 동기 호출을 사용하는 것보다 성능이 떨어질 수 있습니다. 비동기 호출은 스택에서 동기 호출이 발생하는 동안 두 개의 커널 호출과 힙 할당을 필요로합니다.
if (!socket.ReceiveAsync(e.AsyncEventArgs)) {
this.HandleIOCompleted(null, e);
}
을하지만 그렇게하면 오류가 발생합니다 :이 도움이
희망, 빌 코드에서