因为公网和内网的IP 不一样无法实现!请高手说话。

解决方案 »

  1.   

    1.并不是很现实~
    2.qq并不是一开始就进行点对点通信的,开始要在服务器进行ip的存储和交互,之后的数据就不通过服务器了~也就是qq的客户在登录时告诉了服务器,
    其他的好友的ip也是经过服务器,才能得到~~
    3.听一个高手(《delphi串口及语音高级编程》的作者),他通过直接扫描的
    方法可以实现,udp的无服务器跨代理直接通信,摩托不知道具体的实现方式~~
    4. :(  摩托好菜~~~
      

  2.   

    OICQ是深圳腾讯公司的一个网络实时通讯软件,在国内拥有大量的用户群。但OICQ必须连接上互联网登陆到腾讯的服务器才能使用。所以我们可以自己写一个在局部网里面使用。
    OICQ使用的是UDP协议,这是一种无连接协议,即通信双方不用建立连接就可以发送信息,所以效率比较高。Delphi本身自带的FastNEt公司的NMUDP控件就是一个UDP协议的用户数据报控件。不过要注意的是如果你使用了这个控件必须退出程序才能关闭计算机,因为TNMXXX控件有BUG。所有nm控件的基础 PowerSocket用到的ThreadTimer,用到一个隐藏的窗口(类为TmrWindowClass)处理有硬伤
      

  3.   

    新建一个工程,在FASTNET面版拖一个NMUDP控件到窗口,然后依次放上三个EDIT,名字分别为EditIP、EditPort、EditMyTxt,三个按钮BtSend、BtClear、BtSave,一个MEMOMemoReceive,一个SaveDialog和一个状态条StatusBar1。当用户点击BtSend时,建立一个内存流对象,把要发送的文字信息写进内存流,然后NMUDP把流发送出去。当NMUDP有数据接收时,触发它的DataReceived事件,我们在这里再把接收到的流转换为字符信息,然后显示出来。
      注意:所有的流对象建立后使用完毕后要记得释放(Free),其实它的释构函数应该为Destroy,但如果建立流失败的话,用Destroy会产生异常,而用Free的话程序会先检查有没有成功建立了流,如果建立了才释放,所以用Free比较安全。
      在这个程序中我们用到了NMUDP控件,它有几个重要的属性。RemoteHost表示远程电脑的IP或者计算机名,LocalPort是本地端口,主要监听有没有数据传入。而RemotePort是远程端口,发送数据时通过这个端口把数据发送出去。理解这些已经可以看懂我们的程序了。
    全部代码如下:
    unit Unit1;
    interface
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, NMUDP;
    type
      TForm1 = class(TForm)
        NMUDP1: TNMUDP;
        EditIP: TEdit;
        EditPort: TEdit;
        EditMyTxt: TEdit;
        MemoReceive: TMemo;
        BtSend: TButton;
        BtClear: TButton;
        BtSave: TButton;
        StatusBar1: TStatusBar;
        SaveDialog1: TSaveDialog;
        procedure BtSendClick(Sender: TObject);
        procedure NMUDP1DataReceived(Sender: TComponent; NumberBytes: Integer;
          FromIP: string; Port: Integer);
        procedure NMUDP1InvalidHost(var handled: Boolean);
        procedure NMUDP1DataSend(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure BtClearClick(Sender: TObject);
        procedure BtSaveClick(Sender: TObject);
        procedure EditMyTxtKeyPress(Sender: TObject; var Key: Char);
      private
    { Private declarations }
      public
    { Public declarations }
      end;
    var
      Form1: TForm1;
    implementation
    {$R *.DFM}
    procedure TForm1.BtSendClick(Sender: TObject);
    var
      MyStream: TMemoryStream;
      MySendTxt: string;
      Iport, icode: integer;
    begin
      Val(EditPort.Text, Iport, icode);
      if icode< > 0 then
      begin
        Application.MessageBox('端口必须为数字,请重新输入!', '信息', MB_ICONINFORMATION + MB_OK);
        Exit;
      end;
      NMUDP1.RemoteHost := EditIP.Text; {远程主机}
      NMUDP1.LocalPort := Iport; {本地端口}
      NMUDP1.RemotePort := Iport; {远程端口}
      MySendTxt := EditMyTxt.Text;
      MyStream := TMemoryStream.Create; {建立流}
      try
        MyStream.Write(MySendTxt[1], Length(EditMyTxt.Text)); {写数据}
        NMUDP1.SendStream(MyStream); {发送流}
      finally
        MyStream.Free; {释放流}
      end;
    end;
    procedure TForm1.NMUDP1DataReceived(Sender: TComponent;
      NumberBytes: Integer; FromIP: string; Port: Integer);
    var
      MyStream: TMemoryStream;
      MyReciveTxt: string;
    begin
      MyStream := TMemoryStream.Create; {建立流}
      try
        NMUDP1.ReadStream(MyStream); {接收流}
        SetLength(MyReciveTxt, NumberBytes); {NumberBytes为接收到的字节数}
        MyStream.Read(MyReciveTxt[1], NumberBytes); {读数据}
        MemoReceive.Lines.Add('接收到来自主机' + FromIP + '的信息:' + MyReciveTxt);
      finally
        MyStream.Free; {释放流}
      end;
    end;procedure TForm1.NMUDP1InvalidHost(var handled: Boolean);
    begin
      Application.MessageBox('对方IP地址不正确,请重新输入!', '信息', MB_ICONINFORMATION + MB_OK);
    end;procedure TForm1.NMUDP1DataSend(Sender: TObject);
    begin
      StatusBar1.SimpleText := '信息成功发出!';
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      EditIP.Text := '127.0.0.1';
      EditPort.Text := '8868';
      BtSend.Caption := '发送';
      BtClear.Caption := '清除聊天记录';
      BtSave.Caption := '保存聊天记录';
      MemoReceive.ScrollBars := ssBoth;
      MemoReceive.Clear;
      EditMyTxt.Text := '在这里输入信息,然后点击发送.';
      StatusBar1.SimplePanel := true;
    end;
    procedure TForm1.BtClearClick(Sender: TObject);
    begin
      MemoReceive.Clear;
    end;
    procedure TForm1.BtSaveClick(Sender: TObject);
    begin
      if SaveDialog1.Execute then MemoReceive.Lines.SaveToFile(SaveDialog1.FileName);
    end;procedure TForm1.EditMyTxtKeyPress(Sender: TObject; var Key: Char);
    begin
      if Key = #13 then BtSend.Click;
    end;
    end.
      上面的程序跟OICQ相比当然差之甚远,因为OICQ利用的是Socket5通信方式。它上线时先从服务器取回好友信息和在线状态,发送超时还会将信息先保存在服务器,等对方下次上线后再发送然后把服务器的备份删除。你可以根据前面学的概念来完善这个程序,比如说再添加一个NMUDP控件来管理在线状态,发送的信息先转换成ASCII码进行与或运行并加上一个头信息,接收方接收信息后先判断信息头正确与否,如果正确才把信息解密显示出来,这样就提高了安全保密性。
      另外,UDP协议还有一个很大的好处就是可以广播,就是说处于一个网段的都可以接收到信息而不必指定具体的IP地址。
      网段一般分A、B、C三类,1 ~126.XXX.XXX.XXX(A类网): 广播地址为XXX.255.255.255
                128 ~191.XXX.XXX.XXX(B类网): 广播地址为XXX.XXX.255.255
                192 ~254.XXX.XXX.XXX(C类网): 广播地址为XXX.XXX.XXX.255比如说三台计算机192.168.0.1 、192.168.0.10 、192.168.0.18 ,发送信息时只要指定IP地址为192.168.0.255 就可以实现广播了。下面给出一个转换IP为广播IP的函数,快拿去完善自己的OICQ吧^ - ^.function Trun_ip(S: string): string;
    var s1, s2, s3, ss, sss, Head: string;
      n, m: integer;
    begin
      sss := S;
      n := pos('.', s);
      s1 := copy(s, 1, n);
      m := length(s1);
      delete(s, 1, m);
      Head := copy(s1, 1, (length(s1) - 1));
      n := pos('.', s);
      s2 := copy(s, 1, n);
      m := length(s2);
      delete(s, 1, m);
      n := pos('.', s);
      s3 := copy(s, 1, n);
      m := length(s3);
      delete(s, 1, m);
      ss := sss;
      if strtoint(Head) in [1..126] then ss := s1 + '255.255.255'; //1~126.255.255.255 (A类网)
      if strtoint(Head) in [128..191] then ss := s1 + s2 + '255.255'; //128~191.XXX.255.255(B类网)
      if strtoint(Head) in [192..254] then ss := s1 + s2 + s3 + '255'; //192~254.XXX.XXX.255(C类网)
      Result := ss;
    end;
      

  4.   

    两种方法
    1.通过服务器中转。
    2.在代理服务器上运行一个PortMapper程序。
      

  5.   

    我也很有兴趣!大家聊聊。
    关键是要明白代理服务器是怎么工作的。我觉得QQ连接通过代理服务器上网的计算机不可能是通过IP直接对话的,而是完全通过服务器中转。我的想法是,通过一个自己的邮箱,每次向好友的邮箱发送信息,而另一个程序定时扫描自己的邮箱,遇到最新的消息就取下来,达到实时通信...(哇,这么多砖头耶!啊...我闪~~~)
      

  6.   

    通过代理服务器来做了。
    我们正在做的一个项目中有一部分功能就和它类似,只是在广网上分布的。服务器有个固定IP,每个CLIENT只和SERVER通讯,SERVER有个动态的表,每次UDP通讯成功的包里包含的有个当前IP/PORT,只要他没断线,当前的动态IP就会维持的。把它写到表中,其他的CLIENT要个它发消息,有服务器去的他的IP/PORT发过去,如果他断线先存起来,等它下次通讯穿来的新的IP/PORT的时候再发给它,并更新IP/PORT表。
      

  7.   

    8341(八三四一) 兄的想法很好啊,我一个朋友公司的代理就只能收MAIL,他就用MAIL聊天.^_^