P2pClient.pas
上传用户:zhanghw123
上传日期:2021-11-15
资源大小:312k
文件大小:13k
源码类别:

P2P编程

开发平台:

Delphi

  1. Unit P2pClient;
  2. Interface
  3. Uses Windows, SysUtils, Classes,
  4.   Messages, ComCtrls, IniFiles, Graphics,
  5.   P2pConst, P2pCommon,
  6.   NMUDP;
  7. Type
  8.   TP2pClient = Class
  9.     P2pLog: TRichEdit;
  10.     IsConnToServer: Boolean;
  11.   Public
  12.     ID: Cardinal; //唯一标示
  13.     Addr: String; //自己的名称 PTP时需要
  14.     Socket: TNMUDP;
  15.     UserList: THashedStringList; //用户列表
  16.     MMCfg: TP2pUserConfig;
  17.     Constructor Create(Logs: TRichEdit); Overload;
  18.     Destructor Destroy; Override;
  19.     Procedure Log(Const LogStr: String);
  20.     Procedure LoginServer(ICall: String);
  21.     Procedure LoginOutServer;
  22.     Procedure Living;
  23.     Procedure OnDataRec(Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer);
  24.     Procedure OnLoginBack(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  25.     Procedure OnNewUser(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  26.     Procedure OnLeaveUser(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  27.     Procedure OnUserList(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  28.     Procedure OnSomeOneWantDoor(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  29.     Procedure OnDoneDoor(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  30.     Procedure OnSayHello(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  31.     Procedure OnSayHelloResponse(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  32.     Procedure OnChat(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  33.     Procedure OnWantDoor(IDestUser: TObject);
  34.     Procedure SendChat(IUser: TObject; RichEdit: TRichEdit);
  35.   End;
  36. Implementation
  37. Uses CltMain;
  38. Constructor TP2pClient.Create(Logs: TRichEdit);
  39. Begin
  40.   P2pLog := Logs;
  41.   Log('创建点对点客户端服务...');
  42.   IsConnToServer := False;
  43.   MMCfg := TP2pUserConfig.Create;
  44.   Socket := TNMUDP.Create(Nil);
  45.   Socket.OnDataReceived := OnDataRec;
  46.   UserList := THashedStringList.Create;
  47.   Log('创建服务器成功.');
  48. End;
  49. Destructor TP2pClient.Destroy;
  50. Var
  51.   i: Integer;
  52. Begin
  53.   For i := 0 To UserList.Count - 1 Do
  54.     UserList.Objects[i].Free;
  55.   UserList.Free;
  56.   If MMCfg <> Nil Then FreeAndNil(MMCfg);
  57.   If Socket <> Nil Then FreeAndNil(Socket);
  58.   Log('释放服务器成功.');
  59.   Inherited;
  60. End;
  61. {日志事件}
  62. Procedure TP2pClient.Log(Const LogStr: String);
  63. Begin
  64.   //P2pLog.Font.Color := clBlack;
  65.   //P2pLog.SelAttributes.Color := clred;
  66.   P2pLog.Lines.Add(Format('%s %s', [FormatDateTime('YYYY-MM-DD HH:NN:SS', Now), LogStr]));
  67.   SendMessage(P2pLog.Handle, EM_SCROLLCARET, 0, 0);
  68. End;
  69. Procedure TP2pClient.LoginServer(ICall: String);
  70. Var
  71.   LU: TLoginUser;
  72. Begin
  73.   Log(Format('向%s:%d发起连接本地使用端口:%d.', [Socket.RemoteHost, Socket.RemotePort, Socket.LocalPort]));
  74.   LU.ID.ID := P2pLogin;
  75.   LU.Name := ICall;
  76.   Socket.SendBuffer(LU, SizeOf(LU));
  77. End;
  78. {登出服务}
  79. Procedure TP2pClient.LoginOutServer;
  80. Var
  81.   LU: TLoginoutUser;
  82. Begin
  83.   Log('断开服务.');
  84.   LU.ID.ID := P2pLoginOut;
  85.   LU.UserID := ID;
  86.   Socket.SendBuffer(LU, SizeOf(LU));
  87. End;
  88. {发送心跳给服务器}
  89. Procedure TP2pClient.Living;
  90. Var
  91.   Llving: TLiving;
  92. Begin
  93.   Llving.ID.ID := P2pAnyLifeing;
  94.   Llving.SourceUserId := ID;
  95.   Socket.SendBuffer(Llving, SizeOf(Llving));
  96.   MMCfg.LastLiveTime := GetTickCount;
  97. End;
  98. Procedure TP2pClient.OnDataRec(Sender: TComponent; NumberBytes: Integer; FromIP: String; Port: Integer);
  99. Var
  100.   Buf: Pointer;
  101.   Binding: TP2pBinding;
  102. Begin
  103.   If NumberBytes < 0 Then Exit;
  104.   Buf := GetMemory(NumberBytes);
  105.   Try
  106.     Try
  107.       Socket.ReadBuffer(Buf^, NumberBytes);
  108.       Binding.PeerIP := FromIP;
  109.       Binding.PeerPort := Port;
  110.       Log(Format('接收到命令:<%d>.', [Phead(Buf)^.ID]));
  111.       Case Phead(Buf)^.ID Of
  112.         P2pResultLoginBack: OnLoginBack(Buf, NumberBytes, Binding); {注册返回过程}
  113.         P2PALLNewUser: OnNewUser(Buf, NumberBytes, Binding); {有人进来了}
  114.         P2pALLLeaveUser: OnLeaveUser(Buf, NumberBytes, Binding); {有人离开了}
  115.         P2PGiveUserlist: OnUserList(Buf, NumberBytes, Binding); {获取在线用户列表}
  116.         P2pSomeOneWantDoor: OnSomeOneWantDoor(Buf, NumberBytes, Binding); {给用户请求P2P开门命令}
  117.         P2pDoneDoor: OnDoneDoor(Buf, NumberBytes, Binding); {通知客户端已经打开门了}
  118.         P2pSayHello: OnSayHello(Buf, NumberBytes, Binding); {对方的握手信息}
  119.         P2pSayHelloResponse: OnSayHelloResponse(Buf, NumberBytes, Binding); {收到握手的回复信息}
  120.         P2pChat: OnChat(Buf, NumberBytes, Binding); {收到的聊天信息}
  121.       End;
  122.     Except
  123.       On e: Exception Do
  124.         Log(Format('处理数据时异常原因:<%s->%s>', [e.ClassName, e.Message]));
  125.     End;
  126.   Finally
  127.     FreeMem(Buf, NumberBytes);
  128.   End;
  129. End;
  130. {登陆返回过程}
  131. Procedure TP2pClient.OnLoginBack(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  132. Var
  133.   Rlb: PResutLoginBack;
  134. Begin
  135.   Rlb := Data;
  136.   If Rlb^.State = 1 Then
  137.   Begin
  138.     IsConnToServer := True;
  139.     Addr := Rlb.Addr;
  140.     ID := Rlb.UserID;
  141.     Log(Format('登陆服务器成功 返回名称:%s,ID:%d.', [Addr, ID]));
  142.   End Else
  143.   Begin
  144.     Log('登陆服务器失败,也许是由于已经登陆导致.');
  145.     IsConnToServer := False;
  146.   End;
  147. End;
  148. {有人来了}
  149. Procedure TP2pClient.OnNewUser(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  150. Var
  151.   User: TP2pUser;
  152.   Obj: TListITem;
  153.   Binding: TP2pBinding;
  154. Begin
  155.   Log(Format('新用户加入%s:%d.', [PNewUser(Data)^.Ip, PNewUser(Data)^.Port]));
  156.   Binding.PeerIP := PNewUser(Data)^.Ip;
  157.   Binding.PeerPort := PNewUser(Data)^.Port;
  158.   User := TP2pUser.Create(Binding);
  159.   User.ID := PNewUser(Data)^.UserID;
  160.   User.Call := PNewUser(Data)^.Call;
  161.   UserList.AddObject(IntToStr(User.ID), User);
  162.   Obj := FrmMain.UserList.Items.Add;
  163.   With Obj Do
  164.   Begin
  165.     Caption := User.Call;
  166.     Data := User;
  167.     User.ListOwer := TObject(Obj);
  168.   End;
  169. End;
  170. {有人离开了}
  171. Procedure TP2pClient.OnLeaveUser(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  172. Var
  173.   Idx: Integer;
  174. Begin
  175.   Idx := UserList.IndexOf(Format('%d', [PLoginoutUser(Data)^.UserID]));
  176.   If Idx < 0 Then Exit;
  177.   Log(Format('用户%s离开了.', [TP2pUser(UserList.Objects[Idx]).Address]));
  178.   FrmMain.UserList.Items.Delete(TListITem(TP2pUser(UserList.Objects[Idx]).ListOwer).Index);
  179.   TP2pUser(UserList.Objects[Idx]).Free;
  180.   UserList.Delete(Idx);
  181. End;
  182. {接收在线用户列表}
  183. Procedure TP2pClient.OnUserList(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  184.   Procedure GetEveryWord(S: String; e: TStrings; C: String);
  185.   Var
  186.     t, a: String;
  187.   Begin
  188.     t := S;
  189.     While Pos(C, t) > 0 Do
  190.     Begin
  191.       a := Copy(t, 1, Pos(C, t) - 1);
  192.       t := Copy(t, Pos(C, t) + 1, Length(t) - Pos(C, t));
  193.       e.Add(a);
  194.     End;
  195.     If Trim(t) <> '' Then e.Add(t);
  196.   End;
  197. Var
  198.   User: TP2pUser;
  199.   Binding: TP2pBinding;
  200.   Count, Len: Word;
  201.   Items: String;
  202.   i: Integer;
  203.   List, BaseList: TStrings;
  204.   Obj: TListITem;
  205. Begin
  206.   Move(Pointer(PChar(Data) + SizeOf(Thead))^, Count, SizeOf(Count));
  207.   Move(Pointer(PChar(Data) + SizeOf(Thead) + SizeOf(Count))^, Len, SizeOf(Len));
  208.   Log(Format('接收在线用户列表共%d个.', [Count]));
  209.   If Count > 0 Then
  210.   Begin
  211.     SetLength(Items, Len);
  212.     Move(Pointer(PChar(Data) + SizeOf(Thead) + SizeOf(Len) + SizeOf(Len))^, PChar(Items)^, Len);
  213.     List := TStringList.Create;
  214.     BaseList := TStringList.Create;
  215.     Try
  216.       BaseList.Text := Items;
  217.       For i := 0 To BaseList.Count - 1 Do
  218.       Begin
  219.         List.Clear;
  220.         GetEveryWord(BaseList.Strings[i], List, ':');
  221.         Binding.PeerIP := List.Strings[0];
  222.         Binding.PeerPort := StrToInt(List.Strings[1]);
  223.         User := TP2pUser.Create(Binding);
  224.         User.ID := StrToInt(List.Strings[2]);
  225.         User.Call := List.Strings[3];
  226.         UserList.AddObject(Format('%d', [User.ID]), User);
  227.         Obj := FrmMain.UserList.Items.Add;
  228.         With Obj Do
  229.         Begin
  230.           Caption := User.Call;
  231.           Data := User;
  232.           User.ListOwer := TObject(Obj);
  233.         End;
  234.         OnWantDoor(User);
  235.       End; {FOR}
  236.     Finally
  237.       BaseList.Free;
  238.       List.Free;
  239.     End;
  240.   End; {IF}
  241. End;
  242. {服务器来通知有人敲门}
  243. Procedure TP2pClient.OnSomeOneWantDoor(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  244. Var
  245.   Idx: Integer;
  246.   OD: TOpenDoor;
  247.   WD: TWantDoor;
  248. Begin
  249.   Idx := UserList.IndexOf(IntToStr(PWantDoor(Data)^.SourceId));
  250.   If Idx < 0 Then Exit;
  251.   Log(Format('服务端通知%s想和你P2P.', [TP2pUser(UserList.Objects[Idx]).Address]));
  252.   //目标用户存在 给他开门
  253.   OD.ID.ID := P2pOpenDoor;
  254.   TP2pUser(UserList.Objects[Idx]).Socket.SendBuffer(OD, SizeOf(OD));
  255.   Log(Format('已经给%s打开门了.', [TP2pUser(UserList.Objects[Idx]).Address]));
  256.   // 告诉服务器已经打开门了
  257.   WD.ID.ID := P2pDoorOpened;
  258.   WD.SourceId := ID;
  259.   WD.DestID := PWantDoor(Data)^.SourceId;
  260.   Socket.SendBuffer(WD, SizeOf(WD));
  261.   Log(Format('通知服务端已经给%s打开门了.', [TP2pUser(UserList.Objects[Idx]).Address]));
  262. End;
  263. {服务器转告申请开门成功 可以握手了}
  264. Procedure TP2pClient.OnDoneDoor(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  265. Var
  266.   SH: TSayHello;
  267.   Idx: Integer;
  268. Begin
  269.   Idx := UserList.IndexOf(IntToStr(PWantDoor(Data)^.DestID));
  270.   If Idx < 0 Then Exit;
  271.   Log(Format('服务器通知%s已经把门打开.', [TP2pUser(UserList.Objects[Idx]).Address]));
  272.   // 对方把门打开了 发送握手信息
  273.   SH.ID.ID := P2pSayHello;
  274.   SH.UserID := ID;
  275.   TP2pUser(UserList.Objects[Idx]).Socket.SendBuffer(SH, SizeOf(SH));
  276.   Log(Format('向%S发送握手信息,看对方是否在线.', [TP2pUser(UserList.Objects[Idx]).Address]));
  277. End;
  278. {对方的握手信息}
  279. Procedure TP2pClient.OnSayHello(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  280. Var
  281.   Idx: Integer;
  282.   RH: TSayHello;
  283. Begin
  284.   Idx := UserList.IndexOf(Format('%d', [PSayHello(Data)^.UserID]));
  285.   If Idx < 0 Then Exit;
  286.   RH.ID.ID := P2pSayHelloResponse;
  287.   RH.UserID := ID;
  288.   TP2pUser(UserList.Objects[Idx]).Socket.SendBuffer(RH, SizeOf(RH));
  289.   Log(Format('接收到用户%S发送的握手信息,给他个回复.', [TP2pUser(UserList.Objects[Idx]).Address]));
  290. End;
  291. {用户友好的握手了}
  292. Procedure TP2pClient.OnSayHelloResponse(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  293. Var
  294.   Idx: Integer;
  295. Begin
  296.   Idx := UserList.IndexOf(Format('%d', [PSayHello(Data)^.UserID]));
  297.   If Idx < 0 Then Exit;
  298.   Log(Format('%s回复了你的握手信息,成功连接.', [TP2pUser(UserList.Objects[Idx]).Address]));
  299. End;
  300. {收到的聊天信息}
  301. Procedure TP2pClient.OnChat(Data: Pointer; NumberBytes: Integer; ABinding: TP2pBinding);
  302. Var
  303.   Len: Word;
  304.   Who: Cardinal;
  305.   Idx: Integer;
  306.   Items: String;
  307.   RF: TREFont;
  308. Begin
  309.   Move(Pointer(PChar(Data) + SizeOf(Thead))^, Len, SizeOf(Len));
  310.   If Len <= 0 Then Exit;
  311.   SetLength(Items, Len);
  312.   Move(Pointer(PChar(Data) + SizeOf(Thead) + SizeOf(Len))^, Who, SizeOf(Who));
  313.   Move(Pointer(PChar(Data) + SizeOf(Thead) + SizeOf(Len) + SizeOf(Who))^, RF, SizeOf(RF));
  314.   Move(Pointer(PChar(Data) + SizeOf(Thead) + SizeOf(Len) + SizeOf(Who) + SizeOf(RF))^, PChar(Items)^, Len);
  315.   With FrmMain.TxtMsg Do
  316.   Begin
  317.     SelAttributes.Color := clBlue;
  318.     Idx := UserList.IndexOf(Format('%d', [Who]));
  319.     If Idx = -1 Then Exit;
  320.     Lines.Add(Format('[%s] [%s]对你说:', [FormatDateTime('YYYY-MM-DD HH:NN:SS', Now), TP2pUser(UserList.Objects[Idx]).Call]));
  321.     SelAttributes.Charset := RF.Charset;
  322.     SelAttributes.Name := RF.Name;
  323.     SelAttributes.Size := RF.Size;
  324.     SelAttributes.Color := RF.Color;
  325.     SelAttributes.Style := RF.Style;
  326.     SelAttributes.Pitch := RF.Pitch;
  327.     Lines.Add(Items + #13#10);
  328.     SendMessage(Handle, EM_SCROLLCARET, 0, 0);
  329.   End;
  330. End;
  331. {想与其它用户P2P}
  332. Procedure TP2pClient.OnWantDoor(IDestUser: TObject);
  333. Var
  334.   WD: TWantDoor;
  335. Begin
  336.   Log(Format('向服务端请求PTP 目标:%s.', [TP2pUser(IDestUser).Address]));
  337.   WD.ID.ID := P2pWantDoor;
  338.   WD.SourceId := ID;
  339.   WD.DestID := TP2pUser(IDestUser).ID;
  340.   Socket.SendBuffer(WD, SizeOf(WD));
  341. End;
  342. {向对方发送聊天信息}
  343. Procedure TP2pClient.SendChat(IUser: TObject; RichEdit: TRichEdit);
  344. Var
  345.   MS: TMemoryStream;
  346.   Head: Thead;
  347.   Who: Cardinal;
  348.   Len: Word;
  349.   RF: TREFont;
  350. Begin
  351.   With RichEdit Do
  352.   Begin
  353.     Log(Format('向用户%s发送聊天信息<%s>', [TP2pUser(IUser).Address, Text]));
  354.     Head.ID := P2pChat;
  355.     Len := Length(Text);
  356.     RF.Charset := SelAttributes.Charset;
  357.     RF.Name := SelAttributes.Name;
  358.     RF.Size := SelAttributes.Size;
  359.     RF.Color := SelAttributes.Color;
  360.     RF.Style := SelAttributes.Style;
  361.     RF.Pitch := SelAttributes.Pitch;
  362.   End;
  363.   Who := ID;
  364.   MS := TMemoryStream.Create;
  365.   Try
  366.     MS.SetSize(SizeOf(Head) + SizeOf(Who) + SizeOf(RF) + Len);
  367.     MS.WriteBuffer(Head, SizeOf(Head));
  368.     MS.WriteBuffer(Len, SizeOf(Len));
  369.     MS.WriteBuffer(Who, SizeOf(Who));
  370.     MS.WriteBuffer(RF, SizeOf(RF));
  371.     MS.WriteBuffer(Pointer(RichEdit.Text)^, Len);
  372.     TP2pUser(IUser).Socket.SendStream(MS);
  373.   Finally
  374.     MS.Free;
  375.   End;
  376. End;
  377. End.