게임강의
각종 노하우 분류

클라이언트와 서버의상관관계(서버팅김현상에대하여..)

컨텐츠 정보

본문

기타] 클라이언트와 서버의상관관계(서버팅김현상에대하여..) 
 
글쓴이 :  UIFree_love 날짜 : 10-03-06 20:02 조회 : 425 
1. 서버와 클라이언트의 상관관계가 왜 중요한가?

  : 지난시간까지 여러가지를 배웠지만 다시한번 복습한다는 의미로 보게 된다면
    네크워크라는 개념도 간단하게 들어가보면 서버와 클라이언트사이의 교환(커뮤니케이션)인 것입니다.
    일반적인 게임에서는 서버가 수정되면 클라이언트도 같이 따라서 수정되고 서버와 굉장히 유기적으로
    같이 물려서 움직이는데 반해, 우리가 취급하는 서버팩은 엄밀히 이야기하면 서버가 아닌것이 중요합니다.
    클라이언트정보를 바탕으로 서버처럼 보이게 클라이언트를 속여주는것에 지나지 않기 때문이죠.
    그렇기때문에 서버팩이 중요한 것이 아니라 오히려 클라이언트에 맞추어서 서버팩을 수정해야 되는 이상한
    현상이 발생하게 되고 접속기 및 클라이언트 패치등이 상당한 비중을 차지하게 되어 버립니다.

2. 그렇다면 팅기는 이유는 클라이언트의 버전이 안맞아서 팅기는 것인가?

  : 정답은 반드시 그런것은 아니다 라는 것입니다.
    물론 클라이언트에 따라서 팅기고 안팅기고 할 수도 있지만, 기본적으로 클라이언트 정보가 맞지 않으면
    아예 접속조차 되지 않습니다. 실제로 Gameserver.java 화일과 ClientThread.java 화일에 그런 정보가
    세팅되어 있는데, 기본적으로 퍼스트패킷등이 일치하게 되면 일단 접속되서 팅기는 일들은 발생하지 않습니다.
    다만 클라이언트가 패치가 다 되어 있지 않을때는 gfxid등 서버팩에 세팅되어 있는 id들과 일치하지 않을때,
    그때는 팅김현상이 발생할 수는 있습니다. 하지만 클라이언트 패치를 받았다고 할지라도 팅김현상이
    생길 때가 있습니다. 이는 접속기문제 때문입니다. 접속기라는 것이 특정한 프로그램이 아니고
    린빈화일(lin.bin 혹은 lineage.bin등)을 바이너리에서 익스큐터블(exe)로 변환한 것이기 때문에,
    린빈화일은 패치 안하고 단순하게 다른 것들만 패치했다고 해서 안팅기는 것은 아니라는 이야기 입니다.
    요즘에 구현된 하피터번과 블랙위자드 터번등을 구현하기 위해서는 클라이언트 패치에 최신접속기를 써야만
    안팅기고 제대로 구현될 수 있다는 이야기와도 일맥상통합니다.

3. 본섭 최신접속기를 쓰고 본섭 풀패치를 받았더니 아예 헤소팩등 EPU서버에 접속이 불가능했다. 그 이유는?

  : 네. 그것은 당연한 현상입니다. EPU서버라고 해서 본섭과 일대일 대응이라는 것은 조금 어불성설이구요,
    헤소팩,윈드팩,넬755,테루수정,사계절,지못미 등으로 통칭되는 EPU서버팩은 몇개월전의 본섭을 기준으로
    퍼스트 패킷을 작성해주고 옵코드를 세팅하여 준 것이기 때문에 접속기또한 그때당시의 본섭 린빈화일을
    기준으로 작성이 되었으며, 현재의 변경,패치 완료된 본섭접속기(린빈화일)로는 접속이 아예 되지 않습니다.
    최근 몇몇 서버에서만 특정해서 자신들만의 본섭린빈화일을 개조한 최신접속기를 써서 구현하고는 있습니다만,
    특정한 방법이 있는 것은 아니고, 접속기에서 보내주는 퍼스트패킷과 서버팩에서 읽어드리는 패킷이 일치하는가를
    따진 다음에 커넥트에 관련된 옵코드와 로그인옵코드등 몇가지 암호만 같게 세팅하여주면 본섭접속기로 가능합니다.
    단, 그 정보등은 스스로 알아내셔야겠죠? 중요한 것은 클라이언트와 접속기가 중요하다는 것입니다.
    Gameserver.java 화일과 ClientThread.java 화일을 잘 비교해서 분석해 보시면 많은 도움이 될 것입니다.
    이에 관련해서는 추후 심도깊게 다룰 예정이니 여기서는 이정도로 해서 넘어가도록 하겠습니다.

4. 그렇다면 접속기도 제대로 했고, 패치도 제대로 했고, 모든조건을 다 갖추었는데도 팅기는 이유는?

  : 그것은 두가지 이유로 볼 수 있습니다. 실제로 수많은 분들이 EPU서버 (예: 헤소펙)등에서 편지지를 구현할려고
    아무리 구현해도 계속 팅기거나 멈추어버리는 경우를 많이 봤습니다. 또한 주머니를 클릭했을때도 팅기고
    무슨 주문서를 클릭해도 팅기고, 봉인된 오만부적을 풀면서도 팅기고 등등 자주 팅기는 현상들이 있죠.
    첫번째의 이유는 다들 아시다시피 OP CODE 라는 숫자때문에 팅길 수 있습니다.
    이제 조금 원리를 들여다 볼까요?
    [2008-10-25 오전 10:24:30:718]  {클라->서버}  OPCODE = 7A  길이 : 52
=======================================================================================
[ADDRESS*]          00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15  0123456789ABCEDF
.......................................................................................
[  DATA  ] (0x0000)  7A 57 6F 64 65 6E 73 31 32 31 32 00              zWodens
[  DATA  ] (0x0010)                00 D3 E6 C3 F0 00 00 00 00 8C E9  ...........
[  DATA  ] (0x0020)  BA E8 A6 36 27 67 AF AF 3D BC 76 88 2A 58 1F 00  ...6'g..=.v.*X..
[  DATA  ] (0x0030)  00 00 45 00                                      ..E.
=======================================================================================

[2008-10-25 오전 10:24:30:765]  {서버->클라}  OPCODE = 09  길이 : 16
=======================================================================================
[ADDRESS*]          00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15  0123456789ABCEDF
.......................................................................................
[  DATA  ] (0x0000)  09 19 00 00 00 00 00 19 00 00 00 00 00 00 E0 00  ................
=======================================================================================
    위는 모서버에 공개되어진 자료였습니다만 프라이버시를 생각해서 제가 일부분을 손 본 것입니다.
    본섭에서 암호화 되어져서 보내지는 패킷을 잡아서 일부분을 그 암호를 풀어서(복호화)
    화일로 보내 본 셈플입니다. 저기 위에 보시면 클라에서 서버로 갈때도 퍼스트패킷다음에 옵코드가 오고
    그 옵코드 다음에 데이타가 옵니다. 마찬가지로 서버에서 클라로 정보를 보낼때도 그런형식입니다.
    위 내용은 로그인 시에 아이디와 패스워드를 입력받는 부분인데, 프라이버시를 생각해서
    아이디 부분과 패스워드부분은 삭제시켰습니다. 하지만 본섭의 OP CODE는 7A가 아이디와 패스워드를
    입력하는 데 반하여 헤소팩에서는 7A라는 옵코드는 교환창 취소라는 행동으로 정의되어져 있습니다.
    이렇듯 각 클라이언트 버전마다 옵코드가 틀리고, 접속기에 따라서도 틀릴 수 있습니다.
    옵코드가 일치하지 않게되거나 엉뚱한 옵코드를 써주게 되면 팅기는 현상이 일어날 수 있는것이죠..

    두번째는 소스자체가 배배꼬아서 작성되었을때에도 팅김현상이 일어날 수 있습니다.
    지난 시간에는 헤소팩 등에서 주머니클릭팅김을 방지하는 소스를 공개해 드렸는데,
    다들 효과 보셨나요? 그 소스는 제가 직접 작성하고 테스트한 뒤에 몇개를 더 첨가해서 강의용으로
    별도로 제작한 소스입니다. 하지만 그 소스만으로도 주머니클릭 팅김은 방지할 수 있었을 것입니다.
    원래는 편지지 소스를 이번강의때 올릴려고 했는데 그거 올리지 말라는 간절한 부탁(?)들로 인하여
    간단한 다른 소스를 예를 들어서 설명하여 드리겠습니다.
    헤소6차팩 기준으로 봉인된 오만주문서를 클릭하면 한번 팅기면서 봉인이 풀리는 것을 볼수 있습니다.
    과연 이것은 옵코드 때문일까요?
    소스를 보자면,
      } else if (itemId >= 40280 && itemId <= 40288) {  // 봉인된 오만의 탑 11~91층 부적
  pc.getInventory().removeItem(l1iteminstance, 1);
  L1ItemInstance item = pc.getInventory().storeItem(itemId + 9, 1);
  if (item != null) {
  pc.sendPackets(new S_ServerMessage(403, item.getLogName()));
    }
    대충 이런식으로 쓰여져 있을것입니다. 에러는 안보이시죠? 네, 저도 특별한 에러는 보이지 않습니다.
    하지만 마지막의 if문을 보시면 item 이라는 변수가 갑자기 등장하죠? 이 변수때문에 팅길수도 있습니다.
    특별히 저 if문이 꼭 필요한 것이 아니기 때문에 완전히 삭제를 해줘보죠.. 그러면 아주 간단한 소스가 됩니다.
      } else if (itemId >= 40280 && itemId <= 40288) {  // 봉인된 오만의 탑 11~91층 부적
                pc.getInventory().storeItem(itemId + 9, 1);
        pc.getInventory().removeItem(l1iteminstance, 1);
    네, 이걸로 끝입니다. 바뀐것은 밑의 if문 전체를 삭제시켜줬고, 아이템을 지우는 것을 밑으로 내리고
    새로운 아이템(봉인풀린 부적)을 주는 것을 위로 올려서 순서를 바꿔주었습니다.
    이거 하나만으로도 절대 봉인을 풀어서 팅기지 않을것입니다. 참고로 테스트 해보고 올리는 것입니다.
    이렇듯 소스수준에서 각종 매소드 및 변수선언등에 의한 이유로 팅기는 경우도 있다는 것이 두번째 이유인 것입니다.
    컴파일했을때 에러가 안나고 제대로 컴파일이 되었다고해서 중요한 것이 아니라 최적화가 더 중요한 이유도 아시겠죠?


5. 그렇다면 옵코드 없이도 소스수준에서 모든 것이 다 구현가능한 것이 아닌가?

  : 그것은 결코 아닙니다. 어느정도까지는 옵코드 없이도 소스로 구현 가능한 것들도 있겠지만, 애시당초 서버프로그램이 아닌
    서버를 있는 것처럼 만들어주는 서버에뮬레이터의 한계때문에 옵코드는 매우 중요합니다. 일팩에서 EPU로 전환하거나
    혹은 EPU에서 일팩으로 전환할때도 어떨때는 옵코드만 수정해주면 그 서버팩에 안착되는 결과가 있기 때문에 이 옵코드는
    상당히 중요하다고 볼 수 있겠습니다. 다만 어느정도까지는 소스수준에서 팅김부분을 방지할 수 있다는 것입니다.


6. 패킷에 관해서 조금 더 자세하게 말해달라.

  : 네 조금 더 깊이 들어가 본다면, 패킷에 대한 이해를 하셔야 합니다. 패킷부분을 먼저 설명을 하고 소스수준에서 보겠습니다.
    클라이언트가 맨 먼저 서버에 접속요청을 하게되면, 서버는 클라이언트에 키패킷이라는 것을 먼저 보내게 됩니다.
    클라이언트의 버전등을 체크하기 위한 것인데요, 이 키패킷이 일치하지 않으면 접속허가를 안내줍니다. 그러면 우리가 보는 입장에서는
    어떻게 로그인화면까지는 갔는데 아이디랑 패스워드 넣고 로그인 아무리 눌러도 접속이 안되는 현상이 발생하겠죠?
    서버창의 CMD화면에서 본다면, xxx.xxx.xxx.xxx IP가 접속했습니다. 까지는 나오는데 그다음부터는 아무런 메시지가 없게 됩니다.
    흔히 그런현상들을 많이 겪으셨으리라 생각됩니다. 이것이 바로 키패킷이 안맞아서 나오는 문제입니다. 키패킷은 접속기와 서버가
    서로 주고받는 패킷중 가장 처음에 버전정보등을 주고받는 패킷이라고 보면되는데, 구체적인 예를 들어보겠습니다.
    12 00 1F 07 7C D2 14 69 4B A3 03 ..... 이런 식의 패킷이 맨먼저 체크된다고 보면 이것이 키패킷이 되겠는데, (키패킷은 암호화 없습니다)
    저 위의 키패킷에서 보게되면 12라는 것은 패킷의 길이가 되겠습니다. 16진수기때문에 실제길이는 18이 되겠네요? 두번째 00은 그냥 공란이고
    세번째 나오는 1F 라는 것이 옵코드입니다. 옵코드 다음에 나오는 07 7C D2 14 이 네자리수가 바로 시드값이 되겠네요. 그리고 이 시드값 뒤에
    나오는 69 4B .... 이 값들이 바로 퍼스트 패킷이 되겠습니다. 이러한 패킷들을 자신의 텍스트화일에 저장시켜 주는 프로그램등은 시중에
    공개버전으로 많이 돌고 있습니다. 패킷스나이퍼등이 그런 프로그램이 되겠네요. 그리고 암호화 된 패킷을 암호를 풀어서 복호화시키는것은
    스스로 해야 될 것들입니다.


7. 그렇다면 이러한 패킷들또한 우리가 돌리는 서버팩에 정의되어 있는가? 한번도 본적도 들은적도 없다.

  : 네. 정의되어져 있습니다. 이것이 정의되어 있지 않다면 접속조차 불가능 하겠죠?
    지금껏 예를 들어왔던 헤소6차팩을 기준으로 또 설명을 한다면, ClientThread.java 화일에 보면 아래와 같은 소스가 있습니다.
 private static final byte[] FIRST_PACKET = {
                (byte) 0x12, (byte) 0x00,
  (byte) 0x21, (byte) 0x94, (byte) 0xF1,
  (byte) 0x5C,
  (byte) 0x13,
  (byte) 0xA8, (byte) 0x3A, (byte) 0xEA, (byte) 0x38, (byte) 0x00,
  (byte) 0x73, (byte) 0x7F, (byte) 0x9E, (byte) 0x59, (byte) 0x33,
  (byte) 0xC9 };
      바로 퍼스트 패킷에 관한 정의 인데요, 헤소6차팩에서는 시드값따로 옵코드 따로 퍼스트 패킷을 따로 놓는게 아니라 그 전체를 엮어서
      한개의 퍼스트패킷으로 묶은다음에 클라이언트 접속기를 대조하는 방식을 따르고 있음을 알 수 있습니다.
      맨 처음 보이는 0x12 라는 것은 퍼스트패킷의 길이를 말하는 것으로서, 패킷들의 숫자를 더해보면 18개라는 것을 알 수 있습니다.
      16진수 0x12를 십진수로 바꾸면 18인것은 다들 알고 계시겠죠? 두번째의 0x00는 공백이고 세번째 0x21은 옵코드 입니다.
      또한 0x94,0xF1,0x5C,0x13 이 네개의 값이 시드값으로서 예전에는 long seed = { 이런식으로 시드값을 따로 정의하기도 했습니다만,
      헤소팩에서는 이 모든 18개의 바이트를 묶어서 퍼스트 패킷으로 처리해서, 일치하면 접속을 허가하고 불일치하면 접속을 허가하지 않습니다.
      따라서 아무리 본섭 접속기라고 할지라도, 해당 서버팩에서 정의되어진 퍼스트패킷(ClientThread.java)과 일치하지 않으면 접속불가인 것입니다.

8. 패킷만 나왔을 뿐이지 버전에 관련된 것은 안나왔는데 그렇다면 패킷만 수정해준다면 다 되는것인가?

  : 아닙니다. 당연히 버전정보등에도 OP CODE가 따로 있고 서버버전과 클라이언트 버전이 잘 맞아야만 안팅기고 게임을 즐기실 수 있습니다.
    또한 Opcodes.java 화일에 보시면 서버버전에 대한 옵코드도 정의되어 있습니다. 헤소 6차 팩 기준으로 설명한다면,
    public static final int S_OPCODE_SERVERVERSION = 0x74;
    위와 같이 설정되어 있으며, 패킷스나이퍼를 통해서 패킷을 긁어서 살펴보면 74 라는 서버버전 옵코드가 정확하게 동작한다는 것을 알수 있습니다.
    또한 이 서버버전에 대한 자세한 옵코드 및 각종 패킷에 대한 소스는 S_ServerVersion.java 화일에 정의가 되어 있습니다.
    그리고 서버버전에 대한 정보뿐 아니라 엔피시 버전 및 각종 버전등에 대한 정의가 S_ServerVersion.java 화일에 정의되어 있기 때문에
    반드시 매니아라면 꼭 연구하고 넘어가야 될 화일이 되겠습니다. 간단하게 말씀드리자면, Gameserver.java 와 ClinentThread.java 그리고 S_ServerVersion.java
    또한 나중에 다시 공부하게 되겠지만 ClientBasePacket.java 화일과 ServerBasePacket.java 화일에 각종 패킷등에 관한 정의가 되어져 있고,
    암호화와 관련되서는 LineageBlowfish.java 및 LineageEncryption.java 화일에 정의되어져 있습니다.
    이부분에 대해서는 추후 본섭구현편에서 더욱 자세하게 다룰 예정이기 때문에 오늘은 이정도만 이야기 하고 넘어가도록 하겠습니다.

9. 바이트 및 패킷정보 옵코드정보 혹은 데이타정보는 어떤식으로 소스에서 구현되는가?

  : 네, 아주 중요합니다. 이 부분을 이야기 하기 위해서 지난 시간에 read 와 write 의 원리를 설명드렸던 것입니다.
    지난강좌때 read와 write의 이야기를 들으신 분들은 지루하시겠지만 처음 이 강좌를 보시는 분들을 위해서 다시 설명드리자면,
    -127에서 128까지의 값밖에 가질 수 없는 이유가 다 있으며, 라우풀이 32767까지밖에 갈 수가 없고 만피또한 32767이 최고일 수 밖에 없는 이유가
    바로 데이타 저장과 읽는 방식때문에 그렇다고 말씀 드렸습니다. 그리고는 두리뭉실하게 끝맺음을 했는데요,
    이번시간에는 조금만 더 살짝 구체적으로 들어가 보도록 하겠습니다.
    비트와 바이트 및 워드 레코드 필드 데이타베이스등의 체계론은 그냥 넘어가고 막바로 바이트에 관련되서 말씀드리겠습니다.

    * 옵코드는 1바이트로 이루어진다. 따라서 0xFF 이상의 값을 가질 수 없으며, 이는 writeC 로 송신되고 readC 로 수신되어진다.
      ( 1바이트 값을 읽고 쓰고 하는 매소드는 readC 와 writeC 임을 알 수 있다.)
    * 2바이트 값을 가지는 모든 종류의 데이타 값은 readH 와 writeH 매소드로 구현되어 진다.
      (소스수정할때 readH 등이 나올때는 2바이트 이상의 값을 읽을 수 없다.)
    * 4바이트 값을 가지는 모든 종류의 데이타 값은 readD 와 writeD 매소드로 구현되어 진다.
      (소스를 보면서 readD 혹은 writeD 매소드가 호출되어 질 때는 4바이트값이구나 생각하면 된다)
    * 스티링, 즉 문자배열등을 송,수신 할때는 readS 및 writeS 매소드로 구현되어 진다.
      (readS 혹은 writeS 매소드가 나오면 아, 이건 문자열이구나 이렇게 생각하면 됨)
    * 이외의 값은 정의되어 있지 않으나 경우에 따라서 소스를 수정하여 8바이트 값을 가지는 매소드를 만들어 줄 수는 있다.
      (일팩에서는 현재 위와같은 매소드만 지원함. 그러나 스스로 만들어서 쓸 여지는 있음)

    위와 같이 현재 일팩에서는 최대 4바이트값을 가질 수 있기 때문에 65535가 최대값이 되고 이를 0을 기준으로 나누면 -32768에서 32767까지의 값을
    가질 수 있게 되는 것입니다. 따라서 카오수치의 최대값(만카오)을 구할때는 readD 매소드의 원칙에 따라서 -32768 이 만카오값이 되겠습니다.

10. read 와 write 등은 수정할 수 없는 것인가?
 
  : 아닙니다. 예를 들어서 writeH 등으로 데이타가 쓰여지고 readH 등으로 데이타가 읽혀졌다면, 즉 2바이트 값을 가지는 데이타를 처리한다면
    이러한 처리를 4바이트 매소드인 writeD 매소드로 쓰고 readD 매소드로 읽게끔 만들어 준다면 255 값을 가지는 (예를 들자면 AC)의 경우에
    최대값 -128방을 넘는 방법도 가능할 수 있습니다. 다만 단지 그 두개만 수정해서 끝나는것이 아니라 화면출력부분을 비롯해서 각종 관련된
    모든 것들을 수정하면 가능하구요, 또한 새롭게 데이타 입출력방식도 바꿀 수 있습니다.


결론은,
게임에서 팅기는 이유는 무조건 클라이언트 때문만은 아니라는 것입니다.
옵코드가 안맞아서 일수도 있고, 클라이언트 패치때문일 수도 있고 버전충돌일수도 있고, 데이타 형식이 안맞아서 그럴수도 있습니다.
중요한 것은 이러한 원리들을 하나 하나씩 알아가다보면 아, 그렇구나 라는 것을 느낄 수가 있고,
또한 더 응용해서 보다 더 훌륭한 기법들을 익히실 수 있게 되는 것입니다.
소스 가져다가 붙여서 여러가지를 만들어 내는것도 중요하지만, 이번 기회를 통해서 일팩에 대한 전반적인 알고리즘을 정립하는 계기가
되었으면 합니다. 오늘 강좌도 최대한 쉽게 써볼려고 했지만, 아쉽게도 제가 전하려고 하는 바를 반도 쓰지 못한 것 같습니다
[출처] 자바존 - http://javazone.co.kr/bbs/board.php?bo_table=lin00001&wr_id=12

관련자료

댓글 0
등록된 댓글이 없습니다.
전체 7 / 1 페이지
번호
제목
이름

강의실

🏆 포인트 랭킹 TOP 10
순위 닉네임 포인트
1 no_profile 타키야겐지쪽지보내기 자기소개 아이디로 검색 전체게시물 82,042
2 no_profile 라프텔쪽지보내기 자기소개 아이디로 검색 전체게시물 51,280
3 no_profile 동가리쪽지보내기 자기소개 아이디로 검색 전체게시물 32,591
4 no_profile Revolution쪽지보내기 자기소개 아이디로 검색 전체게시물 28,199
5 서번트쪽지보내기 자기소개 아이디로 검색 전체게시물 23,416
6 no_profile 닥터스쪽지보내기 자기소개 아이디로 검색 전체게시물 22,310
7 no_profile 불멸의행복쪽지보내기 자기소개 아이디로 검색 전체게시물 13,822
8 no_profile 호롤롤로쪽지보내기 자기소개 아이디로 검색 전체게시물 13,500
9 no_profile 검은고양이쪽지보내기 자기소개 아이디로 검색 전체게시물 13,246
10 no_profile 하늘2쪽지보내기 자기소개 아이디로 검색 전체게시물 13,239
알림 0