大家都知道,delphi中的TClientSocket和TServerSocket组件传输比较大的文件或者数据时很麻烦,需要自己对数据封包,客户端和服务器端一遍一遍的应答。最关键的是,这样的代码根本没有可重用性,每次都需要重新写大段的代码,甚至客户端与服务器端都要重复写!我考虑了一种方案,将发送和读取封装到函数中,这样可以达到一定程度的复用性。
function SendData(Socket: TCustomWinSocket; const Data,Re:string) :boolean;
//以length=**,re=**,data=*的格式发送字符串
begin
Result := true;
try
Socket.SendText('length=' + IntToStr(Length(Data)) +
',re=' + Re +
',data=' + Data);
except
Result := false;
end;
end;function ReceiveData(Socket: TCustomWinSocket; var Data,DataRe:string;
var DataLength:integer) :boolean;
//收取字符串
var
s:string;
sl:TStringList;
begin
Result := false; try
s := Socket.ReceiveText;
if (Copy(s,1,7) ='length=') then
begin
sl := TStringList.Create;
sl.CommaText := s;
DataLength := StrToIntDef(sl.Values['length'],0);
DataRe := sl.Values['re'];
Data := sl.Values['data'];
sl.Free;
end
else if (DataLength>0) then
Data := Data + s
else {if (DataLength<=0) }
Data := s; if (DataLength<=0) OR (Length(Data) >= DataLength) then
begin
Result := true;
DataLength := 0;
//仅当数据收取完成时,才进行后面的处理
end;
except
end;
end;var
Data, DataRe;string;
DataLength:integer;//////使用方法//////////
procedure TForm1.Button1Click(Sender: TObject);
begin
SendData(ClientSocket1.Socket,a_big_string,str_re);
end;procedure TForm1.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
if(ReceiveData(Socket, Data, DataRe, DataLength)) then
begin
//这儿对数据进行处理。
//如果多次触发了该事件,只有最后一次事件才进行处理。
Memo1.Lines.Add(DataRe + ':' + Data);
end;
end;
function SendData(Socket: TCustomWinSocket; const Data,Re:string) :boolean;
//以length=**,re=**,data=*的格式发送字符串
begin
Result := true;
try
Socket.SendText('length=' + IntToStr(Length(Data)) +
',re=' + Re +
',data=' + Data);
except
Result := false;
end;
end;function ReceiveData(Socket: TCustomWinSocket; var Data,DataRe:string;
var DataLength:integer) :boolean;
//收取字符串
var
s:string;
sl:TStringList;
begin
Result := false; try
s := Socket.ReceiveText;
if (Copy(s,1,7) ='length=') then
begin
sl := TStringList.Create;
sl.CommaText := s;
DataLength := StrToIntDef(sl.Values['length'],0);
DataRe := sl.Values['re'];
Data := sl.Values['data'];
sl.Free;
end
else if (DataLength>0) then
Data := Data + s
else {if (DataLength<=0) }
Data := s; if (DataLength<=0) OR (Length(Data) >= DataLength) then
begin
Result := true;
DataLength := 0;
//仅当数据收取完成时,才进行后面的处理
end;
except
end;
end;var
Data, DataRe;string;
DataLength:integer;//////使用方法//////////
procedure TForm1.Button1Click(Sender: TObject);
begin
SendData(ClientSocket1.Socket,a_big_string,str_re);
end;procedure TForm1.ServerSocketClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
if(ReceiveData(Socket, Data, DataRe, DataLength)) then
begin
//这儿对数据进行处理。
//如果多次触发了该事件,只有最后一次事件才进行处理。
Memo1.Lines.Add(DataRe + ':' + Data);
end;
end;
如果有多个数据类型,我会先压缩,再用Base64编码为字符串格式。
Socket分异步和阻塞两种情况,两种情况下,应该都不一样,所以得分开。而封装成SendData, RecvData,感觉没必要,SendBuf, ReceiveBuf已经是将数据Send, Receive了,再封装,俺感觉很别扭。如果将ClientSocket, ServerSocket再封装一次,变成专门传输自定义格式的数据,那已经不是ClientSocket一层了,就像是你写一个HTTP传输类,组件了。比如使用的HTTP组件,它是使用Socket进行数据传输,但传输的内容已经变成HTTP协议里的内容,而组件外,是使用HTTP这个组件类,而非Socket类了。
我就是要这样的效果啊
至少在整个公司的工程中可以共享这一段代码了。
如果按照通常的做法,
同样需要定义客户端与服务器端的指令,来回应答。同样是自定义格式的,但是每次编码时都需要重新写一次。
的确是已经封装好了,但是有一个缺点,就是当发送大数据量时,有可能接收方会触发多次OnRead事件,仍然需要手工拼装。
还不如写个类,封装好数据传输这块,然后导出一个函数:
function SendCommand(Command: string; WaitFor: Boolean): string;将所有的命令全部在这里进行控制,其它人所关心的只是返回值是否正确就行了,
不然感觉调用的时候还要有个ClientSocket组件,还得控制它的事件,这做什么那做什么不像封装了如果俺来写的话,首先写个类,写好C/S的传输规则,然后在DataModule或App控制类里创建这个类实例,然后用它来导来对应的函数,其它人就使用这些函数进行数据传输。
如果以后升级,扩展的话,只要去改这个类,其它则不需要去改。按你的思路,N个单元需要用到传输的话,需要new N个ClientSocket组件,每个人写的代码可能不同,也许为了控制的问题,但以后维护起来就麻烦点了。
>>的确是已经封装好了,但是有一个缺点,就是当发送大数据量时,有可能接收方会触发多次
>>OnRead事件,仍然需要手工拼装。你使用异步方式是会这样,俺比较少用它,如果客户量不是成欠上万的话,俺一般使用多线程来做的。阻塞+线程的用处就是一直接收数据,它是一个线程里交互全部的会话,而不是使用消息(OnRead事件),所以就不存在这种情况。
我觉得这样太麻烦,所以使用ADODataSet.SaveToFile
一次性发送一个表更方便。
楼主的用意是规范公司的套节字使用话?还是说一下的。
但要是说用只有两个MARK的数据包来谈封装还是太简单了,而且还要调用者提供套节字对象,代码中亦未考虑用户发送内容中是否含有标志位的问题。
如:SendData(ASocket,‘11111,Data1111’,‘11111,Re111111’);
你不应该将传输层和数据的逻辑处理看在一起。传输层只需要确保点与点之间传输就行了,如果传输中又涉及些数据逻辑处理,会使你的程序复杂起来,写程序不外是将功能划分成各个模块,各个模块需要清晰即可,即使它的功能很简单。MIDAS技术中,这种分布就非常的清楚。Connection只是负责数据的传输,其它的解析、DataSet是逻辑处理,如果不是这样,以后别人维护这代码,会很头大。
begin
// Application.ProcessMessages;
Socket.SendBuf(Pointer(Text)^, Length(Text));
end;
procedure TTLWallySmtp.RecvText;
var
r, i: Integer;
s: string;
buf: array[0..1023] of Char;
ReplyCode, ReplyStr: string;
begin
ReplyCode := '';
ReplyStr := '';
i := 0;
Repeat
Application.ProcessMessages;
r := Socket.ReceiveBuf( buf, SizeOf(buf));
s := copy( buf, 1, r);
ReplyCode := trim( copy( s, 1, 4));
ReplyStr := trim( copy( s, 5, pos( CRLF, s) -5));
i := i+1;
until ((ReplyCode <> '') or (i>10));
if ReplyCode <> '' then
begin
// first digit of replycode 2 or 3 indicates success
if (ReplyCode[1] <> '2') and (ReplyCode[1] <> '3') then
raise Exception.Create(ReplyStr)
end else
raise Exception.Create('No Reply');
end;
而且我发现很多解放军都去人家很无聊的散分单子里去泡分,要
不我开个贴子讨论一下,欢迎大家从CODE中走出来,聊点别的。
http://expert.csdn.net/Expert/topic/1850/1850319.xml?temp=.7326166