2012-09-04 4 views
2

저는 C#으로 작성한 프록시 서버가 있습니다. 또한이 C# 프록시를 통해 비디오 서버에서 MJPEG 데이터를받는 Java 애플릿이 있습니다. 내 문제는 비디오 서버에서 현재 MJPEG 데이터를 더 이상 사용할 수 없을 때 프록시가 차단 된 읽기 호출에 걸려 종료 할 수 없다는 것입니다. 멀티 스레드 프로듀서/고객

// write the forwarded output 
// blocking on remoteServerResponseStream.Read 
while (m_running && (read = remoteServerResponseStream.Read(buffer, 0, buffer.Length)) > 0) 
{ 
    bytesRead += read; 

    output.Write(buffer, 0, read); 

    output.Flush(); 
} 

(위 코드에서 변수 output) 스트림 폐쇄 자바 애플릿에 의해 종료한다. 그러나 프록시가 데이터를 기다리는 remoteServerResponseStream.Read에 멈춘 상태에서 닫기 요청을 승인하지 않으므로 Java 애플릿은이 연결을 닫을 수 없습니다.

나는이 문제에 일주일 동안 고착했다. 나는 해결책을 생각해 보았을지도 모르지만 그것이 효과가 있을지 모르겠다. 이것에 대한 의견을 듣고 싶습니다.

내 생각은 removeServerResponseStream.Read을 다른 스레드에두고 공유 대기열을 사용하여 데이터를 전송하는 것이 었습니다. 스레드는 데이터를 읽고 대기열에 배치합니다. 내 메인 스레드가 대기열에있는 모든 데이터를 output으로 전달합니다. 이런 식으로, 나는 항상 output.CanWrite이 false가되었는지를 지속적으로 확인할 수 있습니다.이 경우 읽기 스레드를 중단 할 수 있습니다 (이것이 차단 스트림 읽기를 방해하는 유일한 방법입니다). 이것이 가능한 솔루션입니까? 그렇다면 사용 가능한 데이터 대기열을 지속적으로 폴링해야합니까? 아니면 이벤트를 만들어야합니까? 이 문제에 대한 의견을 듣고 싶습니다. 미리 감사드립니다.

+0

.NET 4.0 Task Parallel Library에서 소개 된 동시 콜렉션 및 기타 컨테이너 클래스를 사용해야합니다. – dthorpe

+0

추가 코드가 필요합니다. –

답변

2

이러한 상황에서 나는 다른 스레드에서 스트림 리더를 끝내기 만합니다. 독자를 닫으면 루프를 깨뜨릴 것입니다. 여분의 동기화 된 대기열을 통과하는 것은 불필요한 작업을하는 보편적 인 것입니다. 결국 어딘가에 차단하는 리더를 멈춰야합니다. 대기열에 값을 넣으면 소비자가 차단되지 않지만 독자는 차단됩니다.

다음은 소켓 차단 읽기와 관련된 예입니다. 나는 의도적으로 보내지 않을 1 바이트를 읽는 것을 막을 것입니다. 몇 초 후에 소켓을 처리 할 것이고, 소켓은 블로킹 읽기에서 빠져 나올 것이며 앱은 정상적으로 종료 될 것입니다. 나는 각 스레드가 무엇을하고 있는지 그리고 언제 일어나는지를 기록 할 것이다. (각 로그 라인의 접두 번호는 쓰레드 ID이고 별도의 쓰레드 들여 쓰기 할 것이다).

블로킹 리더를 사용하는 클래스에서 일회용 패턴을 구현하고 거기에서 독자를 닫거나 처분해야합니다. 당신이 이것을 실행하면

static void Main(string[] args) 
{ 
    SocketTest(); 

    Console.WriteLine("Press any key to exit"); 
    Console.ReadKey(); 
} 

public static void SocketTest() 
{ 
    int port = 22345; 

    var tcpListener = new TcpListener(IPAddress.Any, port); 

    tcpListener.Start(); 

    // Listening thread 
    new Thread(() => 
    { 

     Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Waiting for connection to port"); 

     var socket = tcpListener.AcceptSocket(); 

     Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Connection accepted"); 

     var stream = new NetworkStream(socket); 
     var reader = new BinaryReader(stream); 

     try 
     { 
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Starting blocking read"); 
      var bytes = reader.ReadBytes(1); 
      Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Done blocking read, read {0} bytes", bytes.Length); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error reading " + ex); 
     } 
    }).Start(); 

    // connecting thread 
    new Thread(() => 
    { 
     var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

     Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Connecting to local port"); 

     socket.Connect("127.0.0.1", port); 

     Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Connecting to local succeeded"); 

     Thread.Sleep(TimeSpan.FromSeconds(2)); 

     Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Disposing of socket"); 

     socket.Dispose(); 

    }).Start(); 

    Thread.Sleep(TimeSpan.FromSeconds(5)); 
} 

당신이 얻을 :

내 생각이 다른 스레드

에 removeServerResponseStream.Read을했다 : 비록

3 - Waiting for connection to port 
     4 - Connecting to local port 
     4 - Connecting to local succeeded 
3 - Connection accepted 
3 - Starting blocking read 
     4 - Disposing of socket 
3 - Done blocking read, read 0 bytes 
Press any key to exit 

당신은 가지 여기에 질문을 직접 대답

정확히 무엇을해야합니다. 귀하의 응용 프로그램이 활성 스레드에서 소켓을 닫았 음을 알고있을 때. 이 무료 스레드는 차단 된 스레드이므로 정상적으로 종료 할 수 있습니다.

여기 패턴은 일반적으로 특정 소켓에 대한 스레드를 회전시키고 사용자가 요청에 대한 "컨트롤러"스레드의 종류 인 하나의 활성 스레드를 유지 관리하는 것입니다.

+0

내 문제는 소켓을 처분해야 할 때를 알지 못합니다.비디오 클립이 끝나면 비디오 서버는 데이터 보내기를 중지하지만 비디오 서버가 클립의 다른 위치를 찾는 명령을받을 수 있으므로 연결을 유지합니다. 예를 들어 처음에 클립을 찾는 명령을 받으면 같은 연결을 통해 데이터를 다시 보내기 시작합니다. 따라서 연결을 통해 전송되지 않는 데이터가 반드시 우리가 데이터를 닫아야한다는 것을 의미하지는 않습니다. Java 애플릿이'output'을 닫을 때 연결을 닫아야합니다. – mittmemo

+1

소켓 처분시기 : 애플릿이 닫힐 때를 알 수 있습니다. 애플릿의 연결이 끊어 졌는지 알 수있는 결정적인 방법이 없다면 소켓에서 시간 초과를 할당 할 수 있습니다. 알려진 시간에 데이터를 얻지 못하면 소켓은 자동으로 닫히지 만 그 값이 올바르지 않으면 재 연결해야한다는 자비로 가득 차게됩니다. 애플릿에 ping을 사용할 수 있습니까? 특정 시간 내에 ping이 수신되지 않으면 컨트롤러 스레드가 해당 열린 소켓을 닫을 수 있습니다. – devshorts

+1

@ kdg123, 귀하의 의견에 대한 응답을 – devshorts