高手帮我看看这个源程序,解决了送200分!!

lwzlemon 2003-09-29 11:09:00
这几天查了很多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后,停止发送。至此,整个文件传输完毕!

...全文
42 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
aiirii 2003-09-30
  • 打赏
  • 举报
回复
>>1.ss.socket.sendText和TForm1.ssClientRead(Sender: TObject; Socket: >>TCustomWinSocket);中的socket有什么区别?
如果你有直接用 api 编写 winsock 的程序,或在VC中编写过,你可能就会理解,delphi封装处理的比较好,所以,会有点误解!
其实,在 server 端会开个端口在侦听,然后,如果有连接请求, server会开个新的端口,将新的连接重定向到 新端口,所以, 你的问题就是, ss.socket是侦听的端口, 而 CustomWinsocket那个socket是对应连接的端口!

>>可是我用Memo1来测试到底接收多少数据时,我才发现第一次仅发送8760个字节,于是>>iNum<>iBYTEPERSEND
应该是socket的默认缓冲区为 8760 吧, 所以,当你发送超过 这个数,就会别分包发送!!
lwzlemon 2003-09-30
  • 打赏
  • 举报
回复
所以我想再问的问题就是:如何判断接受的是最后一个数据包??有更好的方法吗?
lwzlemon 2003-09-30
  • 打赏
  • 举报
回复
1.ss.socket.sendText和TForm1.ssClientRead(Sender: TObject; Socket: TCustomWinSocket);中的socket有什么区别?
还有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,能告诉我原因吗?
aiirii 2003-09-30
  • 打赏
  • 举报
回复
我给你发了邮件了!如收不到,再给我留言!
还有你的问题!我 FileStream 与 MemoryStream 的区别很微小,更不会轻易受到内存大小的限制,我说的轻易,是不清楚,如果你分配了 几百 M 的情况又会如何,但一般,特别是在winNt,2k不用考虑!!
我习惯用MemoryStream多点,回到你的代码,如果abort,或者传输失败,也会生成个不可用的文件!
还有,我相信你的这份代码是测试用的,实用的,一般都要写入线程处理阻塞问题!
而且,在 ssClientRead 中
if SaveDialog1.Execute then
showmessage
这种类似语句,我觉得都很别扭,不好的感觉, 更容易阻塞加阻塞!(即使缓冲区够大)
aiirii 2003-09-29
  • 打赏
  • 举报
回复
重金之下必有勇夫,我也加如一份!!!看改成如何!
hiflower 2003-09-29
  • 打赏
  • 举报
回复
很想搞点分啊,可惜我不喜欢看长长的代码
ronaldli 2003-09-29
  • 打赏
  • 举报
回复
我不喜欢用别的代码,只用别人的思想
lwzlemon 2003-09-29
  • 打赏
  • 举报
回复
谢谢,如果要源程序的话,我可以打一个包发到你的邮箱
FrameSniper 2003-09-29
  • 打赏
  • 举报
回复
好,我先看看!
lwzlemon 2003-09-29
  • 打赏
  • 举报
回复
客户端的程序如下:
控件为:btnConnect,btnSendFile,cs,Edit1,Edit2,edtlPAddress,editSize,Label1,OpenDialog1
源程序:
unit client;

interface

uses
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.
lwzlemon 2003-09-29
  • 打赏
  • 举报
回复
Server端的程序如下:
控件为:edit1,edit2,btnstartServer,SaveDialog1,ss(tServerSocket)
unit server;

interface

uses
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.
lwzlemon 2003-09-29
  • 打赏
  • 举报
回复
高手就是高手,不怕麻烦帮我解决问题,我现在也实现我的承诺,开了两个100分的贴,请进来吧,aiirii(aiirii) !
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(哈欠),就这样定了吧。
aiirii 2003-09-29
  • 打赏
  • 举报
回复
我同意 halfdream(哈欠) 的意见;
虽然现在代码可以实现传送文件,但我在修改的过程中感觉很别扭,很不好的结构!!!有时间要"重构";

还有,为了尽快修改,我加了些调试显示语句在代码里,你可处理掉,而且,我现在每次只传 1024 byte,太慢了,可调大点!
程序运行环境: win2K + D6
如需要,可给我留言,我将代码发给你!!!
aiirii 2003-09-29
  • 打赏
  • 举报
回复
client:

unit clientSocketu;

interface

uses
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.


aiirii 2003-09-29
  • 打赏
  • 举报
回复
我修改了你带代码,实验过,传个27M的文件没问题,具体如下

Server:

unit Unit1;

interface

uses
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.
甜而不腻 2003-09-29
  • 打赏
  • 举报
回复
关注。
lwzlemon 2003-09-29
  • 打赏
  • 举报
回复
再次谢谢哈欠,就是他指出来的那个错误,使我不能传数据!现在虽然能传数据了,但不管我的文件多大,每次都只能传8.55K。。。谁能给我一个好的协议呀,给了后马上结贴!
lwzlemon 2003-09-29
  • 打赏
  • 举报
回复
谢谢halfdream(哈欠)呀!!
每次的数据包要自己确定长度, 可以在里面加入长度字段或末尾加上分隔符。
您说的这句话我不好理解呀。因为我传送的是流式文件,里面的数据包当然是流式的啦,我们把ascII符一起送进去,该怎么读出来呢?
例如buf:pointer,然后用流函数读一批数据到buf里面,然后怎么加长字字段呢,还有怎么读出来?
halfdream 2003-09-29
  • 打赏
  • 举报
回复
还有一些设计问题。
另外,这个协议有些地方设计得不妥当。

1,传个文件的交互过程可以用罗索来形容,效率很低。
2,TCP是提供流数据传输,它不约定两方的同步,
每次ONREAD可能会把多次发的一次读出。
每次的数据包要自己确定长度, 可以在里面加入长度字段或末尾加上分隔符。
halfdream 2003-09-29
  • 打赏
  • 举报
回复
至少找着一个。。。

procedure TForm1.ssClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
....
ss.Socket.SendText(MP_ACCEPT);//这句错了!!!
//你这样控制的是侦听SOCKET,而不是建立连接那个。
把它改成
Socket.SendText(MP_ACCEPT);//这儿用的Socket是参数传进来的那个。


....
end;
加载更多回复(3)

1,593

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧