急,有关socket的问题???(100分)

  • 主题发起人 主题发起人 tp168
  • 开始时间 开始时间
T

tp168

Unregistered / Unconfirmed
GUEST, unregistred user!
用tclientsocket和tserversocket的ctnonblocking方式实现文件从client->server
的传输的问题,我的设计思路是client端每发一个包,server端都要给一个响应是
否成功,收到成功响应后才继续发送后续包,否则尝试发送三次,但是出现以下问
题不知如何解决,请各位大虾帮忙一下,多谢了!

const _bbs='send_file_test11'
每个包结构为16位包标识(固定为_bbs)+4位(如下)+包数据(长度不定)

client端发的包(其中vctosave,vdsaveinfo,vcfirstbuf,vclastbuf为必发的包)
vctosave:请求server端上传文件(无包数据)
vcsaveinfo:发送文件信息-》vcsaveinfo+包数据(文件名称|文件时间|文件大小)
vcfirstbuf:
头一个包-》包数据长度:如果文件大小<datalen,为文件大小,
如果文件大小>=datalen,为datalen长的文件流(datalen大小可配置)
vccommonbuf:中间的包-》包数据为datalen长的文件流
vclastbuf:最后的包-》包数据长度为剩余文件流大小

server端发的包
vsready:已准备好
vssaveinfook:存文件信息成功
vsfirstbufok:存第一个包成功
vscommonbufok:存中间包成功
vssaveok:上传文件成功

问题在于:
1。client端发送一个包后,怎样才能判断server端是否响应超时,
以便尝试再发送;
2。client端发送一个包后,按造delphi的onread的原理,可能会激发
server端的多个onread事件,我怎么写server端的读数据的过程;
3。server端也应具有判断client端是否响应超时的功能。

我的程序大概如下:

unit client;
tform1.button1onclick(...)
begin
client1.open;
end;
tform1.client1connect(...)
begin
socket.sendbuf(_bbs+vcsave,20);
end;
tform1.client1tread(...)
var
getbuf,sendbuf:array [1..datalen+20] of char;
len,sendsize:integer;
begin
len:=socket.receivebuf(getbuf,datalen+20);
sendsize:=0;
if len>=20 then
begin
处理包(存文件流,处理出响应包sendbuf,sendsize);
end;
if sendsize>0 then socket.sendbuf(sendbuf,sendsize);
end;

unit server;
tform1.server1read(...)
var
getbuf,sendbuf:array [1..datalen+20] of char;
len,sendsize:integer;
begin
len:=socket.receivebuf(getbuf,datalen+20);
sendsize:=0;
if (len>=20)and(copy(getbuf,1,20)=_bbs) then
//问题就出在这里,如果client端发了一个包,server端激发了三个onread
事件(假设),这是不是判断len>=20不是行不通了,因为其实包还没收完整,
要等待三个onread事件后才能处理,但是却怎么知道还没后续onread事件,
看了论坛中的相关资料,找到说可以用二级缓存的方法实现,由于我的数据
包不定长,只能通过加包头和包尾解决,但是应该如何编码,如果包内容里
也包括该标识又该如何解决呢?
begin
处理包(处理出sendbuf,sendsize);
end;
if sendsize>0 then socket.sendbuf(sendbuf,sendsize);
end;
 
各位大虾,帮忙一下吧!
 
如果Server端连接不算太多,Client实例数在60个以下,建议使用stThreadBlocking方式,
这样会使得编程更为简单,你不需要发送响应包验证是否成功,这个TCPIP可以保证。
 
randolph,首先谢谢你关注我的问题,
由于我的server端连接可能会超过60,所以我考虑用ctnonblocking方式,
发送响应包验证成功是为了实现断点续传和防止丢包现象发生
(网络可能不太稳定且要求正确地上传文件(该数据文件比较重要))
 
将你的包结构改一下:
每个包结构为16位包标识(固定为_bbs)+4位(保存整个包的长度)+4位(如下)+包数据(长度不定)

在server端分两步接收:
1。接收前24位(这24位可以再简化一下),并取出包长度。
2。设置一个动态数组的长度为包长度-24,一次接收所有的数据。
试试看,应该没问题。
 
那定义一个record是不是也可以???
type
myrec=record
bbs:array [0..15] of char;
packinfo:integer;(4位包信息标识,就是vcsvae,vsready...)
packsize:integer;(包长度,包括bbs+packinfo+packszie+packnr的长度)
packnr:array of char;(包内容)
end;
发送该记录不就行了
 
其实意思是一样的,但使用记录发送可能会有一点问题:
D5发送时会将纪录的长度自动凑为8的整数倍。用D6需要将
Project->Options->Compiler->Code generation 中的Record field alignment设为1
(这一点我也是前不久刚刚知道,现学现卖吧[:D])
所以我想用流的方式会好一点。
 
你好梅梅,你上面说“设置一个动态数组的长度为包长度-24,一次接收所有的数据”,
但是client端每发一次包,server端不止激发一次onread事件,该怎么办???
 
如果你在一个onread事件中把数据端完了,好像就不会在激发onread了。
 
在onread事件里怎么能把数据读完(如果数据还没到socket缓冲区呢???)
 
D5发送时会将纪录的长度自动凑为8的整数倍??
简单得很:
type
myrec = packed record
...
end;

其实触发多少个onread不是问题,参考解决办法:

//示意代码,示情况更改为正确代码
var
CommData; //全局变量,应在使用前分配足够内存
procedure OnRead;
begin
CommData := CommData + NewData;
寻找包开始标志,删除包开始标志之前无效数据
寻找包结束标志,无则返回
PacketData := 包开始标志...包结束标志;
Delete(CommData, 包开始标志...包结束标志);
验证PacketData,发送回包
处理PacketData
end;

在OnRead把数据读完的方法也可以,包头应包含包长度
while true
try
read NewData
PacketData := PacketData + NewData;
if Length(PacketData) = PacketData.PacketSize then Break;
Sleep(1);
except
CloseSocket;
Break;
end;
 
你好,Randolph:
疑问???(//后)
第一种方法:
procedure OnRead
begin
CommData := CommData + NewData;
寻找包开始标志,删除包开始标志之前无效数据
//删除包开始标志之前无效数据,这无效数据是什么情况下回产生?
寻找包结束标志,无则返回
PacketData := 包开始标志...包结束标志;
//包开始标志和结束标志该怎么编码,如果包内容中也包含该标志怎么办?
//因为我的上传功能要支持任何类型的文件,包括文本和二进制文件
Delete(CommData, 包开始标志...包结束标志);
验证PacketData,发送回包
处理PacketData
end;

第二种方法:

在OnRead把数据读完的方法也可以,包头应包含包长度
while true
try
read NewData
PacketData := PacketData + NewData;
if Length(PacketData) = PacketData.PacketSize then Break;
Sleep(1);
except
CloseSocket;
Break;
end;
//1。如果在传送过程发生丢包现象,那接收整个文件流时不就错位了???
//2。假设如果client端发一个包后,server端激发了3个onread事件,而server
端在第一个onread事件中就已经读取了所有数据,那后两个onread事件不就空处理了???
 
包开始标志和结束标志可以任意编码(如是一个DWORD),实际数据需要转换避免出现这
些标志,你所说的流格式才要这样。

第二种方法参考包格式

TDataPacket = packed record
Signature: Word; //$EEEE,可以是你认为合适的数值
DataID: Integer; //数据ID,或者称为文件ID
PacketCount: Integer; //包数量,定义所发送的数据分拆为多少个数据包
PacketNo: Integer; //本包顺序号,每个数据包分配一个从递增的唯一序号
DataSize: Integer; //本包数据实际长度,不包含包头,建议一个数据包不要超过1K
Data: array[0..999] of Char; //存放数据,实际长度在 DataSize
end;

//1。如果在传送过程发生丢包现象(实际接受到的包数量<>PacketCount),检查PacketNo, 请求发送漏掉的包
//2。假设如果client端发一个包后,server端激发了3个onread事件,而server
端在第一个onread事件中就已经读取了所有数据,那后两个onread事件不就空处理了???
在OnRead检查Socket.ReceiveLength,无数据即返回,空处理有问题么?
 
在静海写的一个客户服务器端的程序中,有这样的一个聊天的例子(我还没仔细看),
如果你要,我发给你.我的e_mail:jbas@163.com
 
你好,jbas,my e-mail is tp188@163.com
 
给你几点建议:
1:server端应尽量简单,这样可以减少出错的几率。
2:server端可以用控件,但client端最好自己做,因为大部分错误控制在client端。
3.在数据的首部加上整个数据的长度。
4.双方收到数据后,必须有回应信息给对方。
在以上四点的基础上,试着解答一下你的问题。
>>>>>client端发送一个包后,怎样才能判断server端是否响应超时,以便尝试再发送。
client端发送一个包后,应立即进入接收侦听(如使用WSAWaitForMultipleEvents函数),
如果函数超时返回则可以断定server端出了问题。
>>>>>client端发送一个包后,按造delphi的onread的原理,可能会激发server端的多个onread
>>>>>事件,我怎么写server端的读数据的过程。
按照建议3,你可以在把收到的数据累积,达到规定长度后触发自定义事件。
>>>>>3。server端也应具有判断client端是否响应超时的功能。
应该尽量避免这样做,server端永远是被动的,client端应根据server端行为来判断
server端的情况。





 
很奇怪你的做法,采用TCP连接,居然还要自己响应?那要TCP做什么?直接用IP协议好了
首先,客户端发送数据时如果传输中断,导致服务器端没收到这个包或者收到部分包,客户
端都可以知道的,这是TCP的基本原理,而中间的数据位错误可以由更底层的协议通过CRC等
校验发觉而排除,所以,TCP对数据传输是安全的。
其次,你客户端发送的数据长度是不定的,这就会导致多分组,多分组也没什么,但你自己
响应的话会造成混乱

题外话,有一个感觉,都被封装惯坏了
 
你好,mywyn
能不能写一份例子,谢谢
 
dd说得很对!
你们想干涉正常的TCP/IP安全机制?嘿嘿...不用汇编恐怕是办不到了!
 
后退
顶部