Series60套接字使用指南

来源:互联网 发布:js模块化插件有哪些 编辑:程序博客网 时间:2024/05/16 15:22

Series60套接字使用指南

一、关键组件:

1、  套接字服务器:提供对套接字协议的共享且通用性的访问。

2、  客户端API:提供对套接字服务器的受控访问。

3、  服务器插件系统:这里称为协议模块或PRT

二、客户端和服务器:

套接字表示两个参与者之间连接的端点,两个参与者分别被称为服务器和客户端。服务器被动等待到来的连接,而客户端则主动请求与服务器建立连接。

三、断开式和连接式套接字:

1、  断开式:

断开式套接字把数据包(数据包)简单地从一个参与者传递到另一个参与者,而没有在两个参与者之间进行会话的概念。这导致:

a、  每次向断开式套接字写入数据时,都必须指定目的参与者的地址。

b、  类似地,从断开式套接字读取数据时,每次读取都必须指定发送者的地址。

c、  断开式套接字不保证数据包将按照发送时相同的次序到达目的地。

类似于向一个朋友发送一系列信件。

2、  连接式(面向连接):

连接式套接字保证了在两个参与者之间建立会话的概念,因此只需在首次连接时一次性指定地址,并且数据报会按次序到达。类似于给一个朋友打电话。这种套接字的特点是:建立和使用效率较低。

 

注意:Series60同时支持连接式和断开式套接字,而且两者使用的类完全相同,例如RsocketRsocketServ区别仅在于建立和配置套接字的方式,以及之后用于发送和接收数据的Rsocket成员函数。

四、连接式套接字:

这种套接字要比断开式常用,但是以效率的少量降低为代价,提供了两个端点间的可靠数据流传输,使开发人员将精力集中于数据本身,而不是传输数据的机制。

1、  连接symbian OSSocket服务器:

无论是客户端还是服务器,所有Series60套接字编程的第一步都是连接到symbian OS套接字服务器。这比串行通信所需的初始化简单的多,不需要预先装载驱动器,也不需要显式启动服务器。

Tint err=iSocketServer.Connect();

If(err!=KerrNone && err!=KErrAlreadyExists)

{

       User::Leave(err)

}

其中:上面的Connect()函数的定义是:

TInt Connect(TUint aMessageSlots=KESockDefaultMessageSlots);

在这里提供了一个默认的参数值,即如果使用此函数时不指定参数值的话,就会使用这个缺省的参数。

在这里,参数aMessageSlots代表所要连接的服务器,可以是系统定义的其他服务器,也可以是程序员自己编写的服务器。

包含客户端套接字的应用程序必须连接到Symbian OS套接字服务器以创建套接字。接下来需要再执行两个步骤,首先,作为健全性检查,应该确保套接字服务器至少支持一种协议(即提供一个或多个PRT插件模块)

Tuint numProtocols;

err=iSocketServer.NumProtocols(numProtocols);

User::LeaveIfError(err);

最后需要获取与希望使用的协议相关的信息。根据名字查找协议并获取描述:

_LIT(KPotocol,”RFCOMM”);

TprotocolName protocolName(KProtocol);

err=iSocketServer.FindProtocol(protocolName,iProtocolInfo);

User::LeaveIfError(err);

2、  打开套接字

连接到Symbian OS套接字服务器后,即可打开一个套接字。这通过调用Rsocket::Open()执行,该方法带有以下参数:

l         RsocketServ& aServer:对一个打开的套接字服务器的会话的引用。

l         Tuint addrFamily:表示地址族的常数值。

l         Tuint sockType:表示套接字类型的常数值。

l         Tuint protocol:表示协议的常数值。

举例:

Tint err;

//创建一个服务器端套接字对象iSocket

Err=iSocket.Open(iSocketServer,iProtocolInfo.iAddrFamily,iProtocolInfo.iSockType,iProtocolInfo.iProtocol);

If(err!=KerrNone&&err!=KErrAlreadyExists)

{

User::Leave(err);
}

       注意:打开套接字实际上并未连接它,只是初始化一个套接字对象。

3、  连接服务器套接字:

为把套接字初始化为服务器,需要3个步骤。

a、首先,把该套接字绑定到一个地址和一个端口号,客户端套接字连接到此服务器时需要指定它们。

Const Tuint KportNumber=0x02;

iIrdaSockAddr.setPort(KPortNumber);

iSocket.Bind(iIrdaSocketAddr);

b、把套接字绑定到地址和端口号后,使用Listen()方法令它监听从别处到达的连接。为Listen()方法传递的KsizeOfListenQueue参数指定了监听队列的长度,即拒绝更多请求之前,套接字可以放入队列的未处理连接请求的个数。

此外,需要使用第二个套接字为所有到达的请求服务,因为监听的套接字必须保持空闲,以监视其他到达的连接请求。

Const Tuint KsizeOfListenQueue=1;

iSocket.Listen(KSizeOfListenQueue);

//创建空的套接字iServiceSocket

iServiceSocket.Open(iSocketServer);

c、最后需要调用Accept()接受第一个到达的请求。注意,有可能尚未接收到请求。但Accept() 是一个异步请求,因此它将在接收到请求时最终完成。另一方面,如果其他设备已经发出一个请求,该请求将被挂起至Listen()的监听队列中,导致Accept()快速返回。

任意情况下,调用Accept()的形式都是相同的:

iSocket.Accept(iServicSocket,iStatus);

iState=EAccepting;

SetActive();

在上面的一系列代码片断中,注意:

l         把第二个套接字(iServiceSocket)作为参数传递到Accept(),因为它才是连接建立之后实际用于传输数据的套接字。

l         CirSocketEngine类是一个活动对象,为此,把自己的iStatus成员传递到Accept(),随后调用自己的SetActive()方法。连接请求发生时,调用该对象的RunL()方法。

l         使用iState成员在CIrSocketEngine类内实现了一个基本的状态机。这是必不可少的,因为套接字API本质上是异步的――调用RunL()时,必须能判断当前处于哪种状态(例如接受中、读取中、写入中等)。iStateCirSocketsEngine类中这样的状态机中实现的一部分,而不是套接字API的一部分。注意,其他Series60套接字代码可能使用不同的变量名(或类型),不过,一般来说总会在这些行中实现某中状态机。

4、  连接客户端套接字

在最简单的层次中,连接客户端套接字只涉及调用Connect()并指定需要的地址和端口信息。比如把一个TCP/IP套接字连接到已知地址和端口的因特网服务器就属于这种情况。对于红外线、蓝牙,预先不知道目标机器地址,连接前需要有一个搜索阶段,设备会在此时查找范围内为给定协议提供服务(在某个套接字监听)的其他机器。

1)、搜寻

用于搜寻其他设备的类是RhostResolver。同时,该类还被TCP/IP套接字用于把主机名解析为数字ip地址。这和搜寻红外和蓝牙是完全不同的过程,但它们扮演的角色相似,同时RhostResolver类确保所有套接字类型有一致的API集。打开RhostResolver对象的方法与打开Rsocket的方法相同:调用Open()方法,并传递一个打开的套接字服务器会话、一个地址族和一个协议。

搜寻有可能需要较长的时间,因此,对应的APIRhostResolver::GetByName())是异步的。CirSocketsEngine类本身是一个活动对象,将会发出这些异步请求并处理它们的完成。启动搜寻阶段所需的代码如下:

const Tuint KnumOfDevicesToDiscover=1;

Tint err;

THostName hostname;

TPckgBuf<TUint> buf (KNumOfDevicesToDiscover);

iSocket.SetOpt(KdiscoverySlotsOpt,KleverIrlap,buf);

err=iHostResolver.Open(iSocketServer,iProtocolInfo.iAddrFamily,iProtocolInfo.iProtocol);

User::LeaveIfError(err);

iState=EDiscovering;

iHostResolver.GetByName(hostname,iNameEntry,iStatus);

SetActive();

这段代码最终调用异步函数iHostResolver::GetByName(),并传入自己的iStatus成员,启动搜索过程,同时把自己设置为活动。这使得RunL()方法将在搜索结束时被调用,注意同时相应的设置了iState变量,因此RunL()将能正确地判断正在处理的是哪种请求。

注意:可以通过调用RhostResolver::Cancel()取消GetByName()以及RhostResolver上的其他异步调用。

2)连接

活动对象处理GetByName()的调用成功结束时,iNameEntry成员将包含搜寻到的某个有效服务器机器的地址。之后,使用该地址进行连接,如下:

iHostResolver.Close();

iIrdaSockAddr=TirdaSockAddr(iNameEntry().iAddr);

iIrdaSockAddr.SetPort(KPortNumber);

iState=Econnecting;

iSocket.Connect(iIrdaSockAddr,iStatus);

SetActive();

 

Connect()也是一个异步函数。注意,即使搜索阶段成功,此函数仍有可能失败,例如服务器在被搜寻到之后移动到了范围之外。