登陆网关(LoginGate) + 角色网关(SelGate)详解

传奇这款游戏,一直对我的影响很大。当年为了玩传奇,逃课,被老师叫过N次家长。言归正传,网上有很多源码,当然了,都是Delphi的。并且很多源码还不全,由于一直学习的C、C++。Delphi还真不懂。无奈硬着头皮上。好了。废话不多说。开始。

登录网关,负责游戏最开始的登录处理(与账户服务器LoginSvr通讯)。验证登录器输入的账户密码是否正确。

界面上的控件很多。其实干活的就 就 三个:“TServerSocket”、“TClientSocket”、“DecodeTimer”这三个控件。

ServerSocket:负责与登录器进行通讯,它做的操作:

1、接收连接,代码如下:

  1. {
  2. 函数功能:接受客户端连接,发送消息到 登录服务器
  3. }
  4. procedure TFrmMain.ServerSocketClientConnect(Sender: TObject;
  5. Socket: TCustomWinSocket);
  6. var
  7. UserSession:pTUserSession;
  8. sRemoteIPaddr,sLocalIPaddr:String;
  9. nSockIndex:Integer;
  10. IPaddr :pTSockaddr;
  11. begin
  12. Socket.nIndex:=-1;
  13. // 客户端IP地址
  14. sRemoteIPaddr:=Socket.RemoteAddress;
  15. if g_boDynamicIPDisMode then begin
  16. sLocalIPaddr:=ClientSocket.Socket.RemoteAddress;
  17. end else begin
  18. sLocalIPaddr:=Socket.LocalAddress;
  19. end;
  20. // 过滤ip
  21. if IsBlockIP(sRemoteIPaddr) then begin
  22. MainOutMessage(‘过滤连接: ‘ + sRemoteIPaddr,1);
  23. Socket.Close;
  24. exit;
  25. end;
  26. // 当前IP是否可以连接
  27. if IsConnLimited(sRemoteIPaddr) then begin
  28. case BlockMethod of
  29. // 断开
  30. mDisconnect: begin
  31. Socket.Close;
  32. end;
  33. // 动态过滤
  34. mBlock: begin
  35. New(IPaddr);
  36. IPaddr.nIPaddr:=inet_addr(PChar(sRemoteIPaddr));
  37. TempBlockIPList.Add(IPaddr);
  38. CloseConnect(sRemoteIPaddr);
  39. end;
  40. // 永久过滤
  41. mBlockList: begin
  42. New(IPaddr);
  43. IPaddr.nIPaddr:=inet_addr(PChar(sRemoteIPaddr));
  44. BlockIPList.Add(IPaddr);
  45. CloseConnect(sRemoteIPaddr);
  46. end;
  47. end;
  48. MainOutMessage(‘端口攻击: ‘ + sRemoteIPaddr,1);
  49. exit;
  50. end;
  51. // 如果网关准备好了
  52. if boGateReady then begin
  53. for nSockIndex:= 0 to GATEMAXSESSION 1 do begin
  54. UserSession:=@g_SessionArray[nSockIndex];
  55. if UserSession.Socket = nil then begin
  56. UserSession.Socket:=Socket;
  57. UserSession.sRemoteIPaddr:=sRemoteIPaddr;
  58. UserSession.nSendMsgLen:=0;
  59. UserSession.bo0C:=False;
  60. UserSession.dw10Tick:=GetTickCount();
  61. UserSession.dwConnctCheckTick:=GetTickCount();
  62. UserSession.boSendAvailable:=True;
  63. UserSession.boSendCheck:=False;
  64. UserSession.nCheckSendLength:=0;
  65. UserSession.n20:=0;
  66. UserSession.dwUserTimeOutTick:=GetTickCount();
  67. UserSession.SocketHandle:=Socket.SocketHandle;
  68. UserSession.sIP:=sRemoteIPaddr;
  69. UserSession.MsgList.Clear;
  70. Socket.nIndex:=nSockIndex;
  71. Inc(nSessionCount);
  72. break;
  73. end;
  74. end;
  75. // 和本地登录服务器进行通讯
  76. if Socket.nIndex >= 0 then begin
  77. ClientSocket.Socket.SendText(‘%O’ +
  78. IntToStr(Socket.SocketHandle) +
  79. ‘/’ +
  80. sRemoteIPaddr +
  81. ‘/’ +
  82. sLocalIPaddr +
  83. ‘$’);
  84. MainOutMessage(‘Connect: ‘ + sRemoteIPaddr,5);
  85. end else begin
  86. Socket.Close;
  87. MainOutMessage(‘Kick Off: ‘ + sRemoteIPaddr,1);
  88. end;
  89. end else begin //0x004529EF
  90. Socket.Close;
  91. MainOutMessage(‘Kick Off: ‘ + sRemoteIPaddr,1);
  92. end;
  93. end;

说白了,别看 那些IP过滤规则和连接限制什么的。就是 来了一用户,直接保存到一个 UserSession中。然后通知LoginSvr,有人连接了。。现在市面上的什么:GOM、HeroM2、HGEM2(原3K、IGE)、Legend、GEEM2、77M2…都是换汤不换药。变的就是 加密方式。这里不得不说,JSocket就是TServerSocket、TClientSocket的控件,懒得看源码,看了下其属性,大致就可以了解到。

是采用的线程池的Select模型。早期的游戏,都采用这种方式,不过,对于私*服,连接量不大,完全足够应付。其实有更好的解决办法,那就是IOCP来管理。效率更高。Windows下最适合的模型了。

2、断开连接,代码如下:

  1. procedure TFrmMain.ServerSocketClientDisconnect(Sender: TObject;
  2. Socket: TCustomWinSocket);
  3. var
  4. I:Integer;
  5. UserSession:pTUserSession;
  6. nSockIndex:Integer;
  7. sRemoteIPaddr:String;
  8. IPaddr :pTSockaddr;
  9. nIPaddr :Integer;
  10. begin
  11. sRemoteIPaddr:=Socket.RemoteAddress;
  12. nIPaddr:=inet_addr(PChar(sRemoteIPaddr));
  13. nSockIndex:=Socket.nIndex;
  14. for I := 0 to CurrIPaddrList.Count 1 do begin
  15. IPaddr:=CurrIPaddrList.Items[I];
  16. if IPaddr.nIPaddr = nIPaddr then begin
  17. Dec(IPaddr.nCount);
  18. if IPaddr.nCount <= 0 then begin
  19. Dispose(IPaddr);
  20. CurrIPaddrList.Delete(I);
  21. end;
  22. Break;
  23. end;
  24. end;
  25. if (nSockIndex >= 0) and (nSockIndex < GATEMAXSESSION) then begin
  26. UserSession:=@g_SessionArray[nSockIndex];
  27. UserSession.Socket:=nil;
  28. UserSession.sRemoteIPaddr:=;
  29. UserSession.SocketHandle:=-1;
  30. UserSession.MsgList.Clear;
  31. Dec(nSessionCount);
  32. if boGateReady then begin
  33. ClientSocket.Socket.SendText(‘%X’ +
  34. IntToStr(Socket.SocketHandle) +
  35. ‘$’);
  36. MainOutMessage(‘DisConnect: ‘ + sRemoteIPaddr,5);
  37. end;
  38. end;
  39. end;

删除,释放,并通知 LoginSvr,有人断开连接了…

3、接收数据,代码如下:

  1. procedure TFrmMain.ServerSocketClientRead(Sender: TObject;
  2. Socket: TCustomWinSocket);
  3. var
  4. UserSession:pTUserSession;
  5. nSockIndex:Integer;
  6. sReviceMsg,s10,s1C:String;
  7. nPos:Integer;
  8. nMsgLen:Integer;
  9. begin
  10. nSockIndex:=Socket.nIndex;
  11. if (nSockIndex >= 0) and (nSockIndex < GATEMAXSESSION) then begin
  12. UserSession:=@g_SessionArray[nSockIndex];
  13. sReviceMsg:=Socket.ReceiveText;
  14. if (sReviceMsg <> ) and (boServerReady) then begin
  15. nPos:=Pos(‘*’,sReviceMsg);
  16. if nPos > 0 then begin
  17. UserSession.boSendAvailable:=True;
  18. UserSession.boSendCheck:=False;
  19. UserSession.nCheckSendLength:=0;
  20. s10:=Copy(sReviceMsg,1,nPos 1);
  21. s1C:=Copy(sReviceMsg,nPos + 1,Length(sReviceMsg) nPos);
  22. sReviceMsg:=s10 + s1C;
  23. end;
  24. nMsgLen:=length(sReviceMsg);
  25. if (sReviceMsg <> ) and (boGateReady) and (not boKeepAliveTimcOut)then begin
  26. UserSession.dwConnctCheckTick:=GetTickCount();
  27. if (GetTickCount UserSession.dwUserTimeOutTick) < 1000 then begin
  28. Inc(UserSession.n20,nMsgLen);
  29. end else UserSession.n20:= nMsgLen;
  30. ClientSocket.Socket.SendText(‘%A’ +
  31. IntToStr(Socket.SocketHandle) +
  32. ‘/’ +
  33. sReviceMsg +
  34. ‘$’);
  35. end;
  36. end;
  37. end;
  38. end;

拿到数据,转发到登录账户服务器…它主要干的事完了…

ClientSocket:负责与LoginSvr进行通讯,它做的主要工作就是,

  1. procedure TFrmMain.ClientSocketRead(Sender: TObject;
  2. Socket: TCustomWinSocket);
  3. var
  4. sRecvMsg:String;
  5. begin
  6. sRecvMsg:=Socket.ReceiveText;
  7. ClientSockeMsgList.Add(sRecvMsg);
  8. end;

你没有看错,就是接收到数据。然后保存到链表,然后等待定时器来解析操作…

DecodeTimer 定时器的工作。源码中设置的 时间精度是 1毫秒:

  1. procedure TFrmMain.DecodeTimerTimer(Sender : TObject);
  2. var
  3. sProcessMsg :String;
  4. sSocketMsg :String;
  5. sSocketHandle :String;
  6. nSocketIndex :Integer;
  7. nMsgCount :Integer;
  8. nSendRetCode :Integer;
  9. nSocketHandle :Integer;
  10. dwDecodeTick :LongWord;
  11. dwDecodeTime :LongWord;
  12. sRemoteIPaddr :String;
  13. UserSession :pTUserSession;
  14. IPaddr :pTSockaddr;
  15. begin
  16. ShowMainLogMsg();
  17. if boDecodeLock or (not boGateReady)then exit;
  18. try
  19. dwDecodeTick:=GetTickCount();
  20. boDecodeLock:=True;
  21. sProcessMsg:=;
  22. while (True) do begin
  23. if ClientSockeMsgList.Count <= 0 then break;
  24. sProcessMsg:=sProcMsg + ClientSockeMsgList.Strings[0];
  25. sProcMsg:=;
  26. ClientSockeMsgList.Delete(0);
  27. while (True) do begin
  28. if TagCount(sProcessMsg,‘$’) < 1 then break;
  29. sProcessMsg:=ArrestStringEx(sProcessMsg,‘%’,‘$’,sSocketMsg);
  30. if sSocketMsg = then break;
  31. if sSocketMsg[1] = ‘+’ then begin
  32. if sSocketMsg[2] = ‘-‘ then begin
  33. CloseSocket(Str_ToInt(Copy(sSocketMsg,3,Length(sSocketMsg) 2),0));
  34. Continue;
  35. end else begin //0x004521B7
  36. dwKeepAliveTick:=GetTickCount();
  37. boKeepAliveTimcOut:=False;
  38. Continue;
  39. end;
  40. end; //0x004521CD
  41. sSocketMsg:=GetValidStr3(sSocketMsg,sSocketHandle,[‘/’]);
  42. nSocketHandle:=Str_ToInt(sSocketHandle,-1);
  43. if nSocketHandle < 0 then
  44. Continue;
  45. for nSocketIndex:= 0 to GATEMAXSESSION 1 do begin
  46. if g_SessionArray[nSocketIndex].SocketHandle = nSocketHandle then begin
  47. g_SessionArray[nSocketIndex].MsgList.Add(sSocketMsg);
  48. break;
  49. end;
  50. end;
  51. end; //0x00452246
  52. end; //0x452252
  53. //if sProcessMsg <> ” then ClientSockeMsgList.Add(sProcessMsg);
  54. if sProcessMsg <> then sProcMsg:=sProcessMsg;
  55. nSendMsgCount:=0;
  56. n456A2C:=0;
  57. StringList318.Clear;
  58. for nSocketIndex:= 0 to GATEMAXSESSION 1 do begin
  59. if g_SessionArray[nSocketIndex].SocketHandle <= 1 then Continue;
  60. //踢除超时无数据传输连接
  61. if (GetTickCount g_SessionArray[nSocketIndex].dwConnctCheckTick) > dwKeepConnectTimeOut then begin
  62. sRemoteIPaddr:=g_SessionArray[nSocketIndex].sRemoteIPaddr;
  63. case BlockMethod of //
  64. mDisconnect: begin
  65. g_SessionArray[nSocketIndex].Socket.Close;
  66. end;
  67. mBlock: begin
  68. New(IPaddr);
  69. IPaddr.nIPaddr:=inet_addr(PChar(sRemoteIPaddr));
  70. TempBlockIPList.Add(IPaddr);
  71. CloseConnect(sRemoteIPaddr);
  72. end;
  73. mBlockList: begin
  74. New(IPaddr);
  75. IPaddr.nIPaddr:=inet_addr(PChar(sRemoteIPaddr));
  76. BlockIPList.Add(IPaddr);
  77. CloseConnect(sRemoteIPaddr);
  78. end;
  79. end;
  80. MainOutMessage(‘端口空连接攻击: ‘ + sRemoteIPaddr,1);
  81. Continue;
  82. end;
  83. while (True) do begin;
  84. if g_SessionArray[nSocketIndex].MsgList.Count <= 0 then break;
  85. UserSession:=@g_SessionArray[nSocketIndex];
  86. nSendRetCode:=SendUserMsg(UserSession,UserSession.MsgList.Strings[0]);
  87. if (nSendRetCode >= 0) then
  88. begin
  89. if nSendRetCode = 1 then begin
  90. UserSession.dwConnctCheckTick:=GetTickCount();
  91. UserSession.MsgList.Delete(0);
  92. Continue;
  93. end;
  94. if UserSession.MsgList.Count > 100 then begin
  95. nMsgCount:=0;
  96. while nMsgCount <> 51 do begin
  97. UserSession.MsgList.Delete(0);
  98. Inc(nMsgCount);
  99. end;
  100. end;
  101. Inc(n456A2C,UserSession.MsgList.Count);
  102. MainOutMessage(UserSession.sIP +
  103. ‘ : ‘ +
  104. IntToStr(UserSession.MsgList.Count),5);
  105. Inc(nSendMsgCount);
  106. end else begin //0x004523A4
  107. UserSession.SocketHandle:= 1;
  108. UserSession.Socket:= nil;
  109. UserSession.MsgList.Clear;
  110. end;
  111. end;
  112. end;
  113. if (GetTickCount dwSendKeepAliveTick) > 2 * 1000 then begin
  114. dwSendKeepAliveTick:=GetTickCount();
  115. if boGateReady then
  116. ClientSocket.Socket.SendText(‘%–$’);
  117. end;
  118. if (GetTickCount dwKeepAliveTick) > 10 * 1000 then begin
  119. boKeepAliveTimcOut:=True;
  120. ClientSocket.Close;
  121. end;
  122. finally
  123. boDecodeLock:=False;
  124. end;
  125. dwDecodeTime:=GetTickCount dwDecodeTick;
  126. if dwDecodeMsgTime < dwDecodeTime then dwDecodeMsgTime:=dwDecodeTime;
  127. if dwDecodeMsgTime > 50 then Dec(dwDecodeMsgTime,50);
  128. end;

又是一坨代码,简而言之,就是把刚才保存接收到的数据。分发到 每个用户的自己的消息链表中,然后遍历,发送出去,
代码如下:

  1. // 发送用户消息
  2. function TFrmMain.SendUserMsg(UserSession:pTUserSession;sSendMsg:String):Integer;
  3. begin
  4. Result:= 1;
  5. // 如果
  6. if UserSession.Socket <> nil then begin
  7. // 取反
  8. if not UserSession.bo0C then begin
  9. // 如果不能发送,则置可用
  10. if not UserSession.boSendAvailable and (GetTickCount > UserSession.dwSendLockTimeOut) then begin
  11. UserSession.boSendAvailable := True;
  12. UserSession.nCheckSendLength := 0;
  13. boSendHoldTimeOut := True;
  14. dwSendHoldTick := GetTickCount();
  15. end; //004525DD
  16. if UserSession.boSendAvailable then begin
  17. if UserSession.nCheckSendLength >= 250 then begin
  18. if not UserSession.boSendCheck then begin
  19. UserSession.boSendCheck:=True;
  20. sSendMsg:=‘*’ + sSendMsg;
  21. end;
  22. if UserSession.nCheckSendLength >= 512 then begin
  23. UserSession.boSendAvailable:=False;
  24. UserSession.dwSendLockTimeOut:=GetTickCount + 3 * 1000;
  25. end;
  26. end; //00452620
  27. UserSession.Socket.SendText(sSendMsg);
  28. Inc(UserSession.nSendMsgLen,length(sSendMsg));
  29. Inc(UserSession.nCheckSendLength,length(sSendMsg));
  30. Result:= 1;
  31. end else begin //0x0045264A
  32. Result:= 0;
  33. end;
  34. end else begin //0x00452651
  35. Result:= 0;
  36. end;
  37. end;
  38. end;

登录网关,是不是很简单,这不是重点,重点是市面上的很多引擎的登录网关都基于这套机制,只需要逆向分析下其加密算法,一个自定义网关则出来了。至于过滤规则,什么IP通道,都是浮云…

好了.

接下来是SelGate

哈哈 一句话就可以KO它…

来来来…

其实,SelGate也就是 LoginGate,其源码实现完全相同。不必怀疑,市面上大家都是这么做的…

哈哈 哈哈…

1、本站admin账户所发布的源码均为测试过的源码
2、我们可以保证源码的完整性,不完整的源码可退款!
3、本站不在线下出售源码,请直接冲值购买!
4、由于商品特殊性,非源码本身问题概不退款!
5、会员发布的资源存在欺诈行为的可向QQ7951368举报,核实将获得奖励!
6、您的支持赞助就是本站长久运行!感谢广大爱好者会员用户的大力支持!

19191资源网 » 登陆网关(LoginGate) + 角色网关(SelGate)详解

发表评论

提供最优质的资源集合

立即查看 了解详情