我再说详细些,
在delphi中实现网络socket通讯时,使用TserverSocket类做服务器端,其中该类的ServerType属性有两个选择,分别决定服务器端是为每个客户request生成一个线程(选stThreadBlocking)还是所有的客户request都在一个线程中处理(选stNonBlocking),当我选择stThreadBlocking用线程方式时,客户端发送信息时,服务器端不能激活OnClientRead事件,而当选择非线程方式时则可以实现。
在delphi中实现网络socket通讯时,使用TserverSocket类做服务器端,其中该类的ServerType属性有两个选择,分别决定服务器端是为每个客户request生成一个线程(选stThreadBlocking)还是所有的客户request都在一个线程中处理(选stNonBlocking),当我选择stThreadBlocking用线程方式时,客户端发送信息时,服务器端不能激活OnClientRead事件,而当选择非线程方式时则可以实现。
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;
首先谢谢licwing(行走人生)大虾指点,
我马上按你的方法调试先......
//说明: 阻塞线程下为什么不触发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事件了吧?