MainUnit.pas
上传用户:xgd119
上传日期:2007-05-02
资源大小:514k
文件大小:14k
源码类别:

P2P编程

开发平台:

Delphi

  1. unit MainUnit;
  2. {
  3.    P2P方式模拟QQ即时消息通讯
  4.    thanksharp@163.com
  5. 应考虑问题:
  6.     1.加入TIMER.定时向好友发送握手包,以维护网关NAT的会话SESSION.
  7. }
  8. interface
  9. uses
  10.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  11.   Dialogs, StdCtrls, ExtCtrls, ComCtrls, IdBaseComponent, IdComponent,
  12.   IdUDPBase, IdUDPServer,IdSocketHandle,testQQCommon, WinSkinData,AboutUnit;
  13. type
  14.   TClient = class(TForm)
  15.     Label1: TLabel;
  16.     UserNameEdit: TEdit;
  17.     Label2: TLabel;
  18.     ServerIPEdit: TEdit;
  19.     Label3: TLabel;
  20.     ServerPortEdit: TEdit;
  21.     Panel1: TPanel;
  22.     LogonButton: TButton;
  23.     LogoutButton: TButton;
  24.     Panel2: TPanel;
  25.     SendMsgEdit: TRichEdit;
  26.     UserList: TListView;
  27.     SendMsgButton: TButton;
  28.     StatusBar1: TStatusBar;
  29.     UDPClient: TIdUDPServer;
  30.     RecvEdit: TRichEdit;
  31.     CheckBox1: TCheckBox;
  32.     P2PTestButton: TButton;
  33.     Label4: TLabel;
  34.     Button1: TButton;
  35.     SkinData1: TSkinData;
  36.     Button2: TButton;
  37.     procedure FormCreate(Sender: TObject);
  38.     procedure LogonButtonClick(Sender: TObject);
  39.     procedure LogoutButtonClick(Sender: TObject);
  40.     procedure UDPClientUDPRead(Sender: TObject; AData: TStream;
  41.       ABinding: TIdSocketHandle);
  42.     procedure SendMsgButtonClick(Sender: TObject);
  43.     procedure FormClose(Sender: TObject; var Action: TCloseAction);
  44.     procedure P2PTestButtonClick(Sender: TObject);
  45.     procedure Button1Click(Sender: TObject);
  46.     procedure Button2Click(Sender: TObject);
  47.   private
  48.     { Private declarations }
  49.   public
  50.     { Public declarations }
  51.     MyAccount,ServerIP,MyPublicIP:String;
  52.     MyRunPORT,ServerPORT,MyPublicPORT:Integer;
  53.     function StartUp():Boolean;
  54.     function CallLogout():Boolean;
  55.     function SendHandData(toIP:string;toPORT:Integer):Boolean;
  56.     function ProcRecvLogonData(ThisBinding:TIdSocketHandle;LogonData:TLogonData):boolean;
  57.     function ProcRecvLogoutData(ThisBinding:TIdSocketHandle;LogoutData:TLogoutData):boolean;
  58.     function ProcRecvFriendData(ThisBinding:TIdSocketHandle;UserData:TUserData):Boolean;
  59.     function ProcRecvP2PData(ThisBinding:TIdSocketHandle;HandData:THandData):boolean;
  60.     function ProcRecvChatMsgData(ThisBinding:TIdSocketHandle;ChatData:TChatData):boolean;
  61.   end;
  62. var
  63.   Client: TClient;
  64.   AboutBox: TAboutBox;
  65. implementation
  66. function TClient.StartUp():Boolean;
  67. var Listened:Boolean;
  68.     ClientPort,MaxPort:Integer;
  69.     ASocketHandle:TIdSockethandle;
  70. begin
  71.    Listened:=false;
  72.    ClientPort:=CLIENT_PORT;
  73.    MaxPort:=ClientPort+10;
  74.    Listened:=true;
  75.    repeat        //处理多个程序同时运行的侦听端口问题
  76.    try
  77.       UDPClient.DefaultPort:=ClientPort;
  78.       UDPClient.Active :=true;
  79.       Listened:=true;
  80.       break;
  81.    except  on EIdCouldNotBindSocket do
  82.    begin
  83.       UDPClient.Active:=false;
  84.       UDPClient.Bindings.Clear;
  85.       ClientPort:=ClientPort+1;
  86.       if ClientPort>MaxPort then
  87.          break;
  88.       end;
  89.    end;
  90.    until not Listened;
  91.    if Listened then
  92.    begin
  93.       MyRunPORT:=UDPclient.DefaultPort ;
  94.       StatusBar1.Panels.Items[1].Text:='运行端口:['+IntToStr(MyRunPORT)+']';
  95.       Result:=true;
  96.    end
  97.    else
  98.    begin
  99.       StatusBar1.Panels.Items[1].Text:='侦听失败!';
  100.       Result:=false;
  101.    end;
  102. end;
  103. {$R *.dfm}
  104. procedure TClient.FormCreate(Sender: TObject);
  105. begin
  106.    StartUp();
  107. end;
  108. //登录上线
  109. procedure TClient.LogonButtonClick(Sender: TObject);
  110. var LogonDataPackage:TLogonDataPackage;
  111.     _ServerIP,_Account:string;
  112.     _ServerPORT:Integer;
  113. begin
  114.    _ServerIP:=ServerIPEdit.Text;
  115.    _ServerPORT:=StrToInt(ServerPORTEdit.text);
  116.    _Account:=UserNameEdit.Text;
  117.    with LogonDataPackage do
  118.    begin
  119.       Head.MsgType:=IntToStr(LogonSign);
  120.         StrPCopy(Body.Account,_Account);
  121.         StrPCopy(Body.MyPublicIP,'');
  122.         StrPCopy(Body.MyPublicPORT,'');
  123.         StrPCopy(Body.lResult,'');
  124.       //
  125.    end;
  126.    UdpClient.SendBuffer(_ServerIP,_ServerPORT,LogonDataPackage,SizeOf(LogonDataPackage));
  127. end;
  128. //注销下线
  129. procedure TClient.LogoutButtonClick(Sender: TObject);
  130. begin
  131.    CallLogout();
  132. end;
  133. //向服务器注销
  134. function TClient.CallLogout():Boolean;
  135. var LogoutDataPackage:TLogoutDataPackage;
  136.     i,_ToPORT:integer;
  137.     _toIP:String;
  138. begin
  139.    with LogoutDataPackage do
  140.    begin
  141.      Head.MsgType:=IntToStr(LogoutSign);
  142.        StrPCopy(Body.Account,MyAccount);
  143.        StrPCopy(Body.lResult,'');
  144.    end;
  145.    UdpClient.SendBuffer(ServerIP,ServerPort,LogoutDataPackage,SizeOf(LogoutDataPackage));
  146.       //向好友发送下线信号
  147.    for i:=0 to UserList.Items.Count-1 do
  148.    begin
  149.    _ToIP:=UserList.Items.Item[i].SubItems[0];
  150.    _ToPORT:=StrToInt(UserList.Items.Item[i].SubItems[1]);
  151.    with LogoutDataPackage do
  152.    begin
  153.       Head.MsgType:=IntToStr(LogoutSign);
  154.         StrPCopy(Body.Account,MyAccount);
  155.         StrPCopy(Body.lResult,'');
  156.       //
  157.    end;
  158.    UdpClient.SendBuffer(_toIP,_toPORT,LogoutDataPackage,SizeOf(LogoutDataPackage));
  159.    Sleep(1);
  160.    end;
  161. end;
  162. //UdpClient 读取数据
  163. procedure TClient.UDPClientUDPRead(Sender: TObject; AData: TStream;
  164.   ABinding: TIdSocketHandle);
  165. var _UDPHead:TTQQUDPHead;
  166.     _LogonData:TLogonData;
  167.     _LogoutData:TLogoutData;
  168.     _UserData:TUserData;
  169.     _HandData:THandData;
  170.     _ChatData:TChatData;
  171.     RecvSize,MsgType:integer;
  172. begin
  173. try
  174.    MsgType:=-1;
  175.    RecvSize:=adata.Read (_UDPHead,sizeof(_UDPHead) );  //接收数据头
  176.    MsgType:=StrToInt(_UDPHead.MsgType);
  177.    if Msgtype=-1 then exit;
  178. //   UDPSERVER.Binding.Assign(ABinding); //2005-02-17 Updated!
  179.    case  MsgType of
  180.      LogonSign:
  181.      begin
  182.        //登录 <--Server ReBack
  183.        Adata.Read(_LogonData,sizeof(TLogonData));
  184.        ProcRecvLogonData(Abinding,_LogonData);
  185.      end;
  186.      LogoutSign:
  187.      begin
  188.        //注销 <--Server or Friend
  189.        Adata.Read(_LogoutData,sizeof(TLogoutData));
  190.        ProcRecvLogoutData(Abinding,_LogoutData);
  191.      end;
  192.      FriendDataSign:
  193.      begin
  194.        //收到好友列表信息
  195.        Adata.Read(_UserData,sizeof(TUserData));
  196.        ProcRecvFriendData(Abinding,_UserData);
  197.      end;
  198.      HandSign:
  199.      begin
  200.        //处理P2P请求
  201.        Adata.Read(_HandData,sizeof(THandData));
  202.        ProcRecvP2PData(Abinding,_HandData);
  203.      end;
  204.      ChatMsgSign:
  205.      begin
  206.        //处理聊天消息
  207.        Adata.Read(_ChatData,sizeof(TChatData));
  208.        ProcRecvChatMsgData(Abinding,_ChatData);
  209.      end;
  210.    end;
  211.   except on E:Exception do
  212.      //Memo1.lines.add(E.Message);
  213.   end;
  214. end;
  215. //收到登录服务器的反馈信息处理
  216. function TClient.ProcRecvLogonData(ThisBinding:TIdSocketHandle;LogonData:TLogonData):boolean;
  217. var _LogonDataPackage:TLogonDataPackage;
  218.     _HandDataPackage:THandDataPackage;
  219.     _tempUserBasicInfo:TServerUserBasicInfo;
  220.     _Account,_PeerIP,_tempIP,_NeedReBack,_isLogin:string;
  221.     i,_PeerPORT,_tempPORT:integer;
  222. begin
  223.    _Account:=LogonData.Account;
  224.    _PeerIP:=thisbinding.PeerIP ;
  225.    _PeerPORT:=thisbinding.PeerPort ;
  226.    _IsLogin:=LogonData.lResult;
  227.    //登录成功
  228.    if _IsLogin=IsTrue then
  229.    begin
  230.      ServerIP:=_PeerIP;
  231.      ServerPORT:=_PeerPORT;
  232.      MyPublicIP:=LogonData.MyPublicIP ;
  233.      MyPublicPORT:=StrToInt(LogonData.MyPublicPORT);
  234.      MyAccount:=LogonData.Account ;
  235.      SendMsgButton.Enabled:=true;
  236.      P2PTestButton.Enabled:=true;
  237.      LogoutButton.Enabled:=true;
  238.      LogonButton.Enabled:=false;
  239.     StatusBar1.Panels.Items[0].Text:='登录成功...';
  240.    end;
  241. end;
  242. //接收到用户注销处理
  243. function TClient.ProcRecvLogoutData(ThisBinding:TIdSocketHandle;LogoutData:TLogoutData):boolean;
  244. var _Account,_tempIP,_PeerIP:string;
  245.     i,TheTag,_tempPort,_PeerPORT:Integer;
  246.     _tempUserBasicInfo:TServerUserBasicInfo;
  247.     LogoutDataPackage:TLogoutDataPackage;
  248. begin
  249.   _Account:=LogoutData.Account;
  250.   if _Account=MyAccount then
  251.   begin
  252.      MyAccount:='';
  253.      MyPublicIP:='';
  254.      MyPublicPort:=0;
  255.      SendMsgButton.Enabled:=false;
  256.      P2PTestButton.Enabled:=false;
  257.      LogonButton.Enabled:=true;
  258.      LogoutButton.Enabled:=false;
  259.   end
  260.   else //好友离线
  261.   begin
  262.     for i:=0 to UserList.Items.Count -1 do
  263.     begin
  264.        if UserList.Items.Item[0].Caption=_Account then
  265.        begin
  266.          UserList.Items.Delete(i);
  267.          RecvEdit.Lines.Add('['+IntToStr(RecvEdit.Lines.Count)+']Frien is Offline,From:['+_Account+'] ');
  268.          Break;
  269.        end;
  270.     end;
  271.   end;
  272.   _PeerIP:=thisbinding.PeerIP ;
  273.   _PeerPORT:=thisbinding.PeerPort;
  274. end;
  275. //收到好友列表处理
  276. function TClient.ProcRecvFriendData(ThisBinding:TIdSocketHandle;UserData:TUserData):Boolean;
  277. var _Account,_FriendIP,_FriendPort:String;
  278.    tempItem:TListItem;
  279.    HandDataPackage:THandDataPackage;
  280. begin
  281.   _Account:=UserData.Account ;
  282.   _FriendIP:=UserData.IP;
  283.   _FriendPort:=UserData.PORT;
  284.   if UserData.IsFirstOne=IsTrue then
  285.   begin
  286.      UserList.Items.Clear;
  287.   end;
  288.   tempItem:=UserList.Items.Add;
  289.   tempItem.Caption:=_Account;
  290.   tempItem.SubItems.Add(_FriendIP);
  291.   tempItem.SubItems.Add(_FriendPort);
  292.   with HandDataPackage do
  293.   begin
  294.       Head.MsgType:=IntToStr(HandSign);
  295.         StrPCopy(Body.Account,MyAccount);
  296.         StrPCopy(Body.Mark,'');
  297.         StrPCopy(Body.DesIP,MyPublicIP);
  298.         StrPCopy(Body.DesPORT,IntToStr(MyPublicPORT));
  299.         StrPCopy(Body.NeedReBack,IsTrue);
  300.       //
  301.   end;
  302.   UdpClient.SendBuffer(_FriendIP,StrToInt(_FriendPort),HandDataPackage,SizeOf(HandDataPackage));
  303. end;
  304. //处理握手P2P请求
  305. function TClient.ProcRecvP2PData(ThisBinding:TIdSocketHandle;HandData:THandData):boolean;
  306. var _Account,Mark,toIP,toPort:string;
  307.     HandDataPackage:THandDataPackage;
  308.     tempItem:TListItem;
  309. begin
  310.     _Account:=HandData.Account ;
  311.     toIP:=HandData.DesIP ;
  312.     toPort:=HandData.DesPORT;
  313.     Mark:=HandData.Mark ;
  314.     if HandData.NeedReBack=IsTrue then
  315.     begin
  316.       with HandDataPackage do
  317.       begin
  318.          Head.MsgType:=IntToStr(HandSign);
  319.            Body.Account:=HandData.Account;
  320.            Body.DesIP:=HandData.DesIP;
  321.            Body.DesPORT:=HandData.DesPORT;
  322.            Body.NeedReBack:=IsFalse;
  323.          //Head.DataSize:=SizeOf(HandDataPackage);
  324.       end;
  325.       if HandData.IsDirected=IsTrue then
  326.          UdpClient.SendBuffer(ThisBinding.PeerIP,ThisBinding.PeerPort,HandDataPackage,SizeOf(HandDataPackage))
  327.       else
  328.          UdpClient.SendBuffer(toIP,StrToInt(toPort),HandDataPackage,SizeOf(HandDataPackage));
  329.       RecvEdit.Lines.Add('['+IntToStr(RecvEdit.Lines.Count)+']Recv HandData,From:['+_Account+'] ['+toIP+':'+toPORT+'] SentBack!');
  330.       Sleep(1);
  331.    end
  332.    else
  333.    begin
  334.       RecvEdit.Lines.Add('['+IntToStr(RecvEdit.Lines.Count)+']Recv HandData,From:['+_Account+'] ['+toIP+':'+toPORT+'] ');
  335.    end;
  336.    if Mark=IsTrue then  //是好友的上线信息
  337.    begin
  338.       tempItem:=UserList.Items.Add ;
  339.       tempItem.Caption:=_Account;
  340.       tempItem.SubItems.Add(toip);
  341.       tempItem.SubItems.Add(toPort);
  342.    end;
  343. end;
  344. //处理聊天消息
  345. function TClient.ProcRecvChatMsgData(ThisBinding:TIdSocketHandle;ChatData:TChatData):boolean;
  346. var ChatDataPackage:TChatDataPackage;
  347.     _fromAccount,_Msg:string;
  348. begin
  349.    _fromAccount:=ChatData.fromAccount ;
  350.    _Msg:=ChatData.Msg ;
  351.    if ChatData.IsNeedReBack=IsTrue then
  352.    begin
  353.      with ChatDataPackage do
  354.      begin
  355.         Head.MsgType:=IntToStr(ChatMsgSign);
  356.            StrPCopy(Body.fromAccount,MyAccount);
  357.            StrPCopy(Body.Msg,'');
  358.            StrPCopy(Body.IsNeedReBack,IsFalse);
  359.            StrPCopy(Body.IsReBackSigh,IsTrue);
  360.         //
  361.        UdpClient.SendBuffer(ThisBinding.PeerIP,ThisBinding.PeerPort,ChatDataPackage,SizeOf(ChatDataPackage));
  362.      end;
  363.    end;
  364.      //接收到的是消息
  365.    if ChatData.IsReBackSigh=IsTrue then
  366.    begin
  367.      RecvEdit.Lines.Add('['+IntToStr(RecvEdit.Lines.Count)+']Peer Recv ChatMsg OK,From:['+_fromAccount+']');
  368.    end
  369.    else
  370.    begin
  371.      RecvEdit.Lines.Add('['+IntToStr(RecvEdit.Lines.Count)+']Recv ChatMsg From:['+_fromAccount+']');
  372.      RecvEdit.Lines.Add('['+IntToStr(RecvEdit.Lines.Count)+']'+_Msg);
  373.    end;
  374. end;
  375. //发送消息
  376. procedure TClient.SendMsgButtonClick(Sender: TObject);
  377. var ChatDataPackage:TChatDataPackage;
  378.     _ToIP,_SendMsg,_ToAccount:string;
  379.     _ToPORT:Integer;
  380.     tempItem:TListItem;
  381. begin
  382.   tempItem:=UserList.Selected;
  383.   if (not assigned(tempItem)) or (tempItem=nil) then
  384.   begin
  385.      MessageBox(self.Handle ,'请选择消息接收者!','提示',0);
  386.      exit;
  387.   end;
  388.   _ToAccount:=tempItem.Caption;
  389.   _ToIp:=tempItem.SubItems[0];
  390.   _ToPORT:=StrToInt(tempItem.SubItems[1]);
  391.   _SendMsg:=SendMsgEdit.Text ;
  392.   if length(_SendMsg)>500 then exit;
  393.   with ChatDataPackage do
  394.   begin
  395.      Head.MsgType:=IntToStr(ChatMsgSign);
  396.         StrPCopy(Body.fromAccount,MyAccount);
  397.         StrPCopy(Body.toAccount,_ToAccount);
  398.         StrPCopy(Body.Msg,_SendMsg);
  399.         if CheckBox1.Checked then
  400.            StrPCopy(Body.IsNeedReBack,IsTrue)
  401.         else
  402.           StrPCopy(Body.IsNeedReBack,IsFalse);
  403.         StrPCopy(Body.IsReBackSigh,IsFalse);
  404.      //
  405.   end;
  406.   UdpClient.SendBuffer(_ToIP,_ToPORT,ChatDataPackage,SizeOf(ChatDataPackage));
  407. end;
  408. procedure TClient.FormClose(Sender: TObject; var Action: TCloseAction);
  409. begin
  410.    CallLogout();
  411. end;
  412. //发送握手信息P2P
  413. function TClient.SendHandData(toIP:string;toPORT:Integer):Boolean;
  414. var HandDataPackage:THandDataPackage;
  415.     LogoutDataPackage:TLogoutDataPackage;
  416. begin
  417.    with HandDataPackage do
  418.    begin
  419.       Head.MsgType:=IntToStr(HandSign);
  420.        StrPCopy(Body.Account,MyAccount);
  421.        StrPCopy(Body.DesIP,toIP);
  422.        StrPCopy(Body.DesPORT,IntToStr(toPORT));
  423.        StrPCopy(Body.NeedReBack,IsTrue);
  424.        StrPCopy(Body.IsDirected,IsTrue);
  425.       //Head.DataSize:=SizeOf(HandDataPackage);
  426.    end;
  427.    UdpClient.SendBuffer(toIP,toPort,HandDataPackage,SizeOf(HandDataPackage));
  428.    Sleep(1);
  429. end;
  430. procedure TClient.P2PTestButtonClick(Sender: TObject);
  431. var  _ToIP,_SendMsg,_ToAccount:string;
  432.      _ToPORT:Integer;
  433.      tempItem:TListItem;
  434. begin
  435.   tempItem:=UserList.Selected;
  436.   if (not assigned(tempItem)) or (tempItem=nil) then
  437.   begin
  438.      MessageBox(self.Handle ,'请选择消息接收者!','提示',0);
  439.      exit;
  440.   end;
  441.   _ToAccount:=tempItem.Caption;
  442.   _ToIp:=tempItem.SubItems[0];
  443.   _ToPORT:=StrToInt(tempItem.SubItems[1]);
  444.   SendHandData(_toIP,_ToPORT);
  445. end;
  446. procedure TClient.Button1Click(Sender: TObject);
  447. begin
  448.    RecvEdit.Clear ;
  449. end;
  450. procedure TClient.Button2Click(Sender: TObject);
  451. begin
  452.  AboutBox:= TAboutBox.Create(self);
  453.  AboutBox.ShowModal ;
  454. end;
  455. end.