by 程序(code) /Kyle
估计大家都想念Win32的Socket API, 或者 *nix 的Socket API吧,如果你开始Symbian编程。Win32下有一堆的Socket操作接口,以及各种模式的。Select模型,异步,完成端口,MFC封装的 两个Socket类。这些东东用的久了,也就大致忘记了其他手段了。*nix下,就是可以用用ePoll方式。本质上也算异步的控制翻转。下面是 Symbian的socket代码实例,一睹为快,其中的中文是偶加的,另外修改了其代码缩进风格。
/* Copyright (c) 2001, Nokia. All rights reserved */
#ifndef __SOCKETSENGINE_H__
#define __SOCKETSENGINE_H__
#include <in_sock.h>
#include "TimeOutNotifier.h"
#include "EngineNotifier.h"
#include "Sockets.hrh"
class CSocketsReader;
class CSocketsWriter;
class CTimeOutTimer;
class MUINotifier;
// Socket需要一个Engine类来支撑各个接口,下面的就是这个Engine的接口声明
// 因为SocketEngine需要回调,所以继承了 CActive,
// 因为需要通知超时事件,所以也实现接口 MTimeOutNotifier,
// 以及一些想知道Engine发生了什么事情的代码需要的接口 MEngineNotifier
/*!
@class CSocketsEngine
@discussion This class is the main engine part of the sockets application.
It establishes a TCP connection using its server name and port number (performing a DNS lookup
operation first, if appropriate).
It creates instances of separate active objects to perform reading from, and writing to, the socket.
*/
class CSocketsEngine : public CActive, public MTimeOutNotifier, public MEngineNotifier
{
public: // new methods
/*!
@function NewL
@discussion Create a CSocketsEngine object
@param aConsole console to use for ui output
@result a pointer to the created instance of CSocketsEngine
*/
static CSocketsEngine* NewL(MUINotifier& aConsole);
/*!
@function NewLC
@discussion Create a CSocketsEngine object
@param aConsole console to use for ui output
@result a pointer to the created instance of CSocketsEngine
*/
static CSocketsEngine* NewLC(MUINotifier& aConsole);
/*!
@function ~CSocketsEngine
@discussion Destroy the object and release all memory objects
*/
~CSocketsEngine();
/*!
@function ConnectL
@discussion Initiate connection of socket, using iServerName and iPort
*/
void ConnectL();
/*!
@function Disconnect
@discussion Disconnect socket
*/
void Disconnect();
/*!
@function WriteL
@discussion Write data to socket
@param aData data to be written
*/
void WriteL(const TDesC8& aData);
// 不是真正的读取Socket数据,而是触发 SocketReader 这个 Active Object,
// 真正能够读取数据的操作在 SocketReader 的 RunL中
/*!
@function Read
@discussion Initiate read of data from socket
*/
void Read();
/*!
@function SetServerName
@discussion Set name of server to connect to
@param aName new server name
*/
void SetServerName(const TDesC& aName);
/*!
@function ServerName
@discussion Get server name
@result name of server
*/
const TDesC& ServerName() const;
/*!
@function SetPort
@discussion Set port number to connect to
@param aPort new port number
*/
void SetPort(TInt aPort);
/*!
@function Port
@discussion Get port number
@result port number
*/
TInt Port() const;
/*!
@function Connected
@discussion Is socket fully connected?
@result true if socket is connected
*/
TBool Connected() const;
public: // from MTimeOutNotifier
/*!
@function TimerExpired
@discussion The function to be called when a timeout occurs
*/
void TimerExpired();
public: // from MEngineNotifier
/*!
@function ReportError
@discussion Report a communication error
@param aErrorType error type
@param aErrorCode associated error code
*/
void ReportError(MEngineNotifier::TErrorType aErrorType, TInt aErrorCode);
/*!
@function ResponseReceived
@discussion Data has been received on the socket and read into a buffer
@param aBuffer the data buffer
*/
void ResponseReceived(const TDesC8& aBuffer);
protected: // from CActive
/*!
@function DoCancel
@discussion cancel any outstanding operation
*/
void DoCancel();
/*!
@function RunL
@discussion called when operation complete
*/
void RunL();
private: // New methods
/*!
@function CSocketsEngine
@discussion Perform the first phase of two phase construction
@param aConsole the console to use for ui output
*/
CSocketsEngine(MUINotifier& aConsole);
/*!
@function ConstructL
@discussion Perform the second phase construction of a CSocketsEngine
*/
void ConstructL();
/*!
@function ConnectL
@discussion initiate a connect operation on a socket
@param aAddr the ip address to connect to
*/
void ConnectL(TUint32 aAddr);
/*!
@enum TSocketsEngineState
@discussion Tracks the state of this object through the connection process
@value ENotConnected The initial (idle) state
@value EConnecting A connect request is pending with the socket server
@value EConnected A connection has been established
@value ELookingUp A DNS lookup request is pending with the socket server
*/
enum TSocketsEngineState
{
ENotConnected,
EConnecting,
EConnected,
ELookingUp
};
/*!
@function ChangeStatus
@discussion handle a change in this object's status
@param aNewStatus new status
*/
void ChangeStatus(TSocketsEngineState aNewStatus);
/*!
@function Print
@discussion display text on the console
@param aDes text to display
*/
void Print(const TDesC& aDes);
private: // Member variables
/*! @const The maximum time allowed for a lookup or connect requests to complete */
static const TInt KTimeOut;
/*! @const The initial port number displayed to the user */
static const TInt KDefaultPortNumber;
/*! @var this object's current status */
TSocketsEngineState iEngineStatus;
/*! @var console for displaying text etc */
MUINotifier& iConsole;
/*! @var the actual socket */
RSocket iSocket;
/*! @var active object to control reads from the socket */
CSocketsReader* iSocketsReader;
/*! @var active object to control writes to the socket */
CSocketsWriter* iSocketsWriter;
/*! @var the socket server */
RSocketServ iSocketServ;
/*! @var DNS name resolver */
RHostResolver iResolver;
/*! @var The result from the name resolver */
TNameEntry iNameEntry;
/*! @var The anme record found by the resolver */
TNameRecord iNameRecord;
/*! @var timer active object */
CTimeOutTimer* iTimer;
/*! @var The address to be used in the connection */
TInetAddr iAddress;
/*! @var port number to connect to */
TInt iPort;
/*! @var server name to connect to */
TBuf<KMaxServerNameLength> iServerName;
};
#endif // __SOCKETSENGINE_H__
从上面的代码可以看出,SocketEngine在接口设计上,完整的体现了Symbian系统的设计风格和倾向。
1. 把各个任务切成各个 Active Object 来完成。
2. Active Object 内部一般都需要自己保留状态机以及其他的信息,来保证在RunL被调用的时候,知道自己在做什么。
3. M接口类的使用和使用。尽量使用组合的方式使用接口,除非当前类需要给其他模块提供接口的时候,才采用继承的方式实现接口。例如,其中的 SocketWriter和SocketReader是使用组合的方式,因为外部模块不需要了解Reader和Writer。外部接口希望知道的是 MEngineNotifier 所声明的接口,所以,SocketEngine实现了这个接口,但是其实是由SocketReader和SocketWriter在RunL或者其他实现 中调用的。
4. Active Object 实际上就是“回调”函数,只不过OO化了。在使用和设计上,有些违反人类的常规思路。比如简单的建立socket连接,需要在RunL中对 Scheduler的反馈的数据进行分析才可以做到。而不是简单的阻塞的方式。这个说明,这种模型是完全OO化的异步模型。希望能降低所有的计算开销。把 轮询等等操作都内化实现在Kernel中,不需要用户代码进行管理。这样的结果就是,用户代码的模型被紧密的耦合在Symbian的Active Object上,选择权的丧失,而且违反直觉。呵呵~