1. 图形客户端

任何图形程序的一个重要目标是维护一个响应式的用户界面。许多常见的图形化编程工具包的事件循环是单线程运行的,任何应用程序代码的延迟都会对用户体验产生显著的负面影响。应用程序通常别无选择,只能为可能阻塞的任何操作,创建单独的线程。

默认情况下,Ice调用使用类似于常规进程内函数调用的同步语义:调用线程阻塞直到调用完成。然而,由于Ice在后台执行网络活动,同步的Ice调用会带来额外的阻塞风险。举个例子,Ice运行时可能需要建立到服务器的连接,这可能需要一些时间。即使由于较早的调用而已经建立了连接,通过打开的连接发送消息也可能意外地阻塞(例如,由于网络中的问题)。

像图形聊天客户端这样需要非阻塞语义的程序必须使用Ice的异步编程模型(称为异步方法调用(AMI))。虽然这个模型需要一些额外的工作,但好处是值得的:Ice保证异步调用永远不会阻塞调用线程。因此,图形应用程序可以安全地从事件循环线程调用Ice,而不会对用户界面产生负面影响。

Ice提供Dispatcher工具,让应用程序控制执行请求派发的线程。聊天示例中的图形客户端使用此功能,以便ChatRoomCallback对象上的调用可以直接更新用户界面。

1.1. 创建一个回话

推客户端的首要任务之一是与Glacier2路由建立会话。这个过程通过使用Ice中包含的Glacier2辅助类来大大简化,我们的图形客户端大量的使用这些类。(下面显示的代码是从Java图形客户端调整的,但是C#和C++代码是相似的。)

在创建会话之前,我们需要配置调度程序,以确保在UI线程中执行请求调度:

initData.dispatcher = (runnable, connection) -> SwingUtilities.invokeLater(runnable);

如你所见,调度程序实现只是委托给Swing的invokeLater函数。

接下来,客户端创建一个Glacier2.SessionFactoryHelper的实例并传递一个回调对象:

Glacier2.SessionCallback callback = ...;
_factory = new Glacier2.SessionFactoryHelper(initData, callback);

调用回调函数,来通知应用程序有关Glacier2会话生命周期中的重大事件。

现在我们可以使用factory来创建一个会话:

Glacier2.SessionHelper session = _factory.connect(userName, password);

客户端调用connect来处理用户提供的帐户信息。返回值是一个新的Glacier2.SessionHelper对象,它负责管理会话并在必要时调用客户端的SessionCallback对象。例如,由于建立会话异步发生,以避免阻塞调用connect的线程,所以客户端不能开始使用会话,直到调用其connected的回调方法来指示请求成功完成:

public void
connected(SessionHelper session)
    throws SessionNotExistException
{
    // ...
    _chat = Chat.ChatSessionPrx.uncheckedCast(_session.session());
    Chat.ChatRoomCallbackPrx callback = Chat.ChatRoomCallbackPrx.uncheckedCast(
        _session.addWithUUID(new ChatRoomCallbackI(coordinator)));
    _chat.setCallbackAsync(callback).whenCompleteAsync(...);
}

SessionHelper方法session返回新创建的Glacier2会话对象的代理。我们将这个代理下载到我们前面看到的派生接口Chat::ChatSession

接下来,代码创建将从聊天室接收事件的回调对象。有很多事情发生在这段简短的代码中。首先,我们实例化我们的回调服务类ChatRoomCallbackI,并将其传递给addWithUUID方法。这个方法通过对象适配器特别是回调对象来注册我们的服务,如果他不存在,则创建对象适配器。addWithUUID还确保与服务关联的对象标识,包含一个UUID,并符合Glacier2对回调对象的约定。最后,我们使用一个未经检查的转换将得到的回调代理缩小到Chat::ChatRoomCallback类型。

最后一步是加入聊天室,我们通过在会话上调用setCallback来完成聊天室。我们在这里使用异步调用来避免阻塞。

1.2. .NET客户端

.NET聊天客户端的用户界面是使用Windows Presentation Foundation(WPF)构建的。聊天客户端的图形元素是使用可扩展应用程序标记语言(XAML)来安排的,而应用程序逻辑是用C#实现的。为了避免阻塞.NET的事件循环,程序使用异步调用与聊天服务器进行通信。例如,下面的代码显示了,客户端如何调用发送操作的异步版本,来发布新类型的聊天消息:

public async void sendMessage(string message)
{
    ...
    try
    {
        long timestamp = await _chat.sendAsync(message);
        userSayEvent(timestamp, _username, message);
    }
    catch(Chat.InvalidMessageException ex)
    {
        appendMessage("<system-message> - " + ex.reason + Environment.NewLine);
    }
    catch(Exception)
    {
        destroySession();
    }
}

sendAsync方法接受聊天消息作为唯一的参数,并返回一个.NET任务。然后,代码等待任务和.NET将安排任务完成后继续运行。完成后,我们像调用同步一样调度事件或处理错误。对于sendAsync的调用是由WPF线程构成的,继续将使用WPF线程执行,因为WPF SynchronizationContext使用其调度程序调度延续,因此可以从此处修改GUI组件。

1.3. C++客户端

C++聊天客户端的用户界面是使用Qt框架构建的。为了避免阻塞Qt的事件循环,程序使用异步调用来与聊天服务端通信。例如,下面的代码显示了客户端如何调用发送操作的异步版本,来发布新类型的聊天消息:

shared_ptr<ChatSessionPrx> session = ...
string message = ...
chat->sendAsync(message, [this, message](long long timestamp)
                         {
                             _coordinator->userSayEvent(timestamp, _username, message);
                         },
                         [this](std::exception_ptr eptr)
                         {
                             try
                             {
                                 rethrow(eptr);
                             }
                             catch(const Chat::InvalidMessageException& e)
                             {
                                 ostringstream os;
                                 os << " - " << e.reason;
                                 _coordinator->appendMessage(os.str());
                             }
                             catch(const Ice::Exception&)
                             {
                                 _coordinator->destroySession(ex);
                              }
                         });

调用成功完成时,Ice调用response回调;该方法的参数对应于Slice操作的返回值和输出参数。在上面的例子中,response接收服务端分配的时间戳。请注意,responseexception回调是在UI线程中执行的,因为我们安装了一个Dispatcher,所以这些函数修改用户界面是安全的。

Copyright © github.com/weiofcn 2017 all right reserved,powered by GitbookLast modified time: 2017-12-27 02:20:43

results matching ""

    No results matching ""