symbian 使用 sokects API

1介绍

      这篇文章介绍了symbian 系统中的 sockets API. 目的是让开发者使用socket 去完成应用间的通讯,这篇文章提供了理论上的概述和实际的代码示例。

 

其中包括:

  • socket通讯相关组件的介绍;
  • socket架构的介绍和主要的两个接口类:RSocketServ 和 RSocket;
  • 讨论两端的建立连接的方法;
  • 讨论sockets间通讯的两种模式:连续的数据流或者是分散的消息;
  • 实际的例子讲述了使用活动对象去连接sockets。

 

2 Socket server 结构

     这篇文章表面上看起来象是通过socket server 的应用,使 程序间可以通讯。但是,计算机内部通讯是高度复杂的,并且要理解socket server 在它的涉及到的范围内是高等级的组件,许多低优先级的程序需要请求才能达到它们的目的。

下图表示Socket server 相对于其他 symbian socket 组建的位置。

 

首先要考虑的是传输层协议,上图包括了internet和红外协议,s60系统也支持蓝牙无线技术。

当我们谈论到 internet 协议时,必须依赖互联网服务提供商,所以 symbian 系统支持拨号接入(如图所示),由此,最重要的系统组件是电话服务。最后我们需要让硬件去发送和接收数据通过我们选择的网络。上图表示拨号接入网络使用internet 协议和串口服务,串口服务通过特定硬件驱动告诉硬件并通向外部世界。

3 socket是什么?

什么是Socket?最原始的应用出自伯克利UNIX中的Sockets的实现,socket就是通讯的一端。

哪又代表什么呢?

确切地说,socket 是一个通信隧道的逻辑端点。socket是物理机器网络地址和逻辑端口号的结合,其他的socket就可以与其传输数据。
因为一个socket是由机器地址和端口好来确定的,所以socket在特定的网络里是唯一确定的。这样一个应用就可以唯一确定在网络上要传输数据的另一端。

这一特点也完美的适用于同一个机器上的sockets会话,因为他们虽然有相同的机器地址,但他们的端口号不同。
机器网络地址和端口号的组合取决于协议。原来应用于ip网络上的sockets通讯,但是sockets server 也支持其他的协议,一会儿也将会提到。正如我们将要看到的,我们可以应用高等级的sockets API 而跟我们选择的传输协议无关。

4 协议模型

正如我们描述的,原始的 sockets 在TCP/IP网络的逻辑端点间应用,最著名的TCP/IP协议网络当然是Internet。 最多的sockets使用也都局限于TCP/IP网络中。Symbian系统的sockets server更加通用,其结构支持插件协议模型。这就允许Symbian扩展sockets server 的生命周期和使用范围。
随着新的协议和传输层的引入,sockets server可以适应新协议变化的环境,因为它知道如何使用新语言或协议去建立通讯。
Symbian 系统R5 的sockets server 支持TCP/IP 和IrDA协议,Symbian 系统v6支持蓝牙无线技术和SMS协议。
协议模型是标准的symbian dll,它们的UID2是KUidProtocolModule (0x1000004A),扩展名是*.PRT。
另外,sockets server还支持PLP,PLP是用来在symbian手机和windows建立通讯的协议。PLP 目前应用于symbian连接的效果好于“PsiWin”
Sockets server 可以通过两种方法加载协议模型:
通常的方法是:协议模型在socket打开的时候才被加载。 另外一种方法,应用明确的加载协议模型。这很有用,因为加载协议模型需要大量的时间。做这件事的API我们一会儿也会提到。
一个模型协议可能包含多个协议实现。举个例子,协议模型TCPIP.PRT包含协议UDP,TCP,ICMP,IP & DNS。每个协议都会映射到扩展名为.esk文件中在 /system/data/. 每个协议模型都有.esk文件指出包含的协议和协议的索引。

5 独立传输

正如所述,sockets server 的特点是一个任何时候都支持添加新协议插件的架构。这个结构允许sockets server 实现独立传输的概念。

通过提供一个通用的核心sockets 接口给所有数据传输系统,允许协议模型给 sockets server 添加特殊的功能, 应用程序员可以避免重新实现大量的传输子系统。
不久,一个新的协议出现了,协议模型需要提供接口给这些新的协议。应用程序员只需要添加代码来处理新协议特殊的性能和行为来适用于他们的程序。sockets server 就会用新的协议来处理传输,通过调用下层的传输组件。
总的来说,sockets server 允许应用程序员编写程序传输与很多协议之上但只维护一个API,这样就可以节省很多为特定协议所写的代码。

6Client-server 接口

Symbian 系统的特点是微内核, 只有核心的服务才会合硬件交互控制机器运转核心的部分.大量的系统服务都是通过用户模式的服务线程来提供的,就是我们通常提到的系统服务.sockets server 就是其中的一个系统服务, 应用程序与它交互通过一个公开的客户端接口,其中最重要的4个如下:

  • RSocketServer: 这个类为建立sockets连接建立和获取需要的资源. 在 client-server结构里,这个类代表应用程序与sockets server的连接. 所有其他的客户端接口都需要这个类的实例去打开与服务器端的会话.
  • RSocket: 这个代表一个socket. 一个标准的应用都会有几个RSocket的实例随时调用.
  • RHostResolver: 这个类提供为主机提供一个名字解析的服务接口.
  • RNetDatabase: 这个类提供网络数据库接入的接口.

RSocket, RHostResolver & RNetDatabase都代表与sockets server会话的子会话,即RSocketServer 实例的子会话.

7主sockets服务类

sockets 服务给客户端提供了两个主要的接口类:

  • RSocketServ: 每个应用进程使用sockets 都需要一个这个类的实例来与 sockets server 建立连接Each application thread
  • RSocket : 每个应用程序进程也都需要一个或几个RSocket 对象 作为子会话.

接下来的两段介绍了这两个类的主要特点,他们都是sockets server暴露给客户端的接口。

8使用RSocketServ  

RSocketServ类非常重要,因为它代表了一个客户端应用程序与sockets server的连接。

但是,一个客户端应用程序不会用这个类来直接与远端发送和接受数据。类RSocket用来解决这些问题。
RSocketServ允许客户端应用程序去询问sockets server去确定它知道多少协议,并且返回每个协议的信息。
希望使用sockets的客户端应用程序需要有一个RSocketServ对象的实例,通过它来建立与sockets server 的连接。每个对立的socket都用RSocket的实例来表示。一个应用程序的RSocketServ对象相当于RSocket实例容器的作用。 RSocketServ提供的两个主要的方法是Connect() 和StartProtocol()。

 9与sockets server建立连接

方法Connect() 用来与sockets server 建立应用连接。它需要一个参数-这个会话可用的信息槽个数。


信息槽的个数限制应用程序连接sockets server 同时发起的操作数。所有的异步操作消耗一个信息槽,并在异步操作未解决之前都会占用该信息槽。典型的socket操作异步读写操作都会占用两个信息槽。你不需要添加多余的信息槽,因为该信息槽是又client-server框架提供的。
你只需要决定你的应用程序使用的信息槽,这样你就可以推断出与sockets server总共的会话所需要的信息槽。如果你没有指定任何值,你会得到默认的信息槽个数KESockDefaultMessageSlots (0x08)。

10预加载协议模型

sockets server 将会在第一socket要求给与协议的时候,自动的按需求加载协议模型。但是加载一个协议模型会消耗大量的时间,RSocketServ提供 了一个方法StartProtocol() 用来预先加载协议模型,这样当socket当socket 请求协议的时候它们就已经准备好了。
如果你想让你的应用程序预加载协议模型,而不是当请求的时候来加载,就使用StartProtocol()方法,原型如下:


StartProtocol()方法需要一个参数:协议家族 (e.g.,KAfInet),协议使用的socket方式(e.g.,KSockStream),从协议家族选择的一个协议(e.g.,KProtocolInetTcp),最后一个异步完成的状态。一会儿将会解释这些参数。注意,即使StartProtocol()是一个异步方法,它是不能被取消的。

11使用RSocket

RSocket代表程序中一个单个的soket。应用程序创建的每个单独的socket需要一个RSocket实例。实际上应用程序更多的时候是使用RSocket类而不是RSocketServ类。RSocket是一个提供很多服务的大类,包括:
客户段和服务器段的连接服务。

设置或查询自己的地址,或者是查询远端的地址。
从socket读取数据。

向socket中写数据。
...等等
你在打开任何sockets之前必须有一个激活的RSocketServ会话,并且在做之前的这些操作之前socket必须是打开的。作为打开的socket地一部分,RSocket子会话对象与sockets server会话相关联,sockets server会话是一个RSocketServ类的实例。下面段落介绍的是编写应用程序时涉及到的可用的RSocket方法。

12建立端点间的连接

我们现在知道我们需要一个RSocketServ的实例,作为与sockets server 的会话,并且一些RSocket对象,每个都代表不同的与我们程序通讯的端点。我们现在怎么建立端点与我们程序的通讯并且发送我们想要的东西呢?这取决于以下因素:

第一,我们需要知道端点间的数据流是什么形式的。数据可以通过一系列的字节,流或者是离散的消息(数据报)来传递。流和数据报的区别一会儿将会提到。
第二,在每个端到端的通讯中,一端作为客户端另一端作为服务器端。我们需要知道我们的应用程序在扮演什么角色。我们怎么建立连接取决于我们的角色。
我们马上来看连接和无连接的sockets,流和数据报

 

连接和无连接的sockets?

当我们建立socket,我们指定这个socket操作在连接或者无连接模式。传给RSocket::Open()一个参数就可以做到这一点,一会儿我们将看到。

如果我们在无连接的模式下操作,我们发送一系列独立的消息给指定的地址。这个可以比作通过mail发送明信片给朋友。如果我们在连接的模式下操作,我们维护两个端点间逻辑的连接,在连接的生命周期里发送一个byte的流。远端的地址在连接的期间只被指定一次。其后,当连接打开的时候,我们发送数据的时候不需要再指定地址。这可以比作拿起电话和朋友打电话。
连接实际上就是一系列连接协议地址发送给我们的独立消息。 
无连接的sockets可以广播给多个地址,然而连接的socket这能发送给唯一地址的远端在一个连接中。

 

流和数据报

现在我们创建了一个socket,指定我们要用无连接或连接模式,我们真正指定的是按照数据报或者流去发送数据。

无连接使用数据报。一个数据报是一个单独的消息,我们需要知道传送消息目的的物理地址。发送和接收的端点之间没有建立连接。因为这个原因,你可以将数据报看成是明信片。在明信片上写消息,标明地址,通过mail发出去。当你发送数据报的时候你不知道你想要发送的接受者是否真的接收到消息。

连接的sockets实用流传送。发送流,首先要在两端之间建立一个逻辑连接。有了建立好的连接,我们就可以发送可靠,有序的数据流了。与发送消息相比我们发送一个持续的bytes流。连接的socket可以检测到数据流中的正确与错误,我们还知道数据是否发送到另外的端点。
因为端点间连接的存在,我们不需要每次都指定接受数据的地址。作为内置的错误检测,失去连接也会被检测到。
物理连接这个术语这里用到了几回。这里我们可靠的,有序的数据流实际上是一系列的数据报,承载着在数据流中的位置和其他的控制信息。

13数据报的生命周期

下面的图标表示了用数据报传送数据时的生命周期

注意在客户应用程序和服务器应用之间没有逻辑或者物理的连接。数据流通过一系列离散的消息或数据报传送。数据报是否传送完整或者数据是否结束都被应用程序控制。由TCP/IP协议组提供的数据报协议叫做UDP协议。
应用程序通过应用层协议UDP承载数据报是平常的事。应用层协议能够达到应用要求的复杂程度,但是通常添加了知识和逻辑控制服务。

14创建socket

一旦你的应用程序需要用sockets工作,你总是需要在用之前创建一个socket。RSocket::Open()方法就是用来创建socket的。

正如已经讨论过的,sockets是sockets server 客户端会话的子会话。所以,当你调用RSocket::Open()时,新的socket必须与自己的三服务会话关联。所以Open()方法的第一个参数类型就是RSocketServ&。

接下来,在函数原型中,我们需要指定一个socket使用的地址家族,e.g., 互联网地址家族, 定义常量 KAfInet (0x0800). 接下来的表是可用的协议家族和与他们关联的id:

 我们已经讨论过,sockets可以用连接和非连接模式打开,或者更精确的说传输是通过流或者是数据报的形式。sockType参数就是指定我们想要创建的socket的类型。两个值KSockDatagram或者KSockStream可以用在这里。

最后,我们的socket要用什么协议。协议模型TCPIP.PR实现了一系列协议,比如UDP & TCP,可以人工的指定id:KProtocolInetUdp 或者 KProtocolInetTcp。但是通常是让sockets server自己选择,这里只要指定KUndefinedProtocol就可以了。

KUndefinedProtocol 询问sockets server去创建指定类型的socket,指定socket 类型为固有的选择。最通常的配对为KSockDatagram和KProtocolInetUdp,KSockStream和KProtocolInetTcp。

15绑定socket

Bind() 方法设置socket的地址.

 
如果你的应用程序的socket准备接受数据,你必须设置它的本地地址,这样数据才能正确地从别的socket发送到你的socket。
TCP/IP sockets 作为我们的例子,Bind()在每一ge数据报socket中,无论是客户端还是服务气端,只要接收数据或者监听流的socket中必须被调到。

(注意:一个客户端流socket连接server时不需要绑定,连接server的时候就已经设置本地地址了).
Bind()方法有一个参数-TSockAddr 实例的引用.这个socket 地址需要指定两部分:

联网机器的地址

联网机器的逻辑端口号

同一个机器上的多个sockets具有同一个机器地址和不同的端口号.通配符地址和端口号是允许的.通配符地址绑定到TCP/IPsocket上:

(KInetAddrAny, <specific port number>). 这样,我们就设置地址到我们选择的端口. 通配符地址KInetAddrAny意思就是我们可以发送数据到我们机器地址的指定端口.
注意: KInetAddrAny被定义成互联网地址0.0.0.0

16监听连接

RSocket::Listen()方法用来准备一个socket监听受到的客户端连接请求.它建立一个队列来维护收到的连接请求.

 

不要看函数名,这个函数实际上不监听连接.当客户端向服务器端发送一个连接请求的时候,每个请求都会在队列中占一个位置.队列一旦变满,以后的请求就会被拒绝.一般情况下队列的大小足够了.RSocket::Accept() 方法实际上是等待连接的请求. 接下来会介绍Accept()方法.

注意: 这个处理连接请求的方法与过去用过的其他sockets方案有所不同.其他的socket方案的Listen()方法会一直等待连接的请求.

17接受连接

Accept()方法用来把空的socket与客户端连接请求组对.

如我们之前讨论的,我们写一个流方式的server时最少需要两个sockets.第一socket用来相应客户端请求,这就是调用Listen()和Accept()方法的socket.Accept()异步等待客户端的连接请求.请求将会被放在由Listen()建立的队列中一个信息槽内, Accept()将会从队列中取出这个请求,如果一切顺利的话,将会把请求与空的socket配对.

我们已经看到了怎样通过地址家族和协议来创建一个socket. RSocket::Open()的一个变量创建了一个空的socket, 这个socket没有和特定的协议关联. 空的sockets不能传输数据知道Accept()方法将它与一个客户端配对. 所以, 应用程序需要在调用Accept()之前,预先创建一个空的socket, 为客户端连接做好准备.

监听的socket可以重复使用,并且你的server支持多少就可以建立多少连接. 换句话说, 你可以在同一个socket中重复调用Accept()函数, 假如这里没有显式的调用 Accept(). 但是只要调用Accept()就会请求一个空的socket.完成Accept()请求, 被其占用的队列里的信息槽将会被释放. 因此,大多数的应用, 队列的大小为1就足够了.

18数据报的连接过程

因为数据报sockets在无连接模式下操作,所以不用连接操作。你需要做的是创建和绑定socket,然后就可以发送和接收数据了。

19关闭socket

用RSocket::Open()创建的socket,都要调用RSocket::Close() 以确保所有与socket相关的资源都释放掉。

另外, 当socket连接的时候Close()被调用,socket 将会与远端断开。这个操作将会耗一些时间,取决于协议的要求。注意Close()方法是同步的。如果需要异步的断开连接, RSocket::Shutdown()可以使用。这个方法将在下一节讨论。注意,如果父会话断开,那么所有sockets(子连接)将会断开,所有的子会话将会强制断开。再去对这些socket请求都会导致应用程序panic

20断开socket

如果需要异步的断开一个socket,而不是同步的断开,你就要使用RSocket::Shutdown().

 

这个方法的第一个参数决定断开的模式。TShutdown 是个枚举。可以有4个值 ENormal, EStopInput, EStopOutput 或者 EImmediate.在socket断开连接的时候所有的操作都将会完成。ENormal允许断开按照协议定义的自然结束。EImmediate做最少的操作断开连接。另外,它很有可能部分的断开连接。EStopInput阻止socket接受数据而EStopOutput阻止socket 发送任何数据。注意, 即使使用ENormal, socket还是开的,它仍然是应用RSocketServ会话的一个实例,如果你使用完socket你还需要连外调用RSocket::Close()来释放所有与socket关联的资源。另外注意shutdown请求一但开始就没有方法取消了。

21多线程的socket应用

当你编写sockets应用在其他的操作系统的时候,创建一个线程去创建连接socket是必须的,并且需要控制socket向别的线程传送数据。

 

在v6.0之前的symbian系统,这种多线程的设计对于cs结构是不可能的。任何客户段用来创建线程的会话或子会话都不能被传送到另外的线程。这同样适用于socket,因为sockets是子会话。但是,对于v6.0你可以在线程之间传递sockets。Symbian系统v6.0包括一个增强的cs架构,它可以处理相关客户端的资源拥有关系,sockets服务也提供了会话的分享功能。为了要适用这个功能,你需要在服务端调用RSessionBase::Share()。你就可以创建用于多线程的sockets。通常多线程是不需要的,因为symbian 系统有效处理事件的方法是活动对象。

 

先写道这,希望对看过的人有一些帮助。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值