📄 lc_bcb_85.txt
字号:
B.服务器端事件(server events)
服务器socket控件通过两中方式连接:监听连接和连接到客户应用.服务器socket收到这两个连接的所有事件.
监听时事件
当构成监听连接前,OnListen事件发生.在这个时候你能够通过socket属性获得server windows socket object.你能够使用它的SocketHandle属性去改变socket,在socket打开监听之前.例如,如果你想限定监听服务使用的IP地址,你可以在这个OnListen事件Handler中做.
与客户端连接的事件
当一个服务器socket同意一个客户连接请求时,接下来的事件发生:
1.服务器socket产生一个OnGetSocket事件,通过windows socket handle传送给连接的另一端的socket.如果你想提供自己定义的TServerClientWinSocket
of descendant,你可以在OnGetSocket 事件 handler中建立,将被用来替代TServerClientWinSocket.
2.一个OnAccept事件发生,传送新的TServerClientWinSocket对象给事件句柄.这是第一个要点,当你使用TServerClientWinSocket的属性去获得被连接中服务的那端的客户的信息时.
3.如果服务类型是stThreadBlocking,一个OnGetThread事件发生.如果你想提供自己定义的TServerClientThread子类,你可以在OnGetThread事件句柄中建立一个,它将替代TServerClientThread.
4.如果服务类型是stThreadBlocking,一个ONThreadStart事件发生当这个线程(thread)开始执行时.如果你想执行任何初始化这个线程,或调用一些windows
socket API在这线程开始通过连接读和写之前,应该使用OnThreadStart事件句柄.
5.当客户端完成一个连接时,一个OnClientConnect事件发生.如果是non-blocking服务,你可能想开始通过socket连接在这端进行读或写操作.
六、通过socket连接进行读和写
通过socket连接到其他机器的原因是想通过这些连接来读和写信息.什么信息是你要读和写的,或者当你想读和写时是依靠哪些socket连接的相关服务的.
通过sockets进行读和写可以是异步的,所以在你的网络应用中不需要阻塞其他代码的执行.这是调用non-blocking connection.你也同样可以通过blocking
connection,这时你的下一行代码的执行必须等到读或写操作完成.
A.Non-blocking连接,读和写是异步的, 所以在你的网络应用中不需要阻塞其他代码的执行.建立一个Non-blocking连接:
1.在客户socket中设置ClientType属性为ctNonBlocking.
2.在服务器socket中设置ServerType属性为stNonBlocking.
当连接是non-blocking时,连接的另一端企图读或写时读和写事件将把这个信息通知你的socket.
读和写操作事件
Non-blocking sockets想通过连接读或写时,它会产生一个读和写操作事件通知你的socket.在客户端sockets,你可以在OnRead或OnWrite事件句柄中对这些事件做出反应.在服务器端Scokets,可以在OnClientRead或OnClientWrite事件句柄中对这些事件做出反应.与socket连接相关联的windows
socket object在事件句柄的读或写中被当作一个参数.Windows socket object提供一个方法号(number of methods)以允许你通过连接读或写.
通过socket连接读,使用ReceiveBuf或ReceiveText方法.在使用ReceiveBuf方法前,使用Receivelength方法去确定在连接的另一端socket准备发送的字节数(number
of bytes).
通过socket连接写,使用SendBuf,SendStream,或SendText方法.如果你通过socket发送信息后不在需要socket连接,你可以使用SendStreamThenDrop方法.
SendStreamThenDrop在写完所有的信息后将关闭Socket连接,它能够从stream读信息.如果你使用SendStream或SendStreamThenDrop方法,不要释放Stream
object, socket在连接结束后会自动释放这个Stream.
注意:SendStreamThenDrop将关闭一个独立的客户连接服务,而不是监听连接.
B.Blocking connections
当你使用的连接是Blocking时,你的Socket必须通过连接发起读或写操作,胜过被动的等待从socket连接发来的通知. 当你的连接末端的读和写操作发生改变时使用Blocking
socket.对于客户端sockets,设置ClientType属性为ctBlocking 以便构成一个blocing connection.根据你的客户端应用想完成什么,你可能想建立一个执行线程去完成读或写操作,以便你的应用能够继续执行其他的线程,当它在等待通过连接读或写操作的完成.
对于服务器sockets,设置ServerType属性为stThreadBlocking以便构成一个blocking connection.因为blocking
connections在等待通过连接读或写信息完成时挂起了其他代码的执行,服务器socket控件通常产生一个新的执行线程给每一个客户连接,当ServerType设置为stThreadBlocking时.许多使用Blocking连接的应用都写使用线程(using
threads.甚至如果你不使用线程,你可能也想使用(using) TWinSocketStream去读和写.
1)using threads
当使用一个blocking connection进行读或写操作时,客户sockets不会自动产生一个新线程.如果你的客户应用程序没有什么事做,直到读或写信息完成,那么这正是你想要的.如果你的应用包括了一个用户界面,它还需要响应用户的操作,那么,你可能想产生一个独立的线程去读写.当服务器sockets形成一个blocking连接时,他们常常产生独立的线程给每一个客户连接,所以没有客户需要等待直到其他客户完成通过连接读或写操作.在默认情况下,服务器sockets使用TServerClientThread对象去实现为每个连接执行不同的线程.
TServerClientThread对象模拟发生在non-blocking连接中的OnClientRead和OnClientWrite事件.可是,这些事件发生在监听socket上时,不是本地线程(thread-local).如果客户请求频繁,你将想建立你自己的TServerClientThread子类去提供一个安全线程(Thread-Safe)去完成读和写操作.
当写客户线程或写服务器线程时,你能够使用TwinSocketStream去做实际的读写操作.
A)写客户端线程
为客户端连接写一个线程,定义一个新线程对象,使用新线程对象对话框.你的新线程对象Execute方法的句柄的通过线程连接进行读写操作的细节,可以建立一个TWinSocketStream对象,然后使用它来读或写.
使用你自己的线程,在OnConnect事件句柄中建立它.关于建立和运行线程的更多信息,请看Executing thread objects.
例子:这个例子显示一个应用的客户线程在连接确定后向服务器发出写请求.
void __fastcall TMyClientThread::Execute()
{
while (!Terminated && ClientSocket1->Active)
// make sure connection is active
{
try
{
TWinSocketStream *pStream = new TWinSocketStream(ClientSocket1.Socket,60000);
try
{
char buffer[10];
GetNextRequest(buffer);
// GetNextRequest must be a thread-safe method
// write a request to the server
pStream->Write(buffer,strlen(buffer) + 1);
// continue the communication (eg read a response)
}
__finally
{
delete pStream;
}
}
catch (Exception &E)
{
if (!E.ClassNameIs("EAbort"))
Synchronize(HandleThreadException());
// you must write HandleThreadException
}
}
}
B)写服务器线程
服务器连接线程由TServerClientThread派生.因为这个,不能使用新线程对象对话框.替代的,手动声明你的线程如下:
class PACKAGE TMyServerThread :
public ScktComp::TServerClientThread
{
public
void __fastcall ClientExecute(void);
}
注意你将用重载ClientExcute方法替代Execute方法.执行ClientExecute方法必须为客户端连接写一个同样的Execute方法线程.然而,当你从控件栏上放一个客户socket控件到你的应用上时来替代这个方法时.监听服务socket同意一个连接时,服务客户线程必须使用TServerClientWinSocket对象来建立.这可以利用共公共的CientSocket属性.另外,你能够使用HandleException这个protected性的方法,胜过
你自己写你的thread-safe例外操作.
警告:Server sockets会缓存他们使用到的线程.确信ClientExecute方法执行一些必要的初始化操作,以便它们在最后执行时不致于产生不利的结果.
当你使用你的线程时,在OnGetThread事件句柄中建立它.当建立线程,设置CreateSuspended参数为false.
例子:这个例子显示一个为一个应用服务的线程,这个应用是在连接确定后由客户端来的读请求.
void __fastcall TMyServerThread::ClientExecute()
{
while (!Terminated && ClientSocket->Connected)
// make sure connection is active
{
try
{
TWinSocketStream *pStream = new TWinSocketStream(ClientSocket,
60000);
try
{
char buffer[10];
memset(buffer, 0, sizeof(buffer));
if (pStream->WaitForData(60000))
// give the client 60 seconds to start writing
{
if (pStream->Read(buffer, sizeof(buffer) == 0)
ClientSocket->Close();
// if can't read in 60 seconds, close the connection
// now process the request
}
else
ClientSocket->Close();
}
__finally
{
delete pStream;
}
}
catch (...)
{
HandleException();
}
}
}
C.使用TwinSocketStream
当为一个blocking连接实现一个线程时,你必须确定在连接的另一端的socket是准备写还是读.Blocking连接不会通知socket当它准备好写或读操作的时候.想看看连接是否准备好,使用TWinSocketStream对象.TWinSocketStream提供一个方法去帮助调整读或写操作时间的选择.调用WaitForData方法去等待,直到socket另一端的
准备好写操作.当读写操作使用TWinSocketStream时,如果读或写操作在指定的时间期限内未能完成,Stream将发生超时.这个超时被当作一个结果,socket应用不会暂停,而是不断的通过一个dropped
connection试图读或写.
注意:你不能在non-blocking连接中使用TWinSocketStream