我再说详细些,
在delphi中实现网络socket通讯时,使用TserverSocket类做服务器端,其中该类的ServerType属性有两个选择,分别决定服务器端是为每个客户request生成一个线程(选stThreadBlocking)还是所有的客户request都在一个线程中处理(选stNonBlocking),当我选择stThreadBlocking用线程方式时,客户端发送信息时,服务器端不能激活OnClientRead事件,而当选择非线程方式时则可以实现。
    

解决方案 »

  1.   

    joymouse,下面是我给你的TServerSocket阻塞线程单元,希望对你有所帮助。需要注意的是:
    1、如果你使用TServerSocket的stNonBlocking模式,重写TServerClientThread线程时要重载
    TServerClientThread的ClientExecute过程,写你自己的处理过程;
    2、如果在线程内部调用外部的过程,建议使用同步化方法;
    3、线程访问线程内部变量的速度远远高于线程访问线程内部变量的速度
    如果有问题的话,可以和我联系 Email: [email protected]//**********************************************************************************
    //说明: TServerSocket阻塞线程
    //作者: licwing          时间: 2001-4-21
    //Email: [email protected]
    //**********************************************************************************
    unit BlockThread;interfaceuses SysUtils, Windows, Messages, Classes, ScktComp,ProxyCnt;type
      TBlockThread = Class(TServerClientThread)
      private
        FWebClient: TClientSocket; //对web进行数据请求
        FWebClientRead: Boolean; //是否可以对web进行数据请求
        FRequestInfo: string;
        FReceiveInfo: TReceiveInfo; //接收的数据流信息
        FRecBytesFromWebSend: Integer; //已接收到的Web数据字节数
        FWebSendBytes: Integer; //Web服务器需要发送的总字节数
       
        Receive_buf: array[0..8191] of char; //接收缓冲区
        Request_buf: array[0..8191] of char; //发送缓冲区,用来保存客户端的请求数据
        Request_buf_bytes: integer; //发送缓冲区已经接收的字节数
        FReceiveInfoSaved : boolean ; //接收的数据头信息保存标志    procedure  InitThread;
        function  SetWebClientInfo(FRequestURLInfo: String): boolean;
      protected
        procedure ClientExecute; override;
        procedure DoTerminate; override;
      public
        constructor Create(CreateSuspended: Boolean; ASocket: TServerClientWinSocket);
        destructor Destroy; override;
      end;implementation//TProxyClientThread
    constructor TBlockThread.Create(CreateSuspended: Boolean; ASocket: TServerClientWinSocket);
    begin
      FWebClient := TClientSocket.Create(nil);
      FWebClient.ClientType := ctBlocking;  inherited Create(CreateSuspended,ASocket);
      InitThread;
    end;destructor TBlockThread.Destroy;
    begin
      FWebClient.Free;
      inherited Destroy;
    end;procedure TBlockThread.InitThread;
    begin
      FRecBytesFromWebSend := 0;
      FWebSendBytes := 0;
      Request_buf_bytes := 0;
      FReceiveInfoSaved := false;
      FWebClientRead := false;
    end;function TBlockThread.SetWebClientInfo(FRequestURLInfo: String): boolean;
    var
      URL_info: TURLHostInfo;
      FWebFilter: boolean;
    begin
      URL_info := GetURLHostPort(FRequestURLInfo);
      Result := ( URL_info.HostName <> '' ) and ( URL_info.HostPort > 0 );
      FWebClient.Port := URL_info.HostPort;
      FWebClient.Host := URL_info.HostName;
    end;procedure TBlockThread.DoTerminate;
    begin
      FRecBytesFromWebSend := 0;
      FWebSendBytes := 0;
      FRequestInfo := '';
      FReceiveInfoSaved := false;
      FWebClientRead := false;
      inherited DoTerminate;
    end;procedure TBlockThread.ClientExecute;
    var
      ReceiveStream,
      RequestStream: TWinSocketStream;
      Rec_Bytes: integer;
    begin
     //获取和处理命令直到连接或线程结束
      while (not Terminated) and (ClientSocket.Connected) do
        try //try 1
          //创建TWinSocketStream对被代理端进行读写操作
          RequestStream := TWinSocketStream.Create(ClientSocket, 60000);
          try  //try 2
            //获取被代理端请求
            Request_buf_bytes := RequestStream.Read(Request_buf,8192);
            //如果有请求,设置Web端信息
            if Request_buf_bytes > 0 then FWebClientRead := SetWebClientInfo(Request_buf);
            //Web端通信准备完成,并且访问站点没有被过滤  IfID=1
            if FWebClientRead  then 
              begin //IFID=1 begin Then method
              try  //try 3
                //Web端开始通信
                ReceiveStream := TWinSocketStream.Create(FWebClient.Socket, 60000);
                FWebClient.Active := True;
                  try //try 4
                    //向Web端发送请求
                    ReceiveStream.Write(Request_buf,Request_buf_bytes);
                    while (not Terminated) and (FWebClient.Socket.Connected) do
                    begin
                      //接收Web端返回的数据
                      Rec_bytes := ReceiveStream.Read(Receive_buf,8192);                  Inc(FRecBytesFromWebSend,Rec_bytes);
                      //保存Web端返回数据的信息
                      if not FReceiveInfoSaved then
                        begin
                          FReceiveInfo := AnalyzeReceiveData(Receive_buf);
                          FReceiveInfoSaved := FReceiveInfo.ContentInfo.RequestFound;
                          FWebSendBytes := FReceiveInfo.ContentInfo.ContentSize +
                                       FReceiveInfo.RemoteFileInfo.RemoteFileSize;
                        end;
                      //如果被代理端还连接,传送Web端返回的数据
                      if ClientSocket.Connected then                   RequestStream.Write(Receive_buf,Rec_Bytes);
                      if ( FRecBytesFromWebSend >= FWebSendBytes ) or ( Rec_bytes = 0 )
                       or not ClientSocket.Connected then FWebClient.Close;
                    end;
                  finally  //try 4
                    ReceiveStream.Free;
                  end;
              finally  //try 3
                ClientSocket.Close;
              end;
            end  //IFID=1 end Then method
            else //IFID=1 begin else method
              begin//发送拒绝请求信息到客户端
               // RequestStream.Write(AccessLimit,sizeof(AccessLimit));
              end;
          finally  //try 2
            RequestStream.Free;
            Terminate;
          end;
        except  //try 1
          // HandleException;
        end;
    end;end.
    //**********************************************************************************
    // TServerSocket阻塞线程调用方法
    //**********************************************************************************
    procedure TfrmMain.ServerSocketGetThread(Sender: TObject;
      ClientSocket: TServerClientWinSocket;
      var SocketThread: TServerClientThread);
    begin
      SocketThread := TBlockThread.Create(false,ClientSocket);
    end;
      

  2.   


    首先谢谢licwing(行走人生)大虾指点,
    我马上按你的方法调试先......
      

  3.   

    按licwing(行走人生) 的做法是可以,我已经试过了。但多线程方式为何就不能triger出OnRead消息呢?delphi上的帮助上说是可以的呀。并且我也观察到当有Client联结时服务器端确实是自动为每个Client生成了线程的呀?
      

  4.   

    //**********************************************************************************
    //说明: 阻塞线程下为什么不触发OnRead和OnWrite事件
    //作者: licwing          时间: 2001-5-18
    //Email: [email protected]
    //**********************************************************************************
    1.首先先分析TServerSocket的继承关系
      TAbstractSocket->TCustomSocket->TCustomServerSocket->TServerSocket
    属性 ServerType 是在 TCustomServerSocket 里面定义的
      property ServerType: TServerType read GetServerType write SetServerType;
    同时在TCustomServerSocket里定义了TServerWinSocket,用来处理客户连接
    TServerWinSocket的继承关系如下:
      TCustomWinSocket->TServerWinSocket
    也就是说我们在TServerSocket里面触发的OnRead,OnWrite事件事实上是由TServerSocket
    的父类TCustomServerSocket中的TServerWinSocket触发的。2.现在我们分析在TCustomServerSocket里定义的TServerWinSocket客户连接方法
    procedure TServerWinSocket.Accept(Socket: TSocket);
    var
      ClientSocket: TServerClientWinSocket;
      ClientWinSocket: TSocket;
      Addr: TSockAddrIn;
      Len: Integer;
      OldOpenType, NewOpenType: Integer;
    begin
      Len := SizeOf(OldOpenType);
      if getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@OldOpenType),
        Len) = 0 then
      //从这里开始是处理多线程的
      try
        if FServerType = stThreadBlocking then
        begin
          //  SO_SYNCHRONOUS_NONALERT = $20 在winsock单元里面定义 
          //{$EXTERNALSYM SO_SYNCHRONOUS_NONALERT}
          NewOpenType := SO_SYNCHRONOUS_NONALERT;  
          setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@NewOpenType), Len);
        end;
        Len := SizeOf(Addr);
        ClientWinSocket := WinSock.accept(Socket, @Addr, @Len);
        if ClientWinSocket <> INVALID_SOCKET then
        begin
          ClientSocket := GetClientSocket(ClientWinSocket);
          if Assigned(FOnSocketEvent) then
            FOnSocketEvent(Self, ClientSocket, seAccept);
          if FServerType = stThreadBlocking then
          begin
            //********多线程事件响应关键部分*******
    //TAsyncStyle = (asRead, asWrite, asOOB, asAccept, asConnect, asClose);
    //asRead The socket receives notification that the connection is ready for reading.
            //asWrite The socket receives notification that the connection is ready for writing.
            ClientSocket.ASyncStyles := [];
    //*************************************
            GetServerThread(ClientSocket);
          end;
        end;
      finally
        Len := SizeOf(OldOpenType);
        setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, PChar(@OldOpenType), Len);
      end;
    end;3.ASyncStyles属性只有在TCustomWinSocke类里面定义的,再看SetAsyncStyles过程
    procedure TCustomWinSocket.DoSetAsyncStyles;
    var
      Msg: Integer;
      Wnd: HWnd;
      Blocking: Longint;
    begin
      Msg := 0;
      Wnd := 0;
      if FAsyncStyles <> [] then  //非阻塞模式
      begin
        Msg := CM_SOCKETMESSAGE;  //由消息CM_SOCKETMESSAGE触发的过程是过程
                                  //TCustomWinSocket.CMSocketMessage(var Message: TCMSocketMessage);
        Wnd := Handle;
      end;
      WSAAsyncSelect(FSocket, Wnd, Msg, Longint(Byte(FAsyncStyles))); //非阻塞模式处理
      if FASyncStyles = [] then   //阻塞模式
      begin
        Blocking := 0;
        ioctlsocket(FSocket, FIONBIO, Blocking);//阻塞模式处理
      end;
    end;4.再看由消息CM_SOCKETMESSAGE触发的过程
    procedure TCustomWinSocket.CMSocketMessage(var Message: TCMSocketMessage);
    begin
      with Message do
        if CheckError then
          case SelectEvent of
            FD_CONNECT: Connect(Socket);
            FD_CLOSE: Disconnect(Socket);
            FD_READ: Read(Socket);              //触发Read事件
            FD_WRITE: Write(Socket);            //触发Write事件
            FD_ACCEPT: Accept(Socket);
          end;
    end;现在应该明白为什么在阻塞模式下不触发OnRead和OnWrite事件了吧?