这几天查了很多csdn上朋友给的关于点对点传输的文章,不料没有找到一个可以运行无误的:(,自己硬着头皮在前人基础上改进了一下,不料却出现了的
错误类型是:Windows socket error:由于 套接字没有连接并且(当使用一个sendto调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。(100057),on API 'send'
现在介绍一下我的源代码如下:
采用的协议:
此协议是在前人基础上,在末尾整理了一下,应该没什么问题
首先由Client发送MP_QUERY,Server接受到后发送MP_ACCEPT或MP_FEFUESE;
Client接受到MP_ACCEPT发送MP_FILEPROPERTY,Server接受到后发送MP_NEXTWILLBEDATA;
Client接受到发送MP_NEXTWILLBEDATA,Server接受到后发送MP_DATA;
Client接受到MP_DATA,发送数据块,Server接受数据块,
Server还没接受完,发送MP_NEXTWILLBEDATA,否则发送MP_END
Client接受MP_END后,向Server发送MP_END;
Server收到MP_END后,停止发送。至此,整个文件传输完毕!
错误类型是:Windows socket error:由于 套接字没有连接并且(当使用一个sendto调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。(100057),on API 'send'
现在介绍一下我的源代码如下:
采用的协议:
此协议是在前人基础上,在末尾整理了一下,应该没什么问题
首先由Client发送MP_QUERY,Server接受到后发送MP_ACCEPT或MP_FEFUESE;
Client接受到MP_ACCEPT发送MP_FILEPROPERTY,Server接受到后发送MP_NEXTWILLBEDATA;
Client接受到发送MP_NEXTWILLBEDATA,Server接受到后发送MP_DATA;
Client接受到MP_DATA,发送数据块,Server接受数据块,
Server还没接受完,发送MP_NEXTWILLBEDATA,否则发送MP_END
Client接受MP_END后,向Server发送MP_END;
Server收到MP_END后,停止发送。至此,整个文件传输完毕!
解决方案 »
- 如何将image 中的图像保存 到ini 文件中,且能读取出来?
- 哪个老大有 全国省市地区名称 数据库 ?
- 求助,在三层数据库中如何成批添加记录到服务器!
- 在有几百个Form的系统里,运行Form有没有通用的方法?
- ****** 请告诉几个MIDAS的qq群号码? ******
- 该问题在delphi中如何编程?
- 转学delphi问题之二:如何实现tdbgrid组件的单击column的标题后排序?
- 用ADO连接远程Sql 如果连接失败的话会报错,这个错误可以在哪里处理?(58分献上)
- ****** 20 分 *********** 如何得到STRINGGRID的行号
- 一个问题--bug
- 请大家表意见!ADO联接与BDE联接数据库......(欢迎参于)
- 我差劲得很,居然不知道在Delphi中如何实现几个不同类型的变量共享内存区,惭愧,那位帮我回忆一下?
控件为:edit1,edit2,btnstartServer,SaveDialog1,ss(tServerSocket)
unit server;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp, StdCtrls;
Const
MP_QUERY ='1';
MP_REFUSE ='2';
MP_ACCEPT ='3';
MP_NEXTWILLBEDATA='4';
MP_DATA ='5';
MP_ABORT ='6';
MP_OVER ='7';
MP_CHAT ='8';
MP_END ='9';
MP_FILEPROPERTY ='0';
iBYTEPERSEND=10000;
type
TForm1 = class(TForm)
btnStartServer: TButton;
SaveDialog1: TSaveDialog;
ss: TServerSocket;
Edit1: TEdit;
Edit2: TEdit;
procedure btnStartServerClick(Sender: TObject);
procedure ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ssClientConnect(Sender: TObject; Socket: TCustomWinSocket);
private
{ Private declarations }
fsRecv:TFileStream;
public
{ Public declarations }
end;var
Form1: TForm1;
bReadText:boolean;implementation{$R *.dfm}procedure TForm1.btnStartServerClick(Sender: TObject);
begin
ss.Port:=2000;
bReadText:=true;
Edit1.Text:='Server is listening';
ss.Open;end;procedure TForm1.ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
sTemp:string;
bufRecv:Pointer;
iNum:integer;
begin
if bReadText then
begin
sTemp:=Socket.ReceiveText;
case sTemp[1] of
MP_QUERY:
begin
SaveDialog1.FileName:=Copy(sTemp,2,Length(STemp));
if SaveDialog1.Execute then
begin
ss.Socket.SendText(MP_ACCEPT);
fsRecv:=TFileStream.Create(SaveDialog1.FileName,fmCreate);
edit2.Text:='你的文件的保存路径为'+SaveDialog1.FileName;
end
else
ss.Socket.SendText(MP_REFUSE+'去死');
end;
MP_FILEPROPERTY:
begin
ss.Socket.SendText(MP_NEXTWILLBEDATA);
end;
MP_NEXTWILLBEDATA:
begin
bReadText:=false;
ss.Socket.SendText(MP_DATA);
end;
MP_END:
begin
fsRecv.Free;
bReadText:=true;
end;
MP_ABORT:
begin
fsRecv.Free;
bReadText:=true;
end;
MP_CHAT:
begin
//Chat Msg
end;
end;{of case}
end
else
begin
try
GetMem(bufRecv,iBYTEPERSEND);
iNum:=Socket.ReceiveBuf(bufRecv^,iBYTEPERSEND);
fsRecv.WriteBuffer(bufRecv^,iNum);
finally
FreeMem(bufRecv);
end;{of try}
bReadText:=true;
//如果iNum=预设的发送字节,说明文件还没传送完,否则传送完毕
if iNum=iBYTEPERSEND THEN
ss.Socket.SendText(MP_NEXTWILLBEDATA)
else
ss.Socket.SendText(MP_END);
end;
end;
procedure TForm1.ssClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Edit1.Text:='Server is connected by'+Socket.RemoteAddress;
end;
end.
控件为:btnConnect,btnSendFile,cs,Edit1,Edit2,edtlPAddress,editSize,Label1,OpenDialog1
源程序:
unit client;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp;
Const
MP_QUERY ='1';
MP_REFUSE ='2';
MP_ACCEPT ='3';
MP_NEXTWILLBEDATA='4';
MP_DATA ='5';
MP_ABORT ='6';
MP_OVER ='7';
MP_CHAT ='8';
MP_END ='9';
MP_FILEPROPERTY ='0';
iBYTEPERSEND=2000;
type
TForm1 = class(TForm)
edtIPAddress: TEdit;
OpenDialog1: TOpenDialog;
cs: TClientSocket;
btnConnect: TButton;
btnSendFile: TButton;
Edit1: TEdit;
edtSize: TEdit;
Label1: TLabel;
Edit2: TEdit;
procedure btnConnectClick(Sender: TObject);
procedure btnSendFileClick(Sender: TObject);
procedure csRead(Sender: TObject; Socket: TCustomWinSocket);
procedure FormCreate(Sender: TObject);
private
fsSend:TFileStream;
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}procedure TForm1.btnConnectClick(Sender: TObject);
begin
cs.Address:=edtIPAddress.Text;
cs.Port:=2000;
cs.Open;
end;procedure TForm1.btnSendFileClick(Sender: TObject);
begin
if OpenDialog1.Execute then
Begin
cs.Socket.SendText(MP_QUERY+OpenDialog1.FileName);
end;
end;procedure TForm1.csRead(Sender: TObject; Socket: TCustomWinSocket);
var
sRecv:string;
sTemp:string;
iNum:integer;
bufSend:pointer;
begin
GetMem(bufSend,iBytePersend+1);
sRecv:=Socket.ReceiveText;
Case sRecv[1] of
MP_REFUSE:ShowMessage('Faint,be refused!');
MP_ACCEPT:begin
fsSend:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
//iBYTEPERSEND是个常量,每次发送包的大小。
edtSize.Text:=IntToStr(fsSend.Size);
edit2.text:='总共发送次数为:'+IntToStr(Trunc(fsSend.Size/iBYTEPERSEND)+1);
cs.Socket.SendText(MP_FILEPROPERTY+IntToStr(Trunc(fsSend.Size/iBYTEPERSEND)+1));
end;
MP_NEXTWILLBEDATA:begin
Socket.SendText(MP_NEXTWILLBEDATA);
end;
MP_DATA:
begin
try
GetMem(bufSend,iBYTEPERSEND+1);
//iNum是实际实的数据的大小
iNum:=fsSend.Read(bufSend^,iBYTEPERSEND);
Socket.SendBuf(bufSend^,iNum);
finally
FreeMem(bufSend,iBYTEPERSEND+1);
end;{of try}
end;
MP_END:
begin
fsSend.Free;
end;
MP_ABORT:begin
//被取消了:(
fsSend.Free;
end;
end;{of case}
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
edit1.Color:=clBlack;
edit1.Font.Color:=clLime;
end;end.
begin
....
ss.Socket.SendText(MP_ACCEPT);//这句错了!!!
//你这样控制的是侦听SOCKET,而不是建立连接那个。
把它改成
Socket.SendText(MP_ACCEPT);//这儿用的Socket是参数传进来的那个。
....
end;
另外,这个协议有些地方设计得不妥当。1,传个文件的交互过程可以用罗索来形容,效率很低。
2,TCP是提供流数据传输,它不约定两方的同步,
每次ONREAD可能会把多次发的一次读出。
每次的数据包要自己确定长度, 可以在里面加入长度字段或末尾加上分隔符。
每次的数据包要自己确定长度, 可以在里面加入长度字段或末尾加上分隔符。
您说的这句话我不好理解呀。因为我传送的是流式文件,里面的数据包当然是流式的啦,我们把ascII符一起送进去,该怎么读出来呢?
例如buf:pointer,然后用流函数读一批数据到buf里面,然后怎么加长字字段呢,还有怎么读出来?
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp, StdCtrls;
Const
MP_QUERY ='1';
MP_REFUSE ='2';
MP_ACCEPT ='3';
MP_NEXTWILLBEDATA='4';
MP_DATA ='5';
MP_ABORT ='6';
MP_OVER ='7';
MP_CHAT ='8';
MP_END ='9';
MP_FILEPROPERTY ='0';
iBYTEPERSEND=1024;
type
TForm1 = class(TForm)
btnstartServer: TButton;
Edit1: TEdit;
Edit2: TEdit;
ss: TServerSocket;
SaveDialog1: TSaveDialog;
Memo1: TMemo;
Button1: TButton;
Edit3: TEdit;
procedure btnstartServerClick(Sender: TObject);
procedure ssClientConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ssClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
fsRecv:TMemoryStream;
public
{ Public declarations }
end;var
Form1: TForm1;
bReadText:boolean;
implementation{$R *.dfm}procedure TForm1.btnstartServerClick(Sender: TObject);
begin
ss.Port:=2000;
bReadText := true;
Edit1.Text:='Server is listening';
ss.Open;
end;procedure TForm1.ssClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Edit1.Text:='Server is connected by'+Socket.RemoteAddress;
end;procedure TForm1.ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
sTemp:string;
bufRecv:Pointer;
iNum:integer;
begin
Memo1.Lines.Add('received size :' + intToStr(Socket.ReceiveLength));
if bReadText then
begin
sTemp:=Socket.ReceiveText;
case sTemp[1] of
MP_QUERY:
begin
Memo1.Lines.Add('receive MP_QUERY');
SaveDialog1.FileName:=Copy(sTemp,2,Length(STemp));
if SaveDialog1.Execute then
begin
// ss.Socket.SendText(MP_ACCEPT);
ss.Socket.Connections[0].SendText(MP_ACCEPT); fsRecv := TMemoryStream.Create;
// fsRecv:=TFileStream.Create(SaveDialog1.FileName,fmCreate);
edit2.Text := SaveDialog1.FileName;
end
else
ss.Socket.Connections[0].SendText(MP_REFUSE+'');
end;
MP_FILEPROPERTY:
begin
Memo1.Lines.Add('receive MP_FILEPROPERTY');
ss.Socket.Connections[0].SendText(MP_NEXTWILLBEDATA);
end;
MP_NEXTWILLBEDATA:
begin
Memo1.Lines.Add('receive MP_NEXTWILLBEDATA');
bReadText:=false;
ss.Socket.Connections[0].SendText(MP_DATA);
end;
MP_END:
begin
Memo1.Lines.Add('receive MP_END');
fsRecv.Free;
bReadText:=true;
end;
MP_ABORT:
begin
Memo1.Lines.Add('receive MP_ABORT');
fsRecv.Free;
bReadText:=true;
end;
MP_CHAT:
begin
Memo1.Lines.Add('receive MP_CHAT');
end;
end;{of case}
end
else
begin
try GetMem(bufRecv, iBYTEPERSEND);
iNum := Socket.ReceiveBuf(bufRecv^, iBYTEPERSEND);
fsRecv.WriteBuffer(bufRecv^, iNum);
finally
FreeMem(bufRecv);
end;{of try}
bReadText:=true; if iNum = iBYTEPERSEND THEN
begin
ss.Socket.Connections[0].SendText(MP_NEXTWILLBEDATA);
end
else
begin
fsRecv.SaveToFile(SaveDialog1.FileName);
fsRecv.Free;
ss.Socket.Connections[0].SendText(MP_END);
end;
end;
end;
procedure TForm1.ssClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
Memo1.Lines.Add('ErrorCode :' + IntToStr(ErrorCode));
ErrorCode := 0;
end;procedure TForm1.Button1Click(Sender: TObject);
var I, j: integer;
begin
J := ss.Socket.ActiveConnections;
Memo1.Lines.Add('ActiveConnectiong is ' + inttostr(j));
for I:= 0 to j- 1 do
ss.Socket.Connections[i].SendText(edit3.Text)
end;procedure TForm1.FormCreate(Sender: TObject);
begin
ss.Port:=2000;
bReadText := true;
Edit1.Text:='Server is listening';
ss.Open;
end;end.
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp;
Const
MP_QUERY ='1';
MP_REFUSE ='2';
MP_ACCEPT ='3';
MP_NEXTWILLBEDATA='4';
MP_DATA ='5';
MP_ABORT ='6';
MP_OVER ='7';
MP_CHAT ='8';
MP_END ='9';
MP_FILEPROPERTY ='0';
iBYTEPERSEND=1024;
type
TForm1 = class(TForm)
btnConnect: TButton;
btnSendFile: TButton;
Edit1: TEdit;
Edit2: TEdit;
cs: TClientSocket;
Label1: TLabel;
edtIPAddress: TEdit;
edtSize: TEdit;
OpenDialog1: TOpenDialog;
Memo1: TMemo;
procedure btnConnectClick(Sender: TObject);
procedure btnSendFileClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure csRead(Sender: TObject; Socket: TCustomWinSocket);
private
fsSend:TFileStream;
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}procedure TForm1.btnConnectClick(Sender: TObject);
begin
cs.Address := edtIPAddress.Text;
cs.Port:=2000;
cs.Open;
end;procedure TForm1.btnSendFileClick(Sender: TObject);
begin
if OpenDialog1.Execute then
Begin
cs.Socket.SendText(MP_QUERY+OpenDialog1.FileName);
end;
end;procedure TForm1.FormCreate(Sender: TObject);
begin
edit1.Color:=clBlack;
edit1.Font.Color:=clLime;
end;procedure TForm1.csRead(Sender: TObject; Socket: TCustomWinSocket);
var
sRecv:string;
// sTemp:string;
iNum:integer;
bufSend:pointer;
begin
// GetMem(bufSend,iBytePersend+1);
sRecv:=Socket.ReceiveText;
Memo1.Lines.Add('sRecv = ' + sRecv);
Case sRecv[1] of
MP_REFUSE:ShowMessage('Faint,be refused!');
MP_ACCEPT:begin
Memo1.Lines.Add('MP_ACCEPT');
fsSend:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
//iBYTEPERSEND琌熌盽秖–Ω塝癳
edtSize.Text:=IntToStr(fsSend.Size);
edit2.text:='total count:'+IntToStr(Trunc(fsSend.Size/iBYTEPERSEND)+1); cs.Socket.SendText(MP_FILEPROPERTY+IntToStr(Trunc(fsSend.Size/iBYTEPERSEND)+1));
fsSend.Seek(0, soFromBeginning);
end;
MP_NEXTWILLBEDATA:begin
Memo1.Lines.Add('MP_NEXTWILLBEDATA');
Socket.SendText(MP_NEXTWILLBEDATA);
end;
MP_DATA:
begin
Memo1.Lines.Add('MP_DATA');
try
GetMem(bufSend, iBYTEPERSEND);
iNum := fsSend.Read(bufSend^, iBYTEPERSEND);
cs.Socket.SendBuf(bufSend^, iNum);
// Socket.SendBuf(bufSend,iNum);
Memo1.Lines.Add('Send Buf finished');
finally
FreeMem(bufSend);
end;{of try}
end;
MP_END:
begin
Memo1.Lines.Add('MP_END');
fsSend.Free;
end;
MP_ABORT:begin
Memo1.Lines.Add('MP_ABORT');
fsSend.Free;
end;
end;{of case}
end;end.
虽然现在代码可以实现传送文件,但我在修改的过程中感觉很别扭,很不好的结构!!!有时间要"重构";还有,为了尽快修改,我加了些调试显示语句在代码里,你可处理掉,而且,我现在每次只传 1024 byte,太慢了,可调大点!
程序运行环境: win2K + D6
如需要,可给我留言,我将代码发给你!!!
http://expert.csdn.net/Expert/topic/2315/2315554.xml?temp=2.788723E-03
http://expert.csdn.net/Expert/topic/2315/2315549.xml?temp=.871258
另外,本贴的100分给halfdream(哈欠),就这样定了吧。
还有你的问题!我 FileStream 与 MemoryStream 的区别很微小,更不会轻易受到内存大小的限制,我说的轻易,是不清楚,如果你分配了 几百 M 的情况又会如何,但一般,特别是在winNt,2k不用考虑!!
我习惯用MemoryStream多点,回到你的代码,如果abort,或者传输失败,也会生成个不可用的文件!
还有,我相信你的这份代码是测试用的,实用的,一般都要写入线程处理阻塞问题!
而且,在 ssClientRead 中
if SaveDialog1.Execute then
showmessage
这种类似语句,我觉得都很别扭,不好的感觉, 更容易阻塞加阻塞!(即使缓冲区够大)
还有cs.socket和procedure TForm1.csRead(Sender: TObject; Socket: TCustomWinSocket);中的socket是一样的吗?
我是在校学生,因为这方面的内容老师还没讲,说不定以后也不会讲,所以只好自学,于是遇到这许多问题
2. if iNum=iBYTEPERSEND THEN
Socket.SendText(MP_NEXTWILLBEDATA)
else
Socket.SendText(MP_END);
end;
这段话是服务器接收后验证是否接收完毕的一段话,可是我用Memo1来测试到底接收多少数据时,我才发现第一次仅发送8760个字节,于是iNum<>iBYTEPERSEND,所以我每次传送的文件只有8.55k,能告诉我原因吗?
如果你有直接用 api 编写 winsock 的程序,或在VC中编写过,你可能就会理解,delphi封装处理的比较好,所以,会有点误解!
其实,在 server 端会开个端口在侦听,然后,如果有连接请求, server会开个新的端口,将新的连接重定向到 新端口,所以, 你的问题就是, ss.socket是侦听的端口, 而 CustomWinsocket那个socket是对应连接的端口!>>可是我用Memo1来测试到底接收多少数据时,我才发现第一次仅发送8760个字节,于是>>iNum<>iBYTEPERSEND
应该是socket的默认缓冲区为 8760 吧, 所以,当你发送超过 这个数,就会别分包发送!!