Adobe
Products
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
Student and Teacher Editions
More products
Solutions
Digital marketing
Digital media
Education
Financial services
Government
Web Experience Management
More solutions
Learning Help Downloads Company
Buy
Home use for personal and home office
Education for students, educators, and staff
Business for small and medium businesses
Licensing programs for businesses, schools, and government
Special offers
Search
 
Info Sign in
Welcome,
My cart
My orders My Adobe
My Adobe
My orders
My information
My preferences
My products and services
Sign out
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out My orders My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Review and Checkout
Adobe Developer Connection / ColdFusion Developer Center /

HTML5 WebSockets and ColdFusion -- Part 2: Coding a Hello World sample and extending it into a chat application

by Kunal Saini

Kunal Saini
  • Adobe

Content

  • HelloWorld – Version 1 : Introducing the CFWEBSOCKET tag
  • HelloWorld – Version 2: Using a channel listener
  • HelloWorld – Version 3: Channel listener and authentication
  • HelloWorld – Version 4: Selective subscription, error handling, and the allowSubscribe() function
  • HelloWorld – Version 5: P2P communication and channel-specific message handlers
  • HelloWorld – Version 6: The invokeAndPublish() and WSGetSubscribers() functions
  • Where to go from here
  • Attributions

Created

21 May 2012

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
application architecture client-server ColdFusion configuration dynamic website enterprise extensibility JavaScript listener P2P performance protocols quick start RIA rich media scalability standards streaming workflow

Requirements

Prerequisite knowledge

A basic understanding of ColdFusion, JavaScript and client-server architecture. Before reading this article, read Part 1 of the series by Awdhesh Kumar.

User level

Intermediate

Required products

  • ColdFusion (Download trial)

Sample files

  • websocket-demo-pt2.zip

HTML5 has gained a lot of traction, with a wide adoption across various industries. The HTML5 WebSocket protocol changes the equation of the traditional HTTP request response model completely by letting you to push data from the server to the client in real time. Yet, understanding the WebSocket protocol and the APIs associated enough to be able to build a full-fledged application remains a challenge.

The HTML5 WebSocket features within ColdFusion 10 completely abstracts the complexity involved in using the HTML5 WebSocket Protocol by helping you build applications easily, and which take advantage of the messaging infrastructure on top of the HTML5 WebSocket protocol. The ColdFusion team implemented the protocol within the ColdFusion 10 server and lets you to just focus on building your application.

WebSocket is a bi-directional, fully-duplex protocol over TCP. ColdFusion 10 empowers you to use WebSockets through its implementation of a messaging layer, making it highly scalable and making it easy to use. ColdFusion 10 provides client-side APIs to work with WebSockets and provides fallback mechanisms for the browsers, which do not support WebSocket natively.

Before proceeding, read HTML5 WebSockets and ColdFusion -- Part 1: An overview and first steps by Awdhesh Kumar. As a review from that article, to start with WebSockets in ColdFusion you must perform the following steps:

1) Specify channel(s) in Application.cfc : WebSocket works on a publish-subscribe model; to leverage that, you must define one or more channels.

2) Define a ChannelListener (optional) : In Application.cfc along with channel, you can define a channel-specific ChannelListener, which extends the base ChannelListener(CFIDE.websocket.ChannelListener) . Use this ChannelListener to control the flow of messages for a given channel.

3) Use the CFWEBSOCKET tag: This tag initializes a WebSocket connection on the client side that will receive, send, and manage the connection with the help of the JavaScript function that is a part of the ColdFusion WebSocket framework.

In this article we start by building a simple Hello World application and extend it step by step to a group chat application, visiting different features of the ColdFusion WebSocket framework along the way.

HelloWorld – Version 1 : Introducing the CFWEBSOCKET tag

To start with this sample application, we use the CFWEBSOCKET tag to develop an application. Version 1 introduces the CFWEBSOCKET tag, initializes the channel, auto-subscribe to a channel, and publish to a channel from the client side.

Step 1: Specifying a channel in the Application.cfc.

Application.cfc

component { this.name = “helloworld"; this.wschannels = [{name="world"}]; }

This code defines a channel named world in Application.cfc. A client can now subscribe and publish to this channel.

Step 2: As channel listener is optional and Application.cfc defines no channel listener, this example uses the base channel listener, CFIDE.websocket.ChannelListener .

Step 3: Create the WebSocket connection using the CFWEBSOCKET tag as follows:

HelloWorld.cfm

<cfwebsocket name="myworld" onMessage="msgHandler" subscribeto="world" onOpen="openHandler"/> <script> var msgHandler = function(message) { // Get data from the recieved message token var data = message.data; if(data) { // If data is present write it to the div var txt=document.getElementById("myDiv"); txt.innerHTML+= data + "<br>"; } } var sayHello = function() { // Client says Hello World myworld.publish("world","Hello World! WebSocket is here !!"); } var openHandler = function() { // do nothing } </script> <input id="hello" type="button" value="Say Hello!" onclick="sayHello();"> <div id="myDiv"></div>

The above HelloWorld.cfm contains the CFWEBSOCKET tag. This tag has an attribute name , which is a JavaScript reference to the WebSocket object that the channel will use to invoke various JavaScript functions. The above code uses the Websocket object myworld to publish a message from the client. Another attribute subscribeto auto-subscribes to the channel named world (defined in Application.cfc). Another attribute onMessage defines a handler to the JavaScript function, which receives all the messages from the server.

When a user lands on the HelloWorld.cfm page, the server creates a WebSocket on the client side and subscribes the user to the channel world . When user clicks the "Say Hello!" button, the client side sends a message through the publish method that the ColdFusion JavaScript API provides.

HelloWorld – Version 2: Using a channel listener

The above example explains how to use auto-subscribe, publishing a message, and receiving the responses from the server. Version 2 introduces the concept of channel listeners where you override the beforePublish() function to modify the message.

Now, if you wants to pre-process the message before publishing it, such as wanting to append a publish time to the original message, you can use channel listeners to do so. To use a channel listener, you must first define it in the Application.cfc. See the modified Application.cfc as follows:

component { this.name = "helloworld"; this.wschannels = [ {name="world", cfclistener="myChannelListener" }]; }

Here, myChannelListener is a CFC that extends the base channel listener, CFIDE.websocket.ChannelListener . The base channel listener has some predefined functions that you can override in the myChannelListener CFC. The following code shows the updated implementation in myChannelListener.cfc

component extends="CFIDE.websocket.ChannelListener" { public any function beforePublish(any message, Struct publisherInfo) { // Show server time also time = DateFormat(now(),"long"); message = time & ": <b>" & message & "</b>"; return message; } }

Here, the code overrides the beforePublish() function to process the message before returning it. Override this method to process the message for all clients. In this case, the code appends date/time to the message.

HelloWorld – Version 3: Channel listener and authentication

Version 3 highlights another predefined function in the base channel listener, beforeSendMessage() function and explains how it is different from the beforePublish() function explained in version 2. This example also shows you how to use authentication with WebSockets.

Version 3 upgrades the version 2 of the application to show the user name along with the hello world message. This require that you authenticate the user (the client) first and then store the username in a struct connectioninfo .

The connectioninfo struct stores all the information about an individual client. You can modify the connection info in Application.cfc and share it across all the channels for the given client.

To enable authentication in WebSocket, you must implement the onWSAuthenticate() function in the Application.cfc file. Add the onWSAuthenticate() function to the Application.cfc as follows:

function onWSAuthenticate(String username, String password, Struct connectionInfo) { // Put some authentication logic here (from LDAP or DB) // Adding the user name to the connection info connectionInfo.username=username; return true; }

The code above passes the connectionInfo struct to the function onWSAuthenticate() . You can use the connectionInfo object to store any client-specific data, which you can retrieve later (through an in-channel listener).

To call the defined onWSAuthenticate() method, the user must call the WebSocket JavaScript function authenticate() from the HelloWorld.cfm. Update this sayHello() function to authenticate, as follows:

var sayHello = function() { uname = document.getElementById("username").value; // Calling authenticate from client side. Calling this //function will invoke onWSAuthenticate from Application.cfc myworld.authenticate(uname,"password"); // Client says Hello World myworld.publish("world","Hello World! WebSocket is here !!"); }

Add the text field to take the user name, as follows:

<input id="username" name="username" type="text" value="admin" >

To make a message unique (by adding user name) for each client, override the beforeSendMessage() method in myChannelListener, as follows:

public any function beforeSendMessage(any message, Struct publisherInfo) { message = "<b>" & publisherInfo.connectionInfo.username & " :</b>" & " <i>" & message & "</i>"; return message; }

The difference between the beforePublish() and beforeSendMessage() functions is as follows: For any message to be sent, the code calls the former once and calls the latter for each and every client. So if there are 10 clients connected and subscribed to the channel 'world', the code calls the beforePublish() once and calls beforeSendMessage() 10 times.

Note: The connectionInfo object that you have updated in the onWSAuthenticate() function is a part of publisherInfo object passed to the beforeSendMessage() function.

HelloWorld – Version 4: Selective subscription, error handling, and the allowSubscribe() function

Version 4 explores selective subscription, error handling, and yet another pre-defined function allowSubscribe() in the base channel listener.

Selective subscription

Version 4 enhances the above application further to allow users to publish/subscribe only if they accept certain "terms and conditions." Since this is a selective subscription, the code does not subscribe users with the CFWEBSOCKET tag, rather a user subscribes explicitly along with information as to whether he/she accepted the proposed terms and conditions.

To enable this feature, you must modify HelloWorld.cfm slightly. The code adds a check box and button to accept terms and conditions. The CFWEBSOCKET tag does not subscribe to the channel by using the subscribeto attribute; there are two ways to subscribe to the channel—through using the CFWEBSOCKET tag, and the other by using the client-side API. This sample also defines an error handler to handle any error messages.

<cfwebsocket name="myworld" onMessage="msgHandler" onOpen="openHandler" onError="errorHandler"/>

Version 4 uses a new JavaScript function subscribe() to explicitly subscribe the user. While subscribing, the function passes some extra information about the user's agreement to the terms and conditions. To subscribe the user explicitly, use the WebSocket JavaScript function subscribe() with the iAgree() function. Here is the the iAgree() function:

var iAgree = function() { var chkbox= document.getElementById("check"); // Explicitly subscribe here with additional information myworld.subscribe("world",{agree:chkbox.checked}); }

In the above code snippet, the subscribe calls the first argument, which is the channel you wish to subscribe to. The second optional argument (a JavaScript object) contains the information about terms and conditions, which in this case has a value of "check." If you provide this second argument, the client send the information across the ColdFusion server along with the subscribe request. This extra information is available as subscriber information to some of the channel listener functions.

Error handling

When errors occur in WebSocket communication, ColdFusion calls the error handler callback that you have defined in the CFWEBSOCKET tag. the following defines the error handler implementation:

var errorHandler = function(err) { var txt=document.getElementById("myDiv"); txt.innerHTML= "<font size='5' color='red'><b>" + err.msg + "</b></font><br>" }

The allowSubscribe() function

On the channel listener side, this code overrides another function allowSubscribe() . The server uses the allowSubscribe() function to control client subscriptions. Here, if the user has agreed to terms and conditions, he/she can subscribe to the channel. If the user declined the terms and conditions, the channel declines his subscription request and displays a predefined error message from allowsubscribe().

The code below shows how to override the allowSubscribe() function in myChannelListener.cfc.

public boolean function allowSubscribe(Struct subscriberInfo) { // If user has agreed on terms and conditions, // return true if(subscriberInfo.agree) return true; return false; }

The function above returns true if the user has agreed to terms and conditions and allows the user to subscribe. This function also sends an acknowledgement of subscription success to the message handler msgHandler . If the allowSubscribe () function returns a value of false/true, the channel sends an error message to error handler defined in HelloWorld.cfm.

HelloWorld – Version 5: P2P communication and channel-specific message handlers

Version 5 highlights point-to-point (P2P) communication and channel-specific message handler. Version 5 enhances the existing application to show a user how long he/she has been logged in. This information is specific to each individual user. To achieve this, you can take advantage of using another WebSocket JavaScript function invoke() that ColdFusion exposes for your easy use. When you call the invoke() function, you initiate a P2P communication between client and server. The invoke() function calls a function defined in a CFC; this function will keep returning the time value—this is a problem because it will be difficult to handle both types of message from a single-message handler. The invoke() function returns the time value again and again to the message handler. To make things clear, you must define a different message handler for the channel world while subscribing.

In HelloWorld.cfm, the CFWEBSOCKET tag uses a default message handler that updates a div, as follows:

<cfwebsocket name="myworld" onMessage="defaultmsgHandler" onOpen="openHandler" onError="errorHandler"/>

Here is implementation of defaultmsgHandler :

var defaultmsgHandler = function(message) { // Get data from the recieved message token var data = message.data; if(data) { // If data is present write it to the div var txt=document.getElementById("timeCounter"); txt.innerHTML= "You have Logged in for <b>" + data + "</b> sec<br>"; } }

The code above displays the time since a user has logged in.

You can define a channel-specfic message handler at the time of subscribing to a channel itself. While subscribing, you use the iAgree() function, and within this function, you can pass a third argument that refers to the channel-specific message listener. In this case, we have passed the argument to msgHandler . Now any response for the channel world comes to msgHandler and not to defaultmsgHandler .

var iAgree = function() { var chkbox= document.getElementById("check"); // Explicitly subscribe here and defining custom message handler // This message handler will now receive messages for the channel world myworld.subscribe("world",{agree:chkbox.checked},"msgHandler"); myworld.invoke("invoke","getLoginTime"); }

Here is the implementation of channel-specific message handler:

var msgHandler = function(message) { // Get data from the recieved message token var data = message.data; if(data) { // If data is present write it to the div var txt=document.getElementById("myDiv"); txt.innerHTML+= data + "<br>"; } }

Also notice that within the iAgree() function, you call Websocket JavaScript function invoke() . This invoke() function invokes function getLoginTime(), defined in invoke.cfc. Here is the implementation for invoke.cfc.

component { public function getLoginTime() { uuid = CreateUUID(); // Unique name for each thread thread action="run" name=uuid { i=0; while(i <100){//running for 100 sec only sleep(1000); i = i +1; WsSendMessage(i); } } } }

The getLoginTime() function executes the ColdFusion server-side Websocket function WsSendMessage() . The WsSendMessage() function returns data to the client who has invoked this function. You call the WsSendMessage() function from inside a thread so that you can run it concurrently for different clients.

HelloWorld – Version 6: The invokeAndPublish() and WSGetSubscribers() functions

Version 6 highlights the client-side WebSocket JavaScript function invokeAndPublish() and the server-side ColdFusion function WSGetSubscribers() .

Version 6 adds functionality to the application to show how many users are online (subscribed) along with their names. To implement this feature, the application developer must have information about all subscribers. ColdFusion 10 provides a function called WSGetSubscribers(), which provides information about all the clients subscribed to a channel. This function returns an array of struct. This struct contains the subscriber's information, including channel name the subscriber subscribed to, and connection information.

Note: In earlier examples we have modified connection name to add username to it.

Below is the getallclients.cfc, which uses the WSGetSubscribers() function to fetch usernames subscribed to the channel world .

component{ public function getSubInfo() { info = ArrayNew(1); subInfo = WSGetSubscribers("world"); getMappings = function(obj){ ArrayAppend(info, obj["subscriberinfo"]["connectioninfo"]["USERNAME"]); }; // Using closure to get subscriber info ArrayEach(subInfo,getMappings); return info; } }

In the above code, the username is part of the connectioninfo struct, which in turn is a part of the sunscriberinfo struct. Whole functionality has been encapsulated in a CFC so that you can invoke it from the client side.

Modify HelloWorld.cfm to invoke function getSubInfo() from getallclints.cfc . The defaultmsgHandler JavaScript function calls invokeAndPublish to invoke getSubInfo() . Also, I moved invoke() from iAgree() to defaultmsgHandler() . The reason for this change is that all of these WebSocket JavaScript functions are asynchronous calls, and in iAgree() we call subscribe() , whose acknowledgment defaultmsgHandler() receives. Once the channel confirms subscription( in defaultmsgHandler() ) then only it calls invoke and invokeAndPublish.

Following is the implementation of defaultmsgHandler:

var defaultmsgHandler = function(message) { // Get data from the recieved message token var data = message.data; var reqType = message.reqType if(reqType) { if(reqType.indexOf("subscribe")!=-1) { // Now calling invoke and invokeAndPublish myworld.invoke("invoke","getLoginTime"); myworld.invokeAndPublish("world","getallclints", "getSubInfo"); } } if(data) { // If data is present write it to the div var txt=document.getElementById("timeCounter"); txt.innerHTML= "You have Logged in for <b>" + data + "</b> sec<br>"; } }

The code passes an acknowledgement of subscribe to defaultmsgHandler() that the request type is "subscribe." The response of invokeAndPublish comes to the message handler defined for the channel world, in other words, on msgHandler() . Here is updated version of msgHandler() that shows currently logged-in users.

var msgHandler = function(message) { // Get data from the recieved message token var msg = message.data; if(msg instanceof Array) { var usrtxt=document.getElementById("users"); usrtxt.innerHTML = "Following users are also logged in: "; for (a=0;a< msg.length;a++){ usrtxt.innerHTML +="<b>" + msg[a] + "</b> |"; } usrtxt.innerHTML += "<br>"; } else { // If data is present write it to the div var msgtxt=document.getElementById("myDiv"); msgtxt.innerHTML+= msg + "<br>"; } }

Where to go from here

This tutorial showed how you could create a basic group chat application. You can enhance this application further using other WebSocket features. This article covered basic building blocks to write an application using WebSockets. Refer to the ColdFusion 10 Help documentation for more information on WebSockets.

This quick start with WebSockets showed you how to write a basic hello world program and enhance it into a small group chat application. While enhancing the application, we visited different concepts of WebSockets like using a channel listener to control the flow of messages, P2P communication through the invoke() function, client-side broadcast using publish() and invokeAndPublish() functions, selective subscription, and authentication. For further reading please see ColdFusion team developer, Evelin Varghese's blog.

Attributions

I would like to acknowledge the help of the following people and resources that contributed to this article:

  • Awdhesh Kumar, Computer Scientist, ColdFusion product team
  • Evelin Varghese, QA, ColdFusion product team

Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License+Adobe Commercial Rights

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

More Like This

  • ColdFusion 8 server monitoring – Part 3: Automated monitoring and request management with Alerts and Snapshots
  • Object-oriented programming: Using inheritance wisely
  • Moving from Dreamweaver to ColdFusion Builder 2
  • ColdFusion 8 server monitoring – Part 4: Multiserver Monitor, Admin API monitoring, and more
  • Managing Adobe AIR updates with ColdFusion 8
  • Securing your applications using HttpOnly cookies with ColdFusion
  • Manually configuring Internet Information Services 7 connector with ColdFusion
  • Using Flash Builder and ColdFusion Builder in an integrated workflow
  • Enabling multiple user access to the ColdFusion Administrator and RDS
  • ColdFusion 8 performance monitoring

Tutorials & Samples

Tutorials

  • Using Axis2 web services with ColdFusion 10
  • Serving HTML5 videos with ColdFusion 10
  • HTML5 WebSockets and ColdFusion -- Part 2

Samples

ColdFusion Blogs

More
07/06/2012 Adobe ColdFusion 10 on CIO.com
06/22/2012 Elishia Dvorak Joins as ColdFusion Solution Consultant and Product Evangelist
06/19/2012 Outstanding contributions to the ColdFusion 10 and ColdFusion Builder 2.0.1 pre-release
06/18/2012 CF html to pdf service - consume from node.js using rest api

ColdFusion Cookbooks

More
04/01/2012 Send multiple mails with the adresses from database
07/27/2011 Passing a list with with STRING values
05/27/2011 AUTOMATED SANITIZED Resultset with ColdFusion
03/16/2011 Using Metadata To Add Static Variables to ColdFusion Components

Products

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • Mobile Apps
  • Photoshop
  • Touch Apps
  • Student and Teacher Editions

Solutions

  • Digital marketing
  • Digital media
  • Web Experience Management

Industries

  • Education
  • Financial services
  • Government

Help

  • Product help centers
  • Orders and returns
  • Downloading and installing
  • My Adobe

Learning

  • Adobe Developer Connection
  • Adobe TV
  • Training and certification
  • Forums
  • Design Center

Ways to buy

  • For personal and home office
  • For students, educators, and staff
  • For small and medium businesses
  • For businesses, schools, and government
  • Special offers

Downloads

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy Policy and Cookies (Updated)

Ad Choices

Reviewed by TRUSTe: site privacy statement