一、Socket通信:
Delphi在ScktComp单元中对WinSock进行了封装,该单元提供了TAbstractSocket、TClientSocket、TClientWinSocket、TCustomSocket、TCustomWinSocket、TCustomServerSocket 、TServerClientThread、TServerWinSocket、 TServerClientWinSocket、、TServerSocket、TWinSocketStream等相关的类。
它的继承关系为:
TAbstractSocket类是所有Socket组件的基类。
TCustomWinSocket类是所有WinSock对象的基类。
TClientSocket类用于管理客户Socket连接。
TClientWinSocket类:客户Socket组件(TClientSocket)使用它来为客户应用管理WinSock API调用。
TServerSocket类为TCP/IP服务器管理服务器Socket连接。
TServerClientWinSocket类:服务器Socket组件(TServerSocket)使用它管理到一个客户Socket的WinSock API调用。
TServerWinSocket类:服务器Socket组件(TServerSocket)使用它为TCP/IP监听连接管理WinSock API调用。
TWinSocketStream类:在线程阻塞工作模式下提供到Socket连接的读写服务。
TServerClientThread类:用于为每一个到客户Socket的独立的连接创建一个线程。
要点:
TServerWinSocket类用于管理服务器端的Socket,TClientWinSocket类用于管理客户端Socket。因为我们用控件的话,放置的控件是标准的Socket控件不能为Windows平台直接管理和使用,所以要用TServerWinSocket和TClientWinSocket来分别进行管理使其转变为WinSock以便Windows平台更好的控制和使用。
服务器端Socket使用TServerClientWinSocket管理一个到客户Socket的连接。TServerClientWinSocket与TServerWinSocket的区别是:当把一个TServerSocket组件放置到一个窗体或数据模块上并进入监听状态后,就自动创建了TServerWinSocket,而只有当服务器Socket监听到一个客户Socket的连接请求时并接受了请求后,才创建一个TServerClientWinSocket。当客户Socket的连接断开时,自动删除相应的TServerClientWinSocket。TServerClientWinSocket有一个ServerWinSokcet属性,它返回处监听状态的服务器端Socket对象(TServerWinSocket)。
当你把服务端设置成stthreadblocking的阻塞方式的时候,OnRead根本不会被触发,OnRead是在非阻塞的异步的时候才会被触发.正确的方法(阻塞)是定义一个ClientThread的线程类,在ServerSocketGetThread的时候创建一个线程,处理客户端请求..这样就可以支持多个客户端同时连接和收发封包. 至于客户端,用非阻塞的方式就可以了.
疑问:
1、ClientSocket的ClientType为ctNonBlocking ctBlocking的区别?
2、ServerSocket的ServerType为stNonBlocking,stThreadBlocking的区别?
解答:
1、ctNonBlocking(非阻塞):它是异步进行读写操作,因此数据的传输不会阻塞应用程序中其他的代码执行,使用非阻断型连接时,当连接的另一端试图读写信息时,会触发OnReceive和OnSend事件来通知你的Socket,从Socket连接中读取信息需要调用ReceiveBuf或ReceiveIn方法,写信息需要调用SendBuf,SendStream或SendIn方法。当连接阻断后,Socket必须主动在已建立的连接上读写信息,而不是被动地等待Socket连接来通知。如果想控制何时开始读或写,请使用阻断型Socket。它是处理机制是通过消息处理。ctBlocking(阻断):此为阻断式。
2、对于服务器端Socket,将BlockMode属性设置为bmBlocking或是bmThreadBlocking都可以建立阻断型连接。当Socket正在等待完成某个请求的读写操作时,阻断型连接会拦截住所有其他代码的执行。当属性是stThreadBlocking(线程阻塞模式)时,服务器端Socket组件总会为每一个客户端连接生成一个新的执行线程。当属性为bmBlocking时,程序被阻塞,直到一个新的连接建立。要在OngetThread中生成TServerClientThread来处理数据,这时ThreadCacheSize提供了一个线程池的功能。
二、多线程编程:
Delphi的VCL有一个缺陷,就是不支持多个线程同时访问它。如果线程中要访问VCL对象可以用Synchronize来实现,它用Method参数指定的方法去访问VCL对象,实际上线程本人并不调用这个方法,而是通知主线程调用这个方法,主线程一次只能收到一个通知,这样就避免了对VCL对象并发访问。(注:任何继承VCL中定义的类的对象就是VCL对象)
编写线程代码时,必须要考虑其他线程的影响,具体的说:一方面线程互斥,另一方面就是线程同步。互斥就是要解决多线程同时访问一个全局变量,同步就是解决线程执行顺序的问题(一个线程结束之后,自动唤醒等待它的运算结果的其它线程)。
线程互斥:要避免多个线程并发访问全局变量时发生冲突,VCL中提供了三种解决方法:锁定对象、设置临界段、共享读—独占写。
1、锁定对象:一些对象本身就有Lock和UnLock方法,线程在操作这类对象时可以用这两个方法,还有一些线程安全的对象,如TCanvas和TThreadlist,它们自身就有一种机制来保持线程安全,如TCanvas可以自动使用Lock和Unlock来锁定和解锁。
2、设置临界段:如果没Lock方法,可以考虑临界段,它象一个门,同一时刻内只允许一个线程访问。它是通过创建一个TCriticalSection的全局实例来实现的。它有两个方法:Acquire(锁定)和Release(开放)。注意,只有所有线程都是通过临界段访问与之相连的全局内存,这种方法才能起作用。
3、共享读—独占写:设置临界体段的方法有一个缺点,就是在同一时刻只能有一个线程访问全局变量,而实际上我们不是总是改全局变量,很多情况只是读,这并不对内存造成错误,这种情况我们可采用TMultiReadExclusiveWriteSynchronizer对象来实现多个线程同时读全局变量,而只允许一个线程改。与设置临界段相同的是,也要求所有访问都采用这个对象,它有如下方法:BeginRead,EndRead,BeginWrite及EndWrite。
线程同步:如果一个线程必须等到其他线程的任务结束才能够继续,那么可以通知它暂时挂起。具有两种方式:
1、 等待其他线程结束,调用WaitFor方法可以实现。
2、 等待一个作业完成,有时希望等待一个线程完成一些操作,而不是等待一个线程执行结束。如果是这样,则需要一个事件对象TEvent,事件对象必须为全局对象,它对所有的线程都是可见的。当一个线程完成了其他线程所要求的任务,它就调用TEvent.SetEvent方法打开一个标志,其他线程可以检查这个标志,从而得知需要的任务已经完成。如果要关掉这个标志,则应调用ResetEvent方法。
疑问:
线程局部变量与普通变量的区别?threadvar
答:线程的局部变量要想被线程中调用的函数来访问,就要用threadvar来声明这个变量,否则线程中调用的外部函数就不能访问这个变量,这类变量只能被线程内的函数来访问.