2010-05-26 2 views
2

비동기 메서드를 사용하는 .NET 3.5 클라이언트/서버 소켓 인터페이스가 있습니다. 클라이언트는 서버에 연결하고 응용 프로그램이 종료 될 때까지 연결이 열려 있어야합니다. 이 프로토콜은 다음과 같은 패턴으로 구성.NET 3.5 비동기 System.Net.Sockets.Socket에서 데이터 흐름을 정지시키는 것은 무엇입니까?

  1. 전송 STX
  2. 전송 데이터 1
  3. 가 ACK
  4. 전송 데이터 2 (더 많은 데이터하면서 5-6를 반복)
  5. 가 ACK 수신을받을 ACK
  6. 수신
  7. etx

위와 같이 두 개의 데이터 블록을 가진 단일 트랜잭션은 클라이언트로부터 4 개의 전송으로 구성됩니다. etx를 보낸 후에 클라이언트는 더 많은 데이터가 전송 될 때까지 기다렸다가 stx로 다음 전송을 시작합니다. 개인 교환기 간의 연결을 끊거나 각 stx/data/etx 페이로드를 연결하고 싶지 않습니다.

지금 연결 후 클라이언트는 첫 번째 stx를 보내고 하나의 ack를 얻을 수 있지만 그 후에는 더 많은 데이터를 와이어에 넣을 수 없습니다. 어느 쪽도 연결이 끊어지면 소켓은 손상되지 않습니다. 클라이언트 코드는 다음과 같이 심각하게 축약됩니다. 온라인 코드 샘플에서 일반적으로 사용 가능한 패턴을 따르고 있습니다.

private void SendReceive(string data) { 
    // ... 
    SocketAsyncEventArgs completeArgs; 
    completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend); 
    clientSocket.SendAsync(completeArgs); 
    // two AutoResetEvents, one for send, one for receive 
    if (!AutoResetEvent.WaitAll(autoSendReceiveEvents , -1)) 
     Log("failed"); 
    else 
     Log("success"); 
    // ... 
} 

private void OnSend(object sender , SocketAsyncEventArgs e) { 
// ... 
    Socket s = e.UserToken as Socket; 
    byte[] receiveBuffer = new byte[ 4096 ]; 
    e.SetBuffer(receiveBuffer , 0 , receiveBuffer.Length); 
    e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive); 
    s.ReceiveAsync(e); 
// ... 
} 

private void OnReceive(object sender , SocketAsyncEventArgs e) {} 
    // ... 
    if (e.BytesTransferred > 0) { 
     Int32 bytesTransferred = e.BytesTransferred; 
     String received = Encoding.ASCII.GetString(e.Buffer , e.Offset , bytesTransferred); 
     dataReceived += received; 
    } 
    autoSendReceiveEvents[ SendOperation ].Set(); // could be moved elsewhere 
    autoSendReceiveEvents[ ReceiveOperation ].Set(); // releases mutexes 
} 

서버의 코드는 처음받는 것을 제외하고는 매우 유사하며 다음 응답을 보냅니다 - 서버가이 응답을 전송 한 후 연결을 수정 (내가 말할 수있는) 아무것도하지 않습니다. 문제는 클라이언트에서 SendReceive를 두 번째로 누르는 것입니다. 연결이 이미 이상한 상태입니다.

SocketAsyncEventArgs를 유지하고 소켓/연결 기간 동안 동일한 개체를 다시 사용하려면 클라이언트에서 무언가해야합니까? 연결 또는 지정된 교환 기간 동안 어떤 eventargs 객체가 중단되어야하는지 잘 모르겠습니다.

서버에서 데이터 수신을 계속하려면 무언가를해야합니까, 아니면하지 않으시겠습니까?

서버 설정 및 응답 처리는 다음과 같다 :

void Start() { 
    // ... 
    listenSocket.Bind(...); 
    listenSocket.Listen(0); 
    StartAccept(null); // note accept as soon as we start. OK? 
    mutex.WaitOne(); 
} 

void StartAccept(SocketAsyncEventArgs acceptEventArg) { 
    if (acceptEventArg == null) 
    { 
     acceptEventArg = new SocketAsyncEventArgs(); 
     acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted); 
    } 
    Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg); 
    if (!willRaiseEvent) 
     ProcessAccept(acceptEventArg); 
    // ... 
} 

private void OnAcceptCompleted(object sender , SocketAsyncEventArgs e) { 
    ProcessAccept(e); 
} 

private void ProcessAccept(SocketAsyncEventArgs e) { 
    // ... 
    SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs(); 
    readEventArgs.SetBuffer(dataBuffer , 0 , Int16.MaxValue); 
    readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnIOCompleted); 
    readEventArgs.UserToken = e.AcceptSocket; 
    dataReceived = ""; // note server is degraded for single client/thread use 
    // As soon as the client is connected, post a receive to the connection. 
    Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); 
    if (!willRaiseEvent) 
     this.ProcessReceive(readEventArgs); 
    // Accept the next connection request. 
    this.StartAccept(e); 
} 

private void OnIOCompleted(object sender , SocketAsyncEventArgs e) { 
    // switch (e.LastOperation) 
    case SocketAsyncOperation.Receive: 
     ProcessReceive(e); // similar to client code 
     // operate on dataReceived here 
    case SocketAsyncOperation.Send: 
     ProcessSend(e); // similar to client code 
} 

// execute this when a data has been processed into a response (ack, etc) 
private SendResponseToClient(string response) { 
    // create buffer with response 
    // currentEventArgs has class scope and is re-used 
    currentEventArgs.SetBuffer(sendBuffer , 0 , sendBuffer.Length); 
    Boolean willRaiseEvent = currentClient.SendAsync(currentEventArgs); 
    if (!willRaiseEvent) 
     ProcessSend(currentEventArgs); 
} 

닷넷 추적 나타낸다 다음 \ R \ n ABC 보낼 때

Socket#7588182::SendAsync() 
Socket#7588182::SendAsync(True#1) 
Data from Socket#7588182::FinishOperation(SendAsync) 
00000000 : 41 42 43 0D 0A 
Socket#7588182::ReceiveAsync() 
Exiting Socket#7588182::ReceiveAsync()   -> True#1

및 거기 멈춘다. 클라이언트에서 처음 보낸 것처럼 보이지만 서버는 아무런 활동도 보이지 않습니다.

지금은 정보 과부하 일 수 있다고 생각하지만 필요한만큼 자세한 정보를 제공해 드리겠습니다.

감사합니다.

답변

1

첫째로, 나는 알고있을 것이라고 확신하지만, 반복 할 가치가 있습니다. TCP 연결은 바이트 스트림입니다. 각 읽기는 도착한 데이터의 양에 따라 사용하는 버퍼의 크기와 1 바이트 사이에서 되돌릴 수 있습니다. 3 개의 전송 데이터를 전송한다고해서 3 개의 recv가 필요하다는 것을 의미하지는 않습니다. 하나를 가져 오거나 각각의 recv가 1 바이트를 반환 할 수 있습니다. 메시지 프레임에 대해 알고있는 유일한 사람인 것처럼 메시지 프레임을 처리하는 것은 사용자의 몫입니다. TCP는 바이트 스트림입니다.

왜 이러한 이벤트를 사용하고 있습니까? 비동기 데이터 흐름을 사용하지 않으려면 비동기 함수를 사용하지 말고 동기 소켓 함수를 사용하여 매우 간단한 것을 작성하고 비동기 API를 사용하는 무의미한 모든 복잡성을 제거한 다음 동기화 프리미티브를 사용하여 문제를 해결하십시오.