2013-05-16 4 views
1

메신저 나 자신을 위해 이것을 알아내는 것이 어려워서 여기에서 묻습니다. 원격 컴퓨터에 클라이언트를 설치하는 응용 프로그램이 있고 클라이언트가 소켓을 통해 다시보고합니다.홀드 타이머 유형 스레드

홀드 타이머를 만들려고합니다. 즉, 클라이언트가 1 분마다 서버에 hello 메시지를 보냅니다. 서버가 보류 타이머 내의 클라이언트로부터 hello 패킷을받지 못하면 클라이언트가 제거됩니다. 나는 하나의 클라이언트로 그것을 잘 할 수있다. ... 그러나 여러개를 위해 나는 그것을 이해할 수 없다. 서버에 연결된 새로운 클라이언트를 foreach 스레드로 생성하려고합니다. 그리고 그 스레드 내부에서 기다려 타이머를 시작 .. 어떻게 스레드와 클라이언트를 구별합니까 어떻게 안녕하세요 패킷이 클라이언트에서받은 경우 보류 타이머를 재설정합니까.

모든 hello 패킷에 대해 새 스레드를 만들고 이전 패킷을 중지 할 것을 생각했습니다. 하지만 내가 어떻게 cpu 집중이 될지 모르겠다

만약 당신이 내 질문을 이해하지 못한다면, 그렇게 말하면 좋지 않다는 것을 설명하려고 애 쓰지 말아라.

어떤 솔루션이 더 좋습니까? 해결책 A) 새 스레드를 시작하고 hello 패킷이 도착할 때마다 이전 스레드를 중지 하시겠습니까? (매 40 초마다) - 100 클라이언트에서

솔루션 B) 훨씬 더 확장 가능한 솔루션을 여기에 삽입하십시오.

솔루션 C) 동적으로 만드는 스레드 내에서 타이머에 액세스 할 수 있습니까? - 동적으로 생성 된 모든 스레드에 고유 한 스레드 이름이 있습니다.

그리고 스레드와 동일한 이름의 클라이언트로부터 hello 패킷을받을 때 리셋합니까?

해결책 D) 타이머를 사전에 저장하십시오. 공개 사전 timersDict = new Dictionary(); 및 루프 안녕하세요 패킷 수신 대기 타이머에 대한 참조에있는 참조를 찾으십시오.? 원격 호스트 응용 프로그램까지

foreach(var item in timersDict) 
          { 
           if(item.Key == stripip(client.RemoteEndPoint.ToString())) 
            TimerReset(item.Value); 
          } 

솔루션

설정, 서버에 매 3 초에 "hello"라는 메시지를 보낼 수 있습니다. 경과 이벤트가 1 초마다 발생하는 서버에 타이머를 만듭니다. 설정은 원격 컴퓨터가 죽은 것으로 간주하기까지 얼마나 걸릴지를 결정하는 int를 보유하는 정적 값을 9 초로 선택했습니다.

서버가 hello 메시지를 수신하면 클라이언트의 IP 주소와 함께 목록에 타임 스탬프를 저장합니다. timer_elapsed 이벤트

void holdtimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
    { 

     try 
     { 
      if (server.Clients.GetAllItems().Count != 0) 
      { 
       foreach (IScsServerClient _client in server.Clients.GetAllItems()) 
       { 
        string ipaddr = stripip(_client.RemoteEndPoint.ToString()); 
        bool containsentry = _clientlist.Any(item => item.ipadress == ipaddr); 
        if (containsentry) 
        { 
         foreach (Scsserverclient entry in _clientlist.Where(entry => entry.ipadress == ipaddr)) 
         { 
          if ((DateTime.Now - TimeSpan.FromSeconds(Settings.Default.Hold)) > entry.lasthello && 
           entry.isConnect != "Disconnected") 
          { 
           entry.client.Disconnect();        
          } 
         } 
        }            
       } 

      } 
     } 
     catch (Exception ex) 
     { 

     } 
    } 

은 기본적으로 내가 연결된 클라이언트 목록을 실행하고 마지막으로받은 헬로 메시지가 구초보다 오래된 경우에, 나는 분리에 클라이언트로 고려한다.

+0

이미 가지고있는 코드를 게시 할 수 있습니까? 나는 당신이 해결책을 설계하는 것을 막을 구체적인 문제를 발견 할 수 없다. 아마도 당신이 잘못하고있는 것을보고 그것을 지적하는 것이 더 쉬울 수도 있습니다. – usr

+0

잘 그게, 내가 해결책을 주위에 내 머리를 감쌀 수 없다. 나는 작동하지 않는 코드를 가지고 있지 않다. 이 최선의 방법을 알아낼 수 없다. – VisualBean

+0

서버를 ASP.NET 응용 프로그램으로 만들고 클라이언트가 HTTP를 통해 서버를 호출하도록 할 수 있습니까? 그렇게하면 인프라가 연결을 관리하고 이러한 모든 우려가 사라집니다. – usr

답변

7

Reactive Extension에 완벽한 응용 프로그램입니다. 다음은 시도 할 수있는 빠른 consle-app 예제입니다 (Nuget 패키지 Rx-Main 추가). ID에 정수를 입력하도록 요청하여 클라이언트를 시뮬레이트합니다. 특정 ID를 본 후 timeToHold가 경과 한 후 Subscribe의 작업이 실행됩니다.

동기화의 사용은 중요합니다. RX 버전 2에서는 제목 OnNext가 동기화 호출없이 스레드 세이프가 아닙니다.

RX 덕분에이 솔루션은 타이머와 스레드를 사용하는 방법에서 매우 효율적입니다.

참고 : http://www.zerobugbuild.com/?p=230

public void Main() 
{ 
    var clientHeartbeats = new Subject<int>(); 

    var timeToHold = TimeSpan.FromSeconds(5); 

    var expiredClients = clientHeartbeats 
     .Synchronize() 
     .GroupBy(key => key) 
     .SelectMany(grp => grp.Throttle(timeToHold)); 

    var subscription = expiredClients.Subscribe(
     // in here put your disconnect action 
     i => Console.WriteLine("Disconnect Client: " + i)); 

    while(true) 
    { 
     var num = Console.ReadLine(); 
     if (num == "q") 
     { 
      break; 
     } 
    // call OnNext with the client id each time they send hello 
     clientHeartbeats.OnNext(int.Parse(num)); 
    } 

    // if you need to tear down you can do this 
    subscription.Dispose(); 
} 

부록 : 서버가 클라이언트를 시작 책임이있는 경우 오, 당신이 OnNext를 보낼 수 있습니다 내 여기 블로그에 이것에 대한 더 자세한 설명을 추가했습니다 클라이언트가 이 아닌 경우이 하트 비트를 전송할 때 클라이언트를 시작할 때 서버에서 시작합니다.

+0

와우 ... 매우 멋졌습니다. – VisualBean

+2

어쩌면 당신의 유권자 투표에 충분히 좋을까요? ;) –

+0

이 기술에 대한 자세한 내용은 내 블로그를 업데이트하십시오. http://www.zerobugbuild.com/?p=230 –

2

내가 어떻게 할 것인지, 당신의 문제를 이해 한 경우 :

  • 가 클라이언트의 ID를 저장, 고유 모음 만들기를, 관련 소켓 객체 (또는 연결을 드롭 할 필요가 무엇이든), 마지막으로 수신 한 'hello'의 날짜
  • 컬렉션 (컬렉션)을 몇 백 밀리 초마다 반복하여 날짜를 확인하고 쓸모없는 연결을 삭제합니다.
  • 클라이언트가 'hello' , 콜렉션에서 적절한 항목을 찾고 연관된 날짜를 갱신하십시오.

컬렉션에 ConcurrentDictionary을 사용하면 스레드 안전 코드를 쉽게 쓸 수 있습니다.

+0

"ConcurrentDictionary를 사용하면 스레드 안전 코드를 쉽게 작성할 수 있습니다. "- 분명히 당신은 매력적인 인생을 이끌었습니다.) 내가 본 공포를 본다면 ... –

+1

@JamesWorld : D Lemme rephrase : 쉽게 스레드 안전 코드를 작성하려면, * 당신이하고있는 일을 안다는 것을 받아 들였습니다. * –