2012-07-10 3 views
1

C#으로 서버를 만들고 있는데 배열에 로그인 한 모든 사용자를 처리하는 싱글 톤이 있습니다. 이 배열은 UserSession이라는 클래스의 배열입니다.클래스 내부의 여러 메소드를 처리하는 가장 좋은 방법은 무엇입니까?

이 UserSession 클래스에는 해당 사용자의 들어오는 패킷을 처리하는 별도의 스레드에서 실행되는 메서드가 있습니다.

class UserSession 
{ 
    public UserSession(TcpClient client) 
    { 
     var thread = new Thread(new ParameterizedThreadStart(HandleComm); 
     thread.Start((object)client); 
    } 

    public void HandleComm(object tcpClient) 
    { 
     TcpClient client = (TcpClient)tcpClient; 
     NetworkStream stream = client.GetStream(); 
     while(1 == 1) 
     { 
      byte[] buffer = new byte[4096]; 
      stream.Read(buffer, 0, buffer.Length); 
      int testShort = Convert.ToInt32(buffer); 
      switch((ActionEnum)testShort) 
      { 
       case ActionEnum.Something: 
        // here is my problem! 
        // do some more parsing of the message 
        this.DoSomething(SomethingStruct argument); // argument taken from the byte array 
        break; 
       case ActionEnum.AnotherSomething: 
        // the same as before 
        break; 
       // and this goes on and on 
      } 
     } 
    } 
} 

어떤 것, 그 클래스에 80 개 이상의 방법을 반복하지 않고 모든 별도의 열거를 처리하는 가장 좋은 방법 :

음,이 코드를 고려? (ActionEnum은 현재 사용자의 특정 동작이있는 enum입니다.)

나는 그 버퍼를 직렬화한다, 나는 그 코드를 당신이 생각하기에 빨리 만들었다.

+1

책임 http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern

명령 패턴 http://en.wikipedia.org/wiki/Command_pattern

체인'UserSession'은 싱글입니까? 공공 생성자가 있습니다 – Habib

+0

생산 코드에서 어떤 stream.Read()가 반환 되더라도 처리하길 바랍니다. 질문에 관해서는, 많은 방법을 가지고있는 문제는 무엇입니까? 몇 줄의 코드 만 수행하면 핸들링 자체가 케이스 자체에 남아있을 수 있습니다. 그렇지 않으면 아마도 방법이 있어야합니다. upd : 객체가 일률적으로 직렬화되는 경우 실제로는 수신 메시지의 형식에 대한 설명이 인수로 포함 된 하나의 메소드 일 수 있습니다. –

+0

@ Habib.OSU LoggedUsers라는 클래스 내에 UserSession 배열이 있습니다. 싱글 톤입니다. – Pacha

답변

1

가능한 많은 답변이 있습니다. 내 클라이언트/서버 게임을 만들 때이 문제가 발생했습니다. 나는 액션/이벤트/메시지를위한 수십 개의 열거 형 (Enums)으로 시작했다. 그런 다음 더 많은 수의 액션을 수행 할 때 매우 지속 가능하지 않다는 것을 깨달았다.

대략 내가 의사 코드로 수행 한 작업입니다.

NetworkClass 
    { 
     RegisterChannel(objUsingChannel,typeOfChannel, callbackForChannel, expectedReturnType){/*...*/}; 

     PushChannelMsg(channelID, dataToSend) 

     ReceiveMessageFromNetwork(msg){ Read data from network, which also contains channelID, and send it to any matching channelID} 
     //If there is no channel registered for a data that is received, just ignore it 
    } 

EnemyTurretObject 
{ 
    //Register the rotation channel and listen for rotation changes 
    NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=HandleTurretRotated, doubleReturnType) 

    HandleTurretRotated(double rot) 
    { rotate the turret } 
} 

FriendlyTurretObject 
{ 
    //Register two channels that we'll send data across 
    NetworkClass.RegisterChannel(this, channels.TurretFired + this.ID, callback=null, MissleFiredData) 
    NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=null, doubleDataType) 

    FireMissle() 
    { 
     NetworkClass.PushChannelMsg(channels.TurretFired + this.ID, new MissleFiredData(x,y)) 
    } 

    RotateTurret() 
    { 
     NetworkClass.PushChannelMsg(channels.TurretRotated + this.ID, newTurretRotationValue) 
    } 

} 

나는 기본적으로 열거 형의 전체 거대한 질량을 피해야하고, 각 개체가 자신의 데이터 채널 등을 담당하도록 더 일반화 설치했다. 그것은 훨씬 더 유연한 접근 방식이며, 하나의 enum을 변경해도 모든 것이 손상되지는 않습니다. 네트워크 클래스는 전송되는 데이터가 무엇인지 알 필요조차 없습니다. 이제는 단지 도관 일뿐입니다.

+0

사용자로부터 패킷을받을 때 어떻게합니까? "연결"또는 "연결 해제"와 같은 다른 종류의 작업이 있다고 가정 해 보겠습니다. 서로 다른 연결이 많이 있습니까? – Pacha

+0

연결 및 분리는보다 구체적으로 처리됩니다. 이러한 유형의 사례는 위에 설명 된이 메시지 전달 시스템 내부에있을 필요는 없습니다. 그리고 네, 수백 건의 연결을 지원할 수 있습니다. 메시지를받을 때 기본적으로 ChannelID를 읽은 다음 채널에서 예상되는 데이터의 종류를 확인한 다음 예상되는 반환 유형의 새 인스턴스로 페이로드를 읽습니다. –

+0

이 접근 방식으로 이동하려면 많은 양의 리팩토링/재 작성 그러나 코드를 변경했을 때 얻은 가치가 엄청나다고 말할 수 있습니다. 지금은 새로운 작업/유형/이벤트를 추가하는 것이 매우 쉽습니다. 전체 네트워크 코드가 최소 절반으로 줄었고 단순성으로 인해 디버그 및 관리가 훨씬 쉬워졌습니다. –

2

So : 기본적으로 그렇게하려고하면 숫자 코드가 메소드 호출로 변환됩니다.

이 변환이 자동으로 수행되는 것과 같이 얻을 수없는 것들이 있습니다. 대신 메소드의 이름을 전송했다면 리플렉션을 사용하여 메소드를 찾은 다음이를 호출 할 수 있습니다. 따라서 매핑을 설정하기 위해 몇 가지 수동 작업을 수행해야합니다.

다음에 결정해야 할 사항은 숫자를 메소드에 매핑하는 가장 좋은 방법입니다.

한 가지 방법은 현재 사용하고있는 방법 중 하나입니다. 여기에서 맵핑은 디스패치 사이클 (페치 번호, 변환, 호출) 내에서 발생합니다. 문제는 매핑 코드가 디스패치 코드를 가린다는 것입니다. 또한 상당한 양의 상용구 코드가 관련되어 있습니다.

당신은이 같은 명령 패턴의 조합 해시 맵 사용할 수

: 때문에 당신이 사용하고

  1. 이 명령에 대한 공통 인터페이스를 생성 (또는 폐쇄를 사용 : 설치하는 동안

    을 C#)

  2. 인터페이스 pr을 구현하는 개체의 인스턴스를 만듭니다. 메서드를 매핑하려는 경우
  3. 해시 맵에 추가하십시오.파견 루프에서

:

  1. 가져 오기 수
  2. 는 해시 테이블에서 번호를 찾아
  3. 발견하는 경우,보다 유연한 접근 방식의 경우 매핑 된 객체, 다른 실패

를 호출 번호를 처리해야하는 메시지로 간주하고 Chain of Responsibility 패턴을 사용하십시오. 당신의 클래스를 제공하는 방법

+0

이것이 내가하는 일입니다. enum 값을 메소드에 매핑하고이를 찾아서 호출하는 맵을 갖습니다. –

+0

그것은 또한이 상황을위한 나의 해결책입니다. 그것은 관심사를 좋게 분리하고, 커맨드 패턴의 사용을 촉진합니다. –

관련 문제