局域网机器通过internet上实现点对点的难题(高手帮忙,分多多) (200分)

  • 主题发起人 主题发起人 phyyun
  • 开始时间 开始时间
P

phyyun

Unregistered / Unconfirmed
GUEST, unregistred user!
目前只能实现,server端也就是接收端需要有独立的IP。这样的互传我已经实现了。
我想实现象QQ那样。比如接收方在局域网内一台机器a上,在7777端口listen,通过主机b连到internet。c机也是通过internet想连a机,假设c机已经知道目标ip,是b机的,也就网关的,那么怎么映射到A机呢?
我不是用udp连接的。使用clientsocket,serversocket实现的。

如果需要建立一个独立IP的服务器,象QQ服务器一样,存放A,C机连接到服务器的ip和端口。我想a机出去,服务器应该是存放的是b机的端口吧。但具体如何实现,请帮忙提供一下思路,能够有源码更好。
200分不多。如果有思路,我会另外发贴给分。有源码就给500。
server端.pas文件如下:
×××××××××××××××××××××××××××××××××××××
unit server;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
SaveDialog1: TSaveDialog;
ProgressBar1: TProgressBar;
Button1: TButton;
Edit3: TEdit;
Label3: TLabel;
Label1: TLabel;
StatusBar1: TStatusBar;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure SaveDialog1CanClose(Sender: TObject; var CanClose: Boolean);
procedure Button1Click(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure Button2Click(Sender: TObject);
procedure ServerSocket1ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);

private
{ Private declarations }
public

end;
toptions = (load, listen, api);
tinfo = (fname, size, path);

var
Form1: TForm1;
opt, loadopt: toptions;
info: tinfo;
// m1:tmemorystream;
RecevFstream: TFileStream;
filesize: longint; //文件长度;
fullfilesize: longint;
filename, filepath: string;
suspend: boolean = false;
const
maxsize = 2048;
implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
//m1:=tmemorystream.Create; //创建流对象
{ serversocket1.port:=7777;
serversocket1.Open;
opt:=listen; }
Button1Click(nil);
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var tem: string;
len: longint; //包长度
buffer: array[0..10000] of byte; //设置接收缓冲区
buf: array[1..maxsize] of byte; //以2k大小为一个传送单位,大了可能出错
begin
if suspend then
begin
Socket.SendText('suspend');
try
RecevFstream.Free;
RecevFstream := nil;
except
end;

opt := listen; //开始重置状态
loadopt := listen;
info := size;
ProgressBar1.Position := 0;
StatusBar1.Panels[1].Text := '空闲';
suspend := false;
exit;
end;
{ tem := Socket.ReceiveText;
if tem = 'suspend' then
begin
Messagedlg('对方中止传送', mtinformation, [mbok], 0);
RecevFstream.Free;
RecevFstream := nil;
opt := listen; //开始重置状态
loadopt := listen;
info := size;
tem := '';
ProgressBar2.Position := 0;
StatusBar2.Panels[1].Text := '空闲';
exit;
end;
}
case opt of //主循环
listen: //监听状态 (1级)
begin
tem := Socket.ReceiveText;
if tem = 'send' then
begin
if messagedlg('有新文件传来,要接收吗?', mtconfirmation, [mbyes, mbno], 0) = mryes then
begin
socket.SendText('getsize'); opt := load; loadopt := listen; info := size;
end
else begin
socket.SendText('refuse');
exit;
end;
end; //改变状态
end;
load: //接收流 (1级)
begin
case loadopt of
listen: //准备接收(2级)
begin
tem := Socket.ReceiveText;
case info of //准备接收的数据 (3级)
size: begin
info := fname; //改变状态
filesize := strtoint(tem);
fullfilesize := filesize;
socket.SendText('getname');
StatusBar1.Panels[1].Text := '获取文件名';
end;
{ path: begin
info:=fname; //改变状态
filepath:=tem;
socket.SendText('getname');
end;}
fname: begin
info := size; loadopt := load; //改变状态
filename := tem;
SaveDialog1.FileName := filename;
socket.SendText('getdata');
StatusBar1.Panels[1].Text := '数据传送开始';
end;
end;
end;
load: //接收中(2级)
begin
{ len:=socket.ReceiveLength; //读出包长度
socket.ReceiveBuf(buffer,len); //接收数据包并读入缓冲区内
m1.write(buffer,len); //追加入 流M 中
if m1.Size>=filesize then //完成了流M 的传送
begin
m1.Position:=0;
m1.SaveToFile(filename);
//copyfile(pchar(filename),pchar(filepath+filename),true);
opt:=listen; //开始重置状态
loadopt:=listen;
info:=size;
tem:='';
Messagedlg('传送结束', mtinformation, [mbok], 0);
socket.SendText('loadfileok');
end; }

if RecevFstream = nil then
begin
// tem:=socket.ReceiveText;
// len:=socket.ReceiveLength;
SaveDialog1.FileName := FileName;
if not SaveDialog1.Execute then
begin
opt := listen; //开始重置状态
loadopt := listen;
info := size;
tem := '';
StatusBar1.Panels[1].Text := '空闲';
socket.SendText('refuse');
exit;
end;
try
FileName := SaveDialog1.FileName;
RecevFstream := TFileStream.Create(FileName, fmCreate);
except
Messagedlg('文件写入失败', mtinformation, [mbok], 0);
exit;
end;
ProgressBar1.Max := filesize;
ProgressBar1.Position := 0;
// Socket.SendText('getdata');
end;
try
if maxsize < filesize then
begin
len:=socket.ReceiveLength; //读出包长度
if len < 10 then
begin
Messagedlg('对方中止传送', mtinformation, [mbok], 0);
RecevFstream.Free;
RecevFstream := nil;
opt := listen; //开始重置状态
loadopt := listen;
info := size;
tem := '';
ProgressBar1.Position := 0;
StatusBar1.Panels[1].Text := '空闲';
exit;
end;
Socket.ReceiveBuf(buf, maxsize);
RecevFstream.WriteBuffer(buf, maxsize);
filesize := filesize - maxsize;
socket.SendText('getdata');
ProgressBar1.StepBy(maxsize);
StatusBar1.Panels[1].Text := '数据传送中...' + '文件总长度' + inttostr(fullfilesize) + '字节,剩余' + inttostr(filesize) + '字节';
end
else
begin
len:=socket.ReceiveLength; //读出包长度
if len < 10 then
begin
Messagedlg('对方中止传送', mtinformation, [mbok], 0);
RecevFstream.Free;
RecevFstream := nil;
opt := listen; //开始重置状态
loadopt := listen;
info := size;
tem := '';
ProgressBar1.Position := 0;
StatusBar1.Panels[1].Text := '空闲';
exit;
end;
socket.ReceiveBuf(buf, filesize);
RecevFstream.WriteBuffer(Buf, filesize);
RecevFstream.Free;
RecevFstream := nil;
ProgressBar1.Position := ProgressBar1.Max;
StatusBar1.Panels[1].Text := '数据传送完毕';
Messagedlg('传送结束', mtinformation, [mbok], 0);
socket.SendText('loadfileok');
opt := listen; //开始重置状态
loadopt := listen;
info := size;
tem := '';
ProgressBar1.Position := 0;
StatusBar1.Panels[1].Text := '空闲';
end;
except
Messagedlg('数据包写入失败', mtinformation, [mbok], 0);
RecevFstream.Free;
RecevFstream := nil;
exit;
end;
end;
end; // of case loadopt
end; //接收流over

api: //执行各种命令 (1级)
begin
end;
end; //of case opt

end;
procedure TForm1.SaveDialog1CanClose(Sender: TObject;
var CanClose: Boolean);
begin
if FileExists(SaveDialog1.FileName) then
begin
if Messagedlg('目标文件已存在,覆盖吗?', mtconfirmation, [mbyes, mbno], 0) = mryes then
DeleteFile(SaveDialog1.FileName)
else
CanClose := false;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if serversocket1.Active and (RecevFstream <> nil) then
begin
Messagedlg('上次传送还未结束', mtinformation, [mbok], 0);
exit;
end;
try
serversocket1.Close;
serversocket1.port := strtoint(edit3.text);
serversocket1.Open;
opt := listen;
StatusBar1.Panels[0].Text := '已上线';
StatusBar1.Panels[1].Text := '空闲';
except
StatusBar1.Panels[0].Text := '离线';
StatusBar1.Panels[1].Text := '';
end;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if serversocket1.Active and (RecevFstream <> nil) then
begin
if messagedlg('文件传送还未结束,确认退出吗?', mtconfirmation, [mbyes, mbno], 0) = mrno then
begin
CanClose := false;
abort;
end;
suspend := true;

end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
if serversocket1.Active and (RecevFstream <> nil) then
begin
if messagedlg('文件传送还未结束,确认中止吗?', mtconfirmation, [mbyes, mbno], 0) = mryes then
begin
suspend := true;
{ ServerSocket1.Close;
try
RecevFstream.Free;
RecevFstream:=nil;
except
end;

opt:=listen; //开始重置状态
loadopt:=listen;
info:=size;
ProgressBar1.Position:=0;
StatusBar1.Panels[1].Text:='空闲';
ServerSocket1.Open; }
end;
end;
end;

procedure TForm1.ServerSocket1ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
StatusBar1.Panels[0].Text := '已上线';
StatusBar1.Panels[1].Text := '传送失败';
errorcode := 0;
opt := listen; //开始重置状态
loadopt := listen;
info := size;
ProgressBar1.Position := 0;
if (RecevFstream <> nil) then
begin
RecevFstream.Free;
RecevFstream := nil;

end;
end;

end.
××××××××××××××××××××××××××××××
dfm文件如下:
object Form1: TForm1
Left = 289
Top = 233
Width = 430
Height = 125
Caption = 'server'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
Position = poDesktopCenter
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Label3: TLabel
Left = 16
Top = 16
Width = 18
Height = 13
Caption = 'port'
end
object Label1: TLabel
Left = 0
Top = 40
Width = 48
Height = 13
Caption = '传送进度'
end
object ProgressBar1: TProgressBar
Left = 0
Top = 56
Width = 417
Height = 16
Min = 0
Max = 100
TabOrder = 0
end
object Button1: TButton
Left = 176
Top = 8
Width = 75
Height = 25
Caption = 'listen'
TabOrder = 1
OnClick = Button1Click
end
object Edit3: TEdit
Left = 40
Top = 11
Width = 121
Height = 21
TabOrder = 2
Text = '7777'
end
object StatusBar1: TStatusBar
Left = 0
Top = 79
Width = 422
Height = 19
Panels = <
item
Width = 50
end
item
Width = 300
end>
SimplePanel = False
end
object Button2: TButton
Left = 256
Top = 8
Width = 73
Height = 25
Caption = 'stop'
TabOrder = 4
OnClick = Button2Click
end
object ServerSocket1: TServerSocket
Active = False
Port = 0
ServerType = stNonBlocking
OnClientRead = ServerSocket1ClientRead
OnClientError = ServerSocket1ClientError
Left = 152
Top = 24
end
object SaveDialog1: TSaveDialog
OnCanClose = SaveDialog1CanClose
Left = 216
Top = 24
end
end
 
client端.pas文件如下:
×××××××××××××××××××××××××××××××××××××

unit client;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ScktComp, ExtCtrls, ComCtrls;

type
TForm1 = class(TForm)
ClientSocket1: TClientSocket;
Button1: TButton;
Button2: TButton;
OpenDialog1: TOpenDialog;
Edit2: TEdit;
Label2: TLabel;
Edit3: TEdit;
Label3: TLabel;
ProgressBar1: TProgressBar;
Label1: TLabel;
StatusBar1: TStatusBar;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure Button2Click(Sender: TObject);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
//取文件名,但系统自带函数了
function getfilename(fullpath: string): string;
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure Button3Click(Sender: TObject);
procedure ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
m1: TFileStream;
filesize: longint;
fullfilesize: longint;
filepath, filename, shortname: string;
suspend: Boolean = false;
const
maxsize = 2048;
implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
StatusBar1.Panels[0].Text := '未建立连接';
StatusBar1.Panels[1].Text := '空闲';
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if clientsocket1.Active and (m1 <> nil) then
begin
Messagedlg('上次传送还未结束', mtinformation, [mbok], 0);
exit;
end;
try
clientsocket1.Close;
clientsocket1.Port := strtoint(edit3.text);
ClientSocket1.Address := edit2.text;
//clientsocket1.Host:=edit2.text;
clientsocket1.Open;
except
StatusBar1.Panels[0].Text := '不能建立连接';
StatusBar1.Panels[1].Text := '空闲';
end;
end;

procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
begin
StatusBar1.Panels[0].Text := '已建立连接';
StatusBar1.Panels[1].Text := '空闲';
end;

procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
StatusBar1.Panels[0].Text := '连接失败';
StatusBar1.Panels[1].Text := '空闲';
ProgressBar1.Position := 0;
ErrorCode := 0;
if (m1 <> nil) then
begin
m1.Free;
m1 := nil;

end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var tem: string;
begin
if not clientsocket1.Active then
begin
Messagedlg('请先连接目标机器', mtinformation, [mbok], 0);
exit;
end;
if m1 <> nil then
begin
Messagedlg('上次传送还未结束', mtinformation, [mbok], 0);
exit;
end;
ClientSocket1.close;
clientsocket1.open;
if opendialog1.Execute then
begin
try
// filename:=getfilename(OpenDialog1.FileName);
filename := ExtractFileName(OpenDialog1.FileName);
m1 := TFileStream.Create(OpenDialog1.FileName, fmOpenRead); //create 流对象 m
// m1.Position:=0;
// m1.LoadFromFile(filename);
filesize := m1.Size;
fullfilesize := m1.Size;
ProgressBar1.Max := filesize;
ProgressBar1.Position := 0;
StatusBar1.Panels[1].text := '文件总长度' + inttostr(fullfilesize) + '字节,已传送0字节';
except
Messagedlg('文件读入失败', mtinformation, [mbok], 0);
exit;
end;
end;
clientsocket1.Socket.SendText('send');
end;


procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
tem: string;
buf: array[1..maxsize] of byte; //以2k大小为一个传送单位,大了可能出错
begin
if suspend then
begin
ClientSocket1.Socket.SendText('suspend');
StatusBar1.Panels[1].Text := '空闲';
try
m1.Free;
m1 := nil;
except
end;
ProgressBar1.Position := 0;
suspend := false;
exit;
end;
tem := clientsocket1.Socket.ReceiveText;
//label1.Caption:=tem;

if tem = 'getsize' then
begin
clientsocket1.Socket.SendText(inttostr(filesize));
StatusBar1.Panels[1].Text := '传送文件大小';
end;
{if tem='getpath' then clientsocket1.Socket.SendText('d'); }
if tem = 'getname' then
begin
clientsocket1.Socket.SendText(filename);
StatusBar1.Panels[1].Text := '传送文件名';
end;
if tem = 'refuse' then
begin
Messagedlg('对方拒绝接收', mtinformation, [mbok], 0);
try
m1.Free;
m1 := nil;
except
end;
ProgressBar1.Position := 0;
StatusBar1.Panels[1].Text := '空闲';
end;
if tem = 'getdata' then
begin
{ m:=tmemorystream.Create;//create 流对象 m
m.Position:=0;
m.LoadFromFile(filename);
m.Position:=0;
} try
if maxsize < filesize then
begin
m1.ReadBuffer(buf, maxsize);
Socket.SendBuf(buf, maxsize);
filesize := filesize - maxsize;
ProgressBar1.StepBy(maxsize);
StatusBar1.Panels[1].Text := '文件传送中...' + '文件总长度' + inttostr(fullfilesize) + '字节,剩余' + inttostr(filesize) + '字节';
end
else begin
m1.ReadBuffer(buf, filesize);
Socket.SendBuf(buf, filesize);
ProgressBar1.Position := ProgressBar1.Max;
StatusBar1.Panels[1].Text := '文件传送完毕';
m1.Free;
m1 := nil;
end;
except
Messagedlg('数据包传送失败', mtinformation, [mbok], 0);
exit;
end;
end;
if tem = 'loadfileok' then
begin
Messagedlg('传送结束', mtinformation, [mbok], 0);
StatusBar1.Panels[1].Text := '空闲';
ProgressBar1.Position := 0;
end;
if tem = 'suspend' then
begin
Messagedlg('对方中止传送', mtinformation, [mbok], 0);
StatusBar1.Panels[1].Text := '空闲';
m1.Free;
m1 := nil;
ProgressBar1.Position := 0;
end;
end;

function TForm1.getfilename(fullpath: string): string;
var
p: integer;
begin
p := Pos('/', fullpath);
while (p > 0) do
begin
Delete(fullpath, 1, p);
p := Pos('/', fullpath);
end;
Result := fullpath;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if ClientSocket1.Active and (m1 <> nil) then
begin
if messagedlg('文件传送还未结束,确认退出吗?', mtconfirmation, [mbyes, mbno], 0) = mrno then
begin
CanClose := false;
abort;
end;
suspend := true;
end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
if ClientSocket1.Active and (m1 <> nil) then
begin
if messagedlg('文件传送还未结束,确认中止吗?', mtconfirmation, [mbyes, mbno], 0) = mryes then
begin
suspend := true;
end;
end;

end;

procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
if m1 <> nil then
begin
Messagedlg('与目标机器断开连接', mtinformation, [mbok], 0);
StatusBar1.Panels[1].Text := '空闲';
try
m1.Free;
m1 := nil;
except
end;
ProgressBar1.Position := 0;
end;
end;

end.

×××××××××××××××××××××××××××××××××
.dfm文件如下:
object Form1: TForm1
Left = 253
Top = 227
Width = 407
Height = 143
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
Position = poDesktopCenter
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Label2: TLabel
Left = 40
Top = 24
Width = 10
Height = 13
Caption = 'IP'
end
object Label3: TLabel
Left = 40
Top = 48
Width = 18
Height = 13
Caption = 'port'
end
object Label1: TLabel
Left = 8
Top = 64
Width = 48
Height = 13
Caption = '传送进度'
end
object Button1: TButton
Left = 192
Top = 0
Width = 75
Height = 25
Caption = 'connect'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 192
Top = 27
Width = 75
Height = 25
Caption = 'send'
TabOrder = 1
OnClick = Button2Click
end
object Edit2: TEdit
Left = 64
Top = 19
Width = 121
Height = 21
TabOrder = 2
Text = '127.0.0.1'
end
object Edit3: TEdit
Left = 64
Top = 43
Width = 121
Height = 21
TabOrder = 3
Text = '7777'
end
object ProgressBar1: TProgressBar
Left = 1
Top = 80
Width = 392
Height = 16
Min = 0
Max = 100
TabOrder = 4
end
object StatusBar1: TStatusBar
Left = 0
Top = 97
Width = 399
Height = 19
Panels = <
item
Width = 50
end
item
Width = 300
end>
SimplePanel = False
end
object Button3: TButton
Left = 192
Top = 54
Width = 73
Height = 25
Caption = 'stop'
TabOrder = 6
OnClick = Button3Click
end
object ClientSocket1: TClientSocket
Active = False
ClientType = ctNonBlocking
Port = 0
OnConnect = ClientSocket1Connect
OnDisconnect = ClientSocket1Disconnect
OnRead = ClientSocket1Read
OnError = ClientSocket1Error
Left = 112
Top = 64
end
object OpenDialog1: TOpenDialog
Left = 160
Top = 64
end
end

全部贴完了。分数多多,请大家多提宝贵意见。
可以Mail我: phyyun@ha.lss.gov.cn。
谢谢。
 
用TCP是没法在两个都在防火墙后面的程序间建立连接的,用UDP吧,即使是UDP也可能受到防火墙的屏蔽,可能会要服务器中转
 
看了你的一整个程序,如果现在在两个内网里面传输文件,用TCP/IP是不可能的,如果你不用UDP的话,那么只有通过服务器中转,也就是这样:

//需要系统服务器S安装数据库服务器。

1。A用户通过用户名或者编号登陆系统服务器S。
2。B用户通过用户名或者编号登陆系统服务器S。
3。系统服务器S分别记录下用户A和用户B的编号和IP地址。
4。当A用户提出要向B用户发送数据时,向系统服务器S发送要发送文件的消息,并向系统服务器S提供文件名称,文件大小,文件说明或者备注。
5。系统服务器S接受到来自A用户的消息后,发送消息给B用户(因为这个时候B用户是连接在服务器上的,通过系统服务器S可以直接给用户B发送消息),通知B用户有来自A用户的发送请求,要求B用户确认是否接受该文件。同时将A用户的文件消息(文件名称,文件大小,文件说明或者备注)发送给B用户。
6。如果B用户这时突然离线或者无法连接,那么系统服务器S在向用户B发送消息没有收到来自B用户的应答后,通知A用户无法连接B用户,选择 1 :放弃文件传送 选择 2 :通过服务器发送【以后当用户B连接上系统服务器S时可以提供A用户以前的文件,实现离线传输文件的方法,可以按照会员资格开通该服务】,当B用户在以后连线时,那么系统服务器S将向B用户提出有来自A用户的文件传送请求(通过服务器暂存),如果B用户拒绝接受,那么系统服务器S删除暂时存储在系统服务器S上的文件,并且通知A用户 -- (如果A用户当时离线,那么消息放入消息队列,B用户拒绝接受。)
7。如果系统服务器S在向用户B发送消息后收到来自B用户的应答,那么如果B用户不愿意接受,那么系统服务器S通知A用户,B用户放弃文件传输请求。如果B用户同意文件传输,那么B用户发送文件参数给系统服务器S(包括文件存放路径,文件存放的名称等),然后数据开始传输AB用户间的数据经过系统服务器S中转,中转过程为:A用户发送的数据流由系统服务器S接收,同时系统服务器S开通到B用户的文件传输过程,将来自A用户的文件发送给B用户,系统服务器S转发完成。
8。通过系统服务器S转发过程中,如果A用户放弃传输,那么系统服务器S及时的通知B用户,A用户放弃传输。如果是B用户放弃接收,那么系统服务器S及时通知A用户,B用户放弃接受。
9。系统服务器S中转传输过程的控制,方法有2种:1:完全接收来自A用户的数据文件,如果传输过程中A用户放弃传输,那么系统服务器S在接收到A用户的放弃传输指令后,自动删除临时文件,并且通知B用户,A用户放弃传输。如果A用户在传送中发生错误,那么系统服务器S通知A用户重发。2:同步传输,系统服务器S做为缓冲服务器,接收一点发送一点,如果A或者B用户放弃时也是通知对方。如果任何一方用户在传送中发生错误,那么系统服务器S通知A用户重发B用户重新接收。是否传输成功可以按照最前面的A用户文件信息做为标准。

同步传输的好处是服务器不需要暂存空间,如果在大用户量的情况下,暂存空间将需要相当大,因为如果有10000人同步传输,服务器硬盘空间负担太大。不好的地方是服务完成离线传输文件,所以我前面提出离线传输开通会员功能,只有缴费会员可以享用。

如果采用离线传输过程中,在系统服务器S和用户B之间发生文件传送错误,那么服务器可以开启重发请求,要求B用户确认。B用户确认后开始重发。

希望大家可以探讨一下这个问题,我的论坛:www.lanboy.net 网络小子的热被窝
 
无法保存数据: 1。

同步传输的好处是服务器不需要暂存空间,如果在大用户量的情况下,暂存空间将需要相当大,因为如果有10000人要求离线传输,服务器硬盘空间负担太大。不好的地方是服务完成离线传输文件,如果有一方用户不在线,那么就无法发送文件,只有通过别的方法比如电子邮件的方法,现在的jQQ就是这样的,无法完成离线文件传输服务,所以我前面提出离线传输开通会员功能,只有缴费会员可以享用。

-------------------------------------------------------------------------------------------------------

前面的字打错了,哈哈。
 
非常感谢天使屁屁猪详细的回答。也谢谢yangying_2000回帖。
我做这个程序的出发点是,我在公司上班,经常需要和同学互传东西,想自己做个玩玩,DELPHI主要是做数据库方面的。我查了很资料,在delphibbs也搜索过,对于P2P这种跨网互传,均没有一个详细的解决方案。
很多都是通过服务器中转,也就是你说的这个思路,当然肯定没你详细。
网络通信这方面,我刚刚学,从上面我写的这个程序也能看出来。我用的是DELPHI5,《delphi5开发人员指南》上居然没有SOCKET编程这段,晕倒。
对于你说的,我有几点疑问,希望能够继续得到指点:
1、第9点。如果A用户在传送中发生错误,那么系统服务器S通知A用户重发。那么如何得知,传送发生错误呢?
2、对于通过服务器中转,转发数据。是不是B用户连到服务器,服务器就可以给B用户发送数据了。那么服务器怎么确定B用户的IP和端口呢?而且B用户是在局域网内通过网关连服务器的?可能只需要SOCKETCONNECTION一个方法或属性,但我确实不知道。
3、接收过程如何在发生传送错误后通知对方重新传?我这个程序,在接收数据时,如果传送方也就是CLIENT端想中止传送时,发送一个SUSPEND给SERVER端,然后SERVER端读取时发生了点麻烦,
如果我通过tem:=socket.ReceiveText读取,然后和‘SUSPEND’比较,那么如果此时收到的是正确文件数据的话,就会导致接收的文件丢失,因为再Socket.ReceiveBuf(buf, maxsize)就收不到了。我只能通过ReceiveLength来判断,呵呵,这方法很无奈。因为我没有定义数据包的格式,原先没考虑过要发消息聊天。
4、象第3点,系统服务器S分别记录下用户A和用户B的编号和IP地址。这个怎么实现?是否要求要自定义发送数据包的格式呢?比如在包头加入FROMIP等等,能否知道用户A的端口?
关于自定义格式,有没有一个例子或源码,用SOCKETCONNECTION和CLIENTCONNECTION做的,让我学习学习。这样更快一点。UDP在局域网内传送消息,我知道一点。但是TCP/IP的不清楚。
我做这个程序前没看过任何资料,完全是靠看帮助和网上看到的一点帖子写的,呵呵,基础很不扎实。不过,对于网络通信原理,还是了解一点的。
5、文件续传是如何实现的?我是这样想的,第二次写入文件时,读取已经存在的文件长度,然后把FILESTREAM的起始POSITION赋为该长度,再继续写,至于发送方也是一样。但是对于已存在文件长度的读取,我自己想了两种方法,不知有没有更好的。一是先TFileStream.Create(FileName, fmOpenRead);然后获取FILESIZE。一是在上次文件的最后,想中止接收时,在最后写入一个固定大小的文件长度,下次续传时先读出来,再删掉。但是这样有个问题,就是如果发生一次意外中断,就不行了。
6、象现在的网络下载工具FLASHGET都有多线程功能嘛。可以自由改变线程,对于这个,需要自己编写一个THREAD类实现,还是SOCKETCONNECTION控件自带了这种功能,或者哪个VCL类封装好了。。多线程时的续传又怎么实现呢?
7、多客户端连接时,怎么办?多一个客户端,服务器端就要多开一个线程,是不是?象我现在程序里文件信息以及一些条件控制都用的全局变量,很无奈。请问如何实现,我看了些资料,只有一些朦胧的概念。我对于一个SOCKETCONNECTION有多个SOCKET,以及如何找到想要的SOCKET还不太了解。
8、如果采用UDP传送,没有服务器,是否只要发到目标机器的网关和自定义的端口,网关就会自动转发到目标机器上去?
不好意思,问题很多。主要包括,消息格式的定义,多线程续传,多客户端等实现。我想通过做这个程序都锻炼一下,我相信高手都是很多的,毕竟这些都是常见问题。
 
1、第9点。如果A用户在传送中发生错误,那么系统服务器S通知A用户重发。那么如何得知,传送发生错误呢?

解答如下:第一 TCP/IP是非常安全和稳定的通讯方式,如果任何一方发生问题,对方都可以得到已经断开连接的消息,这类消息有如下几种:1.ClientSocket.Socket.Connented
2.连接超时。3。其他的断开连接的消息(比如再次发送消息时返回错误异常) 第二 你可以判断文件已经接收的大小是否和一开始A用户提供的文件大小相同。

2、对于通过服务器中转,转发数据。是不是B用户连到服务器,服务器就可以给B用户发送数据了。那么服务器怎么确定B用户的IP和端口呢?而且B用户是在局域网内通过网关连服务器的?可能只需要SOCKETCONNECTION一个方法或属性,但我确实不知道。

解答如下:其实不需要知道B用户的IP地址和端口号码的,A用户也是一样,我们也不需要关心他的IP地址和端口。因为当有用户和服务器连接时,他先给服务器发送请求消息,服务器应答后,就可以发送消息了 ServerSocket.Socket.connections[0].sendtext(Msg);,不需要知道对方的IP地址的,就算知道了,也只能是网关服务器的IP地址。

3、接收过程如何在发生传送错误后通知对方重新传?我这个程序,在接收数据时,如果传送方也就是CLIENT端想中止传送时,发送一个SUSPEND给SERVER端,然后SERVER端读取时发生了点麻烦,
如果我通过tem:=socket.ReceiveText读取,然后和‘SUSPEND’比较,那么如果此时收到的是正确文件数据的话,就会导致接收的文件丢失,因为再Socket.ReceiveBuf(buf, maxsize)就收不到了。我只能通过ReceiveLength来判断,呵呵,这方法很无奈。因为我没有定义数据包的格式,原先没考虑过要发消息聊天。

解答如下:文件在正确传输的过程中,是不需要检测消息的,因为可靠的IP连接,而且最好在传输文件是开启多线程传输,通过另外的一个本地TCP/IP端口和系统服务器建立连接,第一可以在传输文件时可以继续聊天。第二可以随时中断文件的传输而不影响聊天。所以最好在你的消息接收模块中,做好不同消息类型的封装。
 
4、象第3点,系统服务器S分别记录下用户A和用户B的编号和IP地址。这个怎么实现?是否要求要自定义发送数据包的格式呢?比如在包头加入FROMIP等等,能否知道用户A的端口?
关于自定义格式,有没有一个例子或源码,用SOCKETCONNECTION和CLIENTCONNECTION做的,让我学习学习。这样更快一点。UDP在局域网内传送消息,我知道一点。但是TCP/IP的不清楚。
我做这个程序前没看过任何资料,完全是靠看帮助和网上看到的一点帖子写的,呵呵,基础很不扎实。不过,对于网络通信原理,还是了解一点的。

解答如下:关于客户端的IP地址和端口号码可以使用:Socket.RemoteAddress 和 Socket.RemotePort 分别了解IP地址和端口号码,但是这个其实是网关服务器的号码,对我们来说是没有用的,但是如果他们是直接在网络上的话,可以通过这些已经知道的通讯资料,让他们自己通讯,而且还可以知道他们在哪里,并提供像QQ一样的会员登陆窗口,告知用户上次登陆的IP地址和时间等消息。对于服务器中转的这种方式来说,知道IP地址是没有什么用的。
 
5、文件续传是如何实现的?我是这样想的,第二次写入文件时,读取已经存在的文件长度,然后把FILESTREAM的起始POSITION赋为该长度,再继续写,至于发送方也是一样。但是对于已存在文件长度的读取,我自己想了两种方法,不知有没有更好的。一是先TFileStream.Create(FileName, fmOpenRead);然后获取FILESIZE。一是在上次文件的最后,想中止接收时,在最后写入一个固定大小的文件长度,下次续传时先读出来,再删掉。但是这样有个问题,就是如果发生一次意外中断,就不行了。

解答如下:第一服务器可以在接收传输的过程中,先判断现在的已经接收的文件的大小,然后根据最初的时候,A用户提供的文件名和文件大小信息就可以知道上次的文件传输状况,在续传时,需要再次对比上次A用户的文件名和文件大小是否和服务器数据库存在的相同。如果相同那么可以续传,如果不相同那么传了也是错误的。每次续传以前都可以判断文件的长度,这个函数可以有几种写法,你可以看一下。
 
6、象现在的网络下载工具FLASHGET都有多线程功能嘛。可以自由改变线程,对于这个,需要自己编写一个THREAD类实现,还是SOCKETCONNECTION控件自带了这种功能,或者哪个VCL类封装好了。。多线程时的续传又怎么实现呢?

解答如下:多线程通讯,本来在控件里面就是支持的,但是主要的问题是如何控制,比如A用户的文件通过了五个线程传输,那么这些线程就要分别在文件不同的起始位置开始读取文件,同时开始传输,然后服务器也是通过多线程接收,然后全部完成进行封装。但是问题是传输到了一半的时候出现了错误,下次开始续传的时候就必须了解到上次文件被分割的块数和分别的起始地点和传输大小,这样虽然还是可以实现的,但是成本太高,所以MSN和QQ都还没有开通多线程续传,虽然他们都支持续传,但是对于多线程的续传,服务器的压力要大好多,记录的信息也比较大需要记录线程数量,线程开始地点和线程已经传输的数据量,开销大,而且传输完成以后还要进行封装,所以成本太高,如果10000人同时传输的话,服务器的线程开销太大了。做倒是可以做的,你考虑一下。
 
7、多客户端连接时,怎么办?多一个客户端,服务器端就要多开一个线程,是不是?象我现在程序里文件信息以及一些条件控制都用的全局变量,很无奈。请问如何实现,我看了些资料,只有一些朦胧的概念。我对于一个SOCKETCONNECTION有多个SOCKET,以及如何找到想要的SOCKET还不太了解。

解答如下:第一这个是你的程序设计思路的问题,1:对于公共相应模块数据接收功能单独写一个类这样可以随着线程调用,如果设计在一个模块里面,那么你支持不了几个人服务器就已经无法相应了。
 
8、如果采用UDP传送,没有服务器,是否只要发到目标机器的网关和自定义的端口,网关就会自动转发到目标机器上去?
不好意思,问题很多。主要包括,消息格式的定义,多线程续传,多客户端等实现。我想通过做这个程序都锻炼一下,我相信高手都是很多的,毕竟这些都是常见问题。

解答如下:那是不可能的。
 
这个可不是简单的问题,如果做好了,你就是高手了,这里面你一定要对公用模块单独写类,第一封装的比较好,第二可以对各自不同的线程提供不同的相应,第三各个用户之间互不干扰,如果你用一整个接收模块来处理所有的消息,那么更本反映不过来,而且如果你这里有一个用户发生通讯错误,那么就会影响到其他人,所有人全部传输中断。
 
楼主
QQ是36525991的朋友说他可以解决这样的问题。请和他联系。如果他给出原理请贴
 
天使屁屁猪,我是个新手,我也想做个这样的东东,不知如何下手。对下面的这段话,我有点疑问:点对点通信不知道对方的IP也能进行的话,那是不是如果有几个接收端(在同一网关里)都在监听同一个端口(8000)的话,则只要发送方通过8000口发送数据,几个接收端都同时收到啊?通过服务器中转,需要在服务器端做哪些设置或装什么?
2、对于通过服务器中转,转发数据。是不是B用户连到服务器,服务器就可以给B用户发送数据了。那么服务器怎么确定B用户的IP和端口呢?而且B用户是在局域网内通过网关连服务器的?可能只需要SOCKETCONNECTION一个方法或属性,但我确实不知道。

解答如下:其实不需要知道B用户的IP地址和端口号码的,A用户也是一样,我们也不需要关心他的IP地址和端口。因为当有用户和服务器连接时,他先给服务器发送请求消息,服务器应答后,就可以发送消息了 ServerSocket.Socket.connections[0].sendtext(Msg);,不需要知道对方的IP地址的,就算知道了,也只能是网关服务器的IP地址。

 
再次感谢天使屁屁猪。
能否再具体解释一下以下问题:
1、连接超时怎么判断?再次发送消息时返回错误异常怎么判断,要用TRY EXCEPT END吗?
2、系统服务器S分别记录下用户A和用户B的编号和IP地址。如你所说,其实记下IP是没有用的,应该记下ServerSocket.Socket.connections.sendtext(Msg)的I的值,对吧?请问用哪个方法取得i值。
3、多线程时的续传,这个我还是想实现一下,占用资源先不管,实现就行,反正我这只是自己用的。固定的多线程可以实现,就是你的这个思路,我也想过。难点在怎么动态修改线程,比如增减线程,然后再续传。
4、我原先是想建个服务器端,收集好用户A,B的相关信息,比如IP和连出端口,然后让A和B互联。服务器不需要做转发的工作,这样是不是不行?我试过,不行,只是想确认一下,因为可能是我这边网关的问题。
5、关于自定义消息格式,有没有一个例子或源码,用SOCKETCONNECTION和CLIENTCONNECTION做的?
我准备重写程序,不过这周开始比较忙,我只能抽空写写,而且很多都不熟,还要查资料。天使屁屁猪,你有没有这方面的资料,DELPHI的网络通信方面的,或者什么网站有,推荐一下,谢谢。
to大力水牛:只要客户端和服务器建立SOCKET连接,那么就不用管在什么端口了。
 
TO:phyyun
谢谢你。
文件通过服务器中转,需要在服务器端装个自动转发的程序吗?
你有没有源码,能给我一份吗?
 
源码我当然没,有我还在这提问吗?
等我最后这些问题回答了,结帖开始写了。
 
后退
顶部