想使用ScktSrvr.exe建立一个双方随时可以平等主动发送数据的连接.orber(破烂的)及 halfdream(哈欠)请进,需要具体解决方案。

解决方案 »

  1.   

    我想在TSocketConnection和ScktSrvr.exe建立一个可以相互主动发送数据的功能,现在Delphi论坛真是冷清啊
      

  2.   

    http://www.2ccc.com/article.asp?articleid=3143
      

  3.   

    lzf1010(深宇),谢谢你,但是你是这个不是真正用Socket来实现的,你是用接口来实现的。
    我要的是通过Socket来进行双向通讯的。
      

  4.   

    那你干脆不要用SocketConnection和代理服务器了,直接用ServerSocket和ClientSocket就行了
      

  5.   

    是啊,我也不明白楼主想干什么?
    用TSocketConnection和ScktSrvr.exe就是要利用它们的接口,如果想通过Socket来进行双向通讯,还不如自己自定义一些规则直接实现。用不着TSocketConnection和ScktSrvr.exe
      

  6.   

    呵呵,是因为我有一个系统现在已经是基于ScktSrvr.exe开发的,现在只不过想直接利用ScktSrvr.exe已经与客户端建立起来的连接进行由双向的信息处理,而不想在客户端再另外再注册一个接口来实现。
      

  7.   

    lzf1010(深宇) 
    你那个示例使用多线程时,会提示"应用程序调用一个已为另一线程整理的接口" 这个问题有没有解决到啊
      

  8.   

    研究这么久,已经有些进展了,应当是要在Borland自己定义的那个ITransport上考虑差不多了..
      

  9.   

    晕,今天才看到贴子里面提到我.任何一个TCP连接本身都是提供双向数据通路,至于通讯双方是否可以互相主动发数据,
    那主要看应用协议与双方使用的I/O模式.关于应用协议方面要求,我想关键注意解决:
    1,一端提收到数据,应该能够分清此数据是对方主动发动的请求还是自己这端请求的回应包.
    2,能够判断回应包是哪次请求的回应.(这点偶尔也不需要)
    然后就是I/O模式了..TClientSocket/TServerSocket缺省非阻塞方式是使用的基于消息的异步I/O,
    TSocketConnection里使用TClientSocket则使用了WINDOWS的事件I/O.
    你可以看一下这方面的资料,就会很明白TSocketconnection与TScktSrvr.exe的实现.
      

  10.   

    halfdream(哈欠) ,
    我以为你睡着了呢:)
    我现在的问题是发送数据时被Borland的ITransport解释为前Sinagture不一样,在Receive的这一段代码出错了
    ....
    if (Sig and CallSig <> CallSig) and
         (Sig and ResultSig <> ResultSig) then
        raise Exception.CreateRes(@SInvalidDataPacket);
    ....然后就提示Error reading from socket
      

  11.   

    这是我实际项目中的代码片段
          ServerTCP.IOHandler.ReadStream(Source,8,False);//读取服务器发过来的数据包的长度
          Source.Position:=4;
          Source.Read(Len,4);
          Source.Position:=8;
          ServerTCP.IOHandler.ReadStream(Source,Len,False);//读取服务器发过来的数据包TSocketconnection与TScktSrvr.exe发送数据时的格式是这样的,前四个字节存放的是Sig(标识),接着的四个字节存放的是该次发送的数据的长度希望我提供的信息对你有所帮助,因为我到现在还不知道你想做些什么
      

  12.   


      看Delphi的演示,Demo
      

  13.   

    lzf1010(深宇),请查看短信息,加我的MSN吧。lxj_com2006(小帅),你说的Demo是哪个?
      

  14.   

    我也不清楚楼主想干什么,呵呵.
    楼主是想根据TSocketConnection与TScktSrvr.exe源码来改造扩展成什么的.
      

  15.   

    TSocketConnection与scktsrvr.exe 设计很大程度是提供一个简单的DISPATCH方式远程调用的通道,楼主是想使用它来传数据?
      

  16.   

    其中一个关键问题就是需通讯协议冲突问题..这儿说的通讯协议,指的是在TCP之上的应用协议.TSocketConnection/scktsrvr.exe用的应用协议,读一下源码,可以把它大致可以划分为两层.
    A: Dispatch调用所需的通讯交易:
      asError         = $01;   // Specify an exception was raised
      asInvoke        = $02;   // Specify a call to Invoke
      asGetID         = $03;   // Specify a call to GetIdsOfNames
      asCreateObject  = $04;   // Specify a com object to create
      asFreeObject    = $05;   // Specify a dispatch to free
      asGetServers    = $10;   // Get classname list
      asGetGUID       = $11;   // Get GUID for ClassName
      asGetAppServers = $12;   // Get AppServer classname list
      asSoapCommand   = $14;   // Soap command
      asMask          = $FF;   // Mask for actionB: 数据简单封装(加标志,长度信息)(未完待续..)
      

  17.   

    我的构思是:想实现从Borland Scktsrvr.exe在增加一个菜单项,名叫["发送消息到客户端正“],当选中相应的客户端连接,然后点击这个菜单,则发送消息到客户端,客户端显示相应的信息。   我的菜单点击代码是:
       var
      i: Integer;
      myData :TDataBlock;
      msg :string;
    begin
      msg  :='来自服务端的信息!';
      myData :=TDataBlock.Create();
      myData.Signature :=  CallSig;
      myData.Write(msg,sizeof(msg  )) ;
      with SelectedSocket.Socket do
      begin
        Lock;
        try
          for i := 0 to ConnectionList.Items.Count - 1 do
            with ConnectionList.Items[i] do
              if Selected then
              begin
                TServerClientThread(Data).ServerSocket.Connections[0].SendBuf(myData,Sizeof(myData)) ;            
              end;
        finally
          Unlock;
        end;
      end;
    end;
    ---
    但是在Receive的这一段代码出错了
    ....
    if (Sig and CallSig <> CallSig) and
         (Sig and ResultSig <> ResultSig) then
        raise Exception.CreateRes(@SInvalidDataPacket);
    ....然后就提示Error reading from socket
      

  18.   

    halfdream(哈欠) ,
    你说的对,因为现在的项目已经是使用TSocketConnection+Scktsrvr.exe做的,现在想扩展下支持双向的发送消息功能。
      

  19.   

    ..Connections[0].SendBuf(...
                 ~~~~\这儿应该是i吧?
      

  20.   

    当然,最大的错误是你用的.SendBuf去传一个对象指针.
    原因你可以看看.TSocketDispatcherThread.Send方法,看它是怎么发送TDataBlock的.于是你的代码至少需要改成下面:.....
        with ConnectionList.Items[0] do
        begin
          if Assigned(Data) then
          begin
            (TSocketDispatcherThread(Data) as ISendDataBlock).send(myData);
          end    end;
    //---------------------------------------------------这仍离你的目标相当远..仅仅能成功的把数据包主动发向客户端而已,这样还存在的不少问题..
    下一步你仍需要深入去理解scktsrvr.exe源码架构,分析解决协议冲突为主的一系列问题.
      

  21.   

    改成这样子就跟我从客户端发送数据产生的错误一样了,提示如下错误:
    Invalid action received: 0.应当是如halfdream(哈欠)所说,主要是我对协议方面处理没搞清楚,自己这方面的经验真的太少,能否指点得更细点。我客户端发送信息的代码是:
    var
      SocketTransport: TSocketTransport;
      Data :TDataBlock;
      msg :string;
    begin
      msg :='客户端发送的消息!';
      Data :=TDataBlock.Create();
      Data.Signature :=  CallSig;  Data.Write(msg,sizeof(msg)) ;
      Self.SocketConnection1.Send(Data,true);
    end;--
    其中Send方法是继承自TStreamedConnection来的,执行后提示如下错误:
    Invalid action received: 0.
      

  22.   

    出现Invalid action ,
    peterzhou2000只要看TDataBlockInterpreter.InterpretData方法源码就很容易理解了..procedure TDataBlockInterpreter.InterpretData(const Data: IDataBlock);
    var
      Action: Integer;
    begin
      Action := Data.Signature;
      if (Action and asMask) = asError then DoException(Data);
      try
        case (Action and asMask) of
          asInvoke: DoInvoke(Data);
          asGetID: DoGetIDsOfNames(Data);
          asCreateObject: DoCreateObject(Data);
          asFreeObject: DoFreeObject(Data);
          asGetServers: DoGetServerList(Data);
          asGetAppServers: DoGetAppServerList(Data);
        else //你发的数据包不属于上面每一种情况..
          if not DoCustomAction(Action and asMask, Data) then
            raise EInterpreterError.CreateResFmt(@SInvalidAction, [Action and asMask]);
        end;
      except........DoCustomAction又干了些什么呢:function TDataBlockInterpreter.DoCustomAction(Action: Integer;
      const Data: IDataBlock): Boolean;
    begin
      Result := False;//很明显,TDataBlockInterpreter这个类并未处理非约定协议的数据.
    end;
    //---------------------------------------
    这是绕不开的地方之一,你想想可以如何去修改或扩展此处.
      

  23.   

    如果是仅仅想向客户端发送一些消息,我最初给的那个链接里面的方法已经可以达到你的要求了。不一定非要自己使用socket
      

  24.   

    那我推荐你一种改法吧,
    自定义一个WINDOWS消息,把custom类数据块拆包,将内容POST出来.这样对原有的VCL代码影响最小.
    至于你要将接收的数据怎么处理,就只管处理那个自定义消息就是了.
      

  25.   

    可以参考,增加自己的协议类型如: asMyMsg        = $15; // customer message by
    服务端发送消息的时候,Data.Signature := ResultSig or asMyMsg;修改如下方法:
    procedure TStreamedConnection.ThreadReceivedStream(var Message: TMessage);
    var
      Data: IDataBlock;
      Applied : boolean;
    begin
      Data := IDataBlock(Message.lParam);
      Data._Release;
      Applied := false;
      if assigned(FOnReceive) then FOnReceive(Data, Applied);
      if not Applied then Interpreter.InterpretData(Data);
    end;FOnReceive 是自定义的事件,可以从TStreamedConnection一直property 到 TSocketConnection, 这样你就可以在TSocketConnection的OnReceive事件中处理自己的数据包了
    如:  if (Data.Signature and asMask) = asMttMsg then
      begin
        //.....
        Applied := true;
      end;这样是可以从客户端接收到服务端的消息的,但是有一个奇怪的问题,我从服务端定时发送消息给客户端(测试客户端是否已经意外断开网络)的时候,还是会执行到 TDataBlockInterpreter.InterpretData 中进行解析,然后提示错误,实际上是自己的消息应该是在这之前就已经被自己处理掉了的。还希望大家能够帮忙借答
      

  26.   

    呵呵,刚才回答完问题,又看了一下代码,发现Data.Signature := ResultSig or asMyMsg;
    改成 Data.Signature := asMyMsg; 然后在处理的时候 if Data.Signature = asMttMsg then 即没有出现错误提示了,多改写一个地方:
    procedure CheckSignature(Sig: Integer);
    begin
      if (Sig and $FF00 <> CallSig) and
         (Sig and $FF00 <> ResultSig) and (Sig <> asMyMsg) then
        raise Exception.CreateRes(@SInvalidDataPacket);
    end;错误分析:估计是,加了ResultSig标志后,在客户端接收数据的时候,原代码碰到ResultSig就会break出循环,结束一次wait,很有可能在客户端正在取数据的过程中(有可能要发送多个数据包完成一次请求),服务端发送一个包含ResultSig标志的自定义消息,中断了系统自己的请求数据操作,引发错误。ScktSrvr的源代码还并没有彻底读懂,不当支出,请高手指正。
      

  27.   

    kasteboy() ,
    CheckSignature你是写在哪里啊?能否将你的OnReceive事件贴出来看看
      

  28.   

    procedure Tdatatest.doOnReceiveMttMsg(const Data: IDataBlock;
      var Applied: boolean);
    var
      aMsg : TMttMsg;
    begin
      if Data.Signature = asMttMsg then
      begin
        aMsg := TMttMsg.Create;
        aMsg.ReadMttMsgFrom(Data);
        if aMsg.SendType <> cSvrTest then
          showmsghintfrm(aMsg);
     
        Applied := true;
        aMsg.free;
      end;
    end;
    ----------------------------
    CheckSignature 是 SConnect 单元里面的
      

  29.   

    应该是:
      if Data.Signature = asMyMsg then
      

  30.   

    TMttMsg又是如何定义的呢?
    我的SConnect 单元里面怎么找不到这个CheckSignature过程。我用的是Delphi6。
      

  31.   

    d6我这里没有,不知道哦,我用的是d7。 你试着在d6的sconnect单元中搜索 (Sig and $FF00 <> CallSig) 这里的判断代码估计是一样的,
    Tmttmsg 是一个自定义的类,没什么特别的内容,仅仅把一些read、write Data的操作简化一下, aMsg.ReadMttMsgFrom(Data); 也只是从data中读取数据,Tmttmsg里与之对应的还有一个aMsg.WriteMttMsgTo(Data)的方法。 你也可以把这句话写成 Data.read(buf^, 100) 或者对应的Data.write(buf^, 100) 等操作,其中buf是你的数据
      

  32.   

    三层连接有个 选项 设置
    设置 好后 可以 用 callback
    server 主动调用 客户端的 方法 
    我用 DCOM 实现过这是对等吗?不过 我习惯这样用 
    我还是喜欢 直接 socket
      

  33.   

    peterzhou2000
    这样写的时候,其实就不需要修改CheckSignature这函数了.  msg :='客户端发送的消息!';
      Data :=TDataBlock.Create();
      Data.Signature :=  CallSig;//对端的CheckSignature仅是判断是否有这东西.
      Data.Write(msg,sizeof(msg)) ;
      SocketConnection1.Send(Data,true);
    .........................peterzhou2000真是走了不少弯路,不过这源码还是有些复杂度.也曾被它弄得头晕晕的..
    好在现在不了.....(继续上面我2.7日发的)...........
    从源码可以很容易看出,这儿的基本数据封装是这样定义:
    通讯包= 包头sig(4字节) + 包体长度(4字节) + 包体
    包头sig四字节,由两部分组成, 
    一部分用来区分主动包还是回应包
      CallSig         = $DA00; // Call signature
      ResultSig       = $DB00; // Result signature
    另一部分是一个字节的功能码
       在我2.7日发的回贴里面具体列出这些功能码,仅从这一层数据封装层次上看,
    功能码具体是什么含义,这儿不管.这样大致分层后,再重新去分析相关的代码,就会清晰得多了..
    我估计把这两层叫为
    A---功能层,定义了几种交易类型,来支撑了DISPATCH远程调用..
    B---SOCKET封装层,加数据包头,发送接收数据.
    A层: TStreamedConnection 相关的一些组件,特别是TDataBlockInterpreter
    B层: TSocketConnection(继承自TStreamedConnection)相关的一些组件,如TSocketTransport很多东西仍需要楼主自己去分析去理解,你自己会发现,往往只差那一层窗户纸罢了.
      

  34.   

    其实BORLAND的程序员在设计TSocketConnection,在架构上是留了扩展余地的:
    TDataBlockInterpreter的DoCustomAction是虚函数,可以扩展的.
    下面是我随手写的,没调试.
    TMyDataBlockInterpreter=class(TDataBlockInterpreter)
      protected
        function DoCustomAction(Action: Integer; const Data: IDataBlock): Boolean; override;
      end;TMySocketConnection = class(TSocketConnection)
      private
        FMyInterpreter: TCustomDataBlockInterpreter;
      protected
        function GetInterpreter: TCustomDataBlockInterpreter; override;
      end;...function TMySocketConnection.GetInterpreter: TCustomDataBlockInterpreter;
    begin
    //
      if not Assigned(FMyInterpreter) then
        FMyInterpreter := TMyDataBlockInterpreter.Create(Self, SSockets);
      Result := FMyInterpreter;end;{ TMyDataBlockInterpreter }function TMyDataBlockInterpreter.DoCustomAction(Action: Integer;
      const Data: IDataBlock): Boolean;
    begin
    //
    //这儿,你可以把数据从Data中读出来
      Result := true;//返回true值,在TDataBlockInterpreter.InterpretData处理时就不会抛异常
    end;
      

  35.   

    scktsrvr中Receive处理中,没有作缓冲的处理,这样在网络有异常的情况,也就容易出现:invalid read from socket这错误了。一般加个缓存机制就基本可以了。本身的ITransport实现类没有作这步处理,且使用阻塞socket实现,收不到数据但连接正常(Internet中经常有此情况出现),就出raise invalid read错误了。也容易处理粘包的问题。
      

  36.   

    看错题  if (Sig and CallSig <> CallSig) and
         (Sig and ResultSig <> ResultSig) then
        raise Exception.CreateRes(@SInvalidDataPacket);SERVER & CLIENT都有这个错误,是为了验证数据包的正确性。所以你在组包的时候,将包加入这个标志就行了。Data.Sig = MyFlag or ResultSig;
      

  37.   

    然后像楼上halfdream(哈欠)写的扩展方法写一个TSocketConnection来处理对应的事件就完了。
    方法N多,不过不建议改原有系统支撑的东西,如果不很熟的话。