大家都知道,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;

解决方案 »

  1.   

    只是传递了String吧,如果是N个数据类型呢?
      

  2.   

    只是一种方案而已:)
    如果有多个数据类型,我会先压缩,再用Base64编码为字符串格式。
      

  3.   

    不觉得这样封装有什么效果。
    Socket分异步和阻塞两种情况,两种情况下,应该都不一样,所以得分开。而封装成SendData, RecvData,感觉没必要,SendBuf, ReceiveBuf已经是将数据Send, Receive了,再封装,俺感觉很别扭。如果将ClientSocket, ServerSocket再封装一次,变成专门传输自定义格式的数据,那已经不是ClientSocket一层了,就像是你写一个HTTP传输类,组件了。比如使用的HTTP组件,它是使用Socket进行数据传输,但传输的内容已经变成HTTP协议里的内容,而组件外,是使用HTTP这个组件类,而非Socket类了。
      

  4.   

    copy_paste:
    我就是要这样的效果啊
    至少在整个公司的工程中可以共享这一段代码了。
    如果按照通常的做法,
    同样需要定义客户端与服务器端的指令,来回应答。同样是自定义格式的,但是每次编码时都需要重新写一次。
      

  5.   

    SendBuf, ReceiveBuf
    的确是已经封装好了,但是有一个缺点,就是当发送大数据量时,有可能接收方会触发多次OnRead事件,仍然需要手工拼装。
      

  6.   

    这样共享好像也没什么啊。
    还不如写个类,封装好数据传输这块,然后导出一个函数:
    function SendCommand(Command: string; WaitFor: Boolean): string;将所有的命令全部在这里进行控制,其它人所关心的只是返回值是否正确就行了,
    不然感觉调用的时候还要有个ClientSocket组件,还得控制它的事件,这做什么那做什么不像封装了如果俺来写的话,首先写个类,写好C/S的传输规则,然后在DataModule或App控制类里创建这个类实例,然后用它来导来对应的函数,其它人就使用这些函数进行数据传输。
    如果以后升级,扩展的话,只要去改这个类,其它则不需要去改。按你的思路,N个单元需要用到传输的话,需要new N个ClientSocket组件,每个人写的代码可能不同,也许为了控制的问题,但以后维护起来就麻烦点了。
      

  7.   

    >>SendBuf, ReceiveBuf
    >>的确是已经封装好了,但是有一个缺点,就是当发送大数据量时,有可能接收方会触发多次
    >>OnRead事件,仍然需要手工拼装。你使用异步方式是会这样,俺比较少用它,如果客户量不是成欠上万的话,俺一般使用多线程来做的。阻塞+线程的用处就是一直接收数据,它是一个线程里交互全部的会话,而不是使用消息(OnRead事件),所以就不存在这种情况。
      

  8.   

    我一般使用同步通信比较多,我的做法是利用这两个组件封装的一些方法来建立连接等操作(避免直接使用Windows底层的繁琐),然后利用他们的Socket对象取得句柄,使用API操作读写,并封装几个读写函数,用于解决大数据量报文的收发、自动拼装报文等,然后将其做成控件,整个部门共享,不就行了?
      

  9.   

    copy_paste(木石三) :有时候一个报文由于网络原因一个ReceiveBuf是接收不完的,也就是说需要多次调用ReceiveBuf才能收到期望字节数的报文,在实际应用中需要考虑到这个情况,不能简单的调用一次,然后如果收的字节数不对就报错。
      

  10.   

    yashenJXF:
    我觉得这样太麻烦,所以使用ADODataSet.SaveToFile
    一次性发送一个表更方便。
      

  11.   

    风舞轻扬:
      楼主的用意是规范公司的套节字使用话?还是说一下的。
      但要是说用只有两个MARK的数据包来谈封装还是太简单了,而且还要调用者提供套节字对象,代码中亦未考虑用户发送内容中是否含有标志位的问题。
      如:SendData(ASocket,‘11111,Data1111’,‘11111,Re111111’); 
      
      

  12.   

    yashenJXF
      你不应该将传输层和数据的逻辑处理看在一起。传输层只需要确保点与点之间传输就行了,如果传输中又涉及些数据逻辑处理,会使你的程序复杂起来,写程序不外是将功能划分成各个模块,各个模块需要清晰即可,即使它的功能很简单。MIDAS技术中,这种分布就非常的清楚。Connection只是负责数据的传输,其它的解析、DataSet是逻辑处理,如果不是这样,以后别人维护这代码,会很头大。
      

  13.   

    没有对服务器端返回的出错信息进行处理。我贴一下我曾写过的代码让你参考:procedure TTLWallySmtp.SendText(const Text: string);
    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;
      

  14.   

    楼上各位大侠,要变成解放军,你们每天花多少时间在CSDN上。
    而且我发现很多解放军都去人家很无聊的散分单子里去泡分,要
    不我开个贴子讨论一下,欢迎大家从CODE中走出来,聊点别的。
    http://expert.csdn.net/Expert/topic/1850/1850319.xml?temp=.7326166