QUALCOMM BREW has had support for TCP/IP sockets since its inception with the INetMgr and ISocket interfaces. Providing an asynchronous interface similar in use to the Berkeley Socket API, these interfaces (along with the IWeb interface for HTTP support) are the staple of networked applications on BREW handsets today.
But, times are changing. With the release of BREW 3.1.3, QUALCOMM has introduced the ISockPort interface. With support for both IPv4 and IPv6, and an extensible mechanism for selecting both the protocol and the bearer network, the ISockPort interface is the one to use in future applications.
In this article, I show how to use the ISockPort interface to create a simple TCP client application using ISockPort, and direct your attention to some of the new features of the ISockPort interface.
Creating an ISockPort Instance
Although INetMgr is a factory for ISocket interfaces, you create ISockPort instances directly from IShell like any other interface:
result = ISHELL_CreateInstance( pMe->a.m_pIShell, AEECLSID_SOCKPORT, (void **)&pMe->pISockPort );
This operation requires privilege, however. Be sure you have asserted network privileges in your Module Information File, or ISHELL_CreateInstance will return EPRIVLEVEL.
Configuring & Opening an ISockPort Instance
Once the instance is created, you must connect it to the remote server. This involves specifying the address and making the connection; you may also want to select the network.
Specifying the address is easy. Use the AEESockAddrStorage data structure:
#define SERVER_PORT 80 #define SERVER_ADDR "10.0.1.127" pMe->sa.wFamily = AEE_AF_INET; pMe->sa.inet.port = HTONS( SERVER_PORT ); INET_PTON( pMe->wFamily, SERVER_ADDR, &pMe->sa.inet.addr );
The first line indicates the protocol family you’d like to use; the other options include INET_AF_LOCAL for the local domain and INET_AF_INET6 for IPv6. A common mistake when using the network stack on any platform is to map addresses and other information to network byte order; I do this using the HTONS macro when setting the destination address port on the next line. Finally, I convert the address string “10.0.1.127” to the network address in network byte order by using the macro INET_PTON, which converts the address string for the indicated network and address.
If you don’t have the IP address of the server—the usual case, because it’s best to keep host names, not IP addresses, as part of application configuration—simply use INETMGR_GetHostByName to obtain the IP address for a specific hostname.
Note: Like other network operations, INETMGR_GetHostByName is asynchronous. You must provide a callback that the system invokes with the result of your DNS query.
Another thing new to BREW 3.1.3 is the ability to pick the bearer network. Most of the time, you will want to let the handset pick the default bearer network (such as CDMA or UMTS). Occasionally, however, you might want to force the application to use a specific network (such as MediaFLO). To do this, invoke ISOCKPORT_SelectNetwork before connecting or binding the socket. For example,
ISOCKPORT_SelectNetwork( pMe->pISockPort, AEE_NETWORK_WLAN )
would select the WLAN network available to a WLAN-enabled handset for the specific socket.
Finally, it’s time to connect the socket. First, you open the socket:
pMe->wSockType = AEE_SOCKPORT_STREAM; ISOCKPORT_OpenEx( pMe->pISockPort, pMe->sa.wFamily, pMe-> pMe->wSockType, 0 );
And then, you attempt to connect the socket. You must do this asynchronously in case the network stack is busy:
static void TryConnect( CApp *pMe ) { int result = ISOCKPORT_Connect( pMe->pISockPort, &pMe->sa ); switch( result ) { case AEEPORT_WAIT: ISOCKPORT_WritableEx( pMe->pISockPort, &pMe->cbWritable, (PFNNOTIFY)TryConnect, pMe ); break; case SUCCESS: ISHELL_PostEvent( pMe->a.m_pIShell, pMe->a.clsID, EVT_SOCKETCONNECT, 0, 0 ); break; default: ISHELL_PostEvent( pMe->a.m_pIShell, pMe->a.clsID, EVT_SOCKETERROR, ISOCKPORT_GetLastError( pMe->pISockPort), 0 ); } }
This code:
- Attempts to connect the socket.
- If the network stack is busy, registers a callback with the socket to be invoked when the network stack is ready to make the connection.
- If the socket connects, posts the event EVT_SOCKETCONNECT (an application-specific event) to the application to begin its network activity on the newly connected socket.
- If the socket fails to connect, posts the event EVT_SOCKETERROR (another application-specific event) to the application to indicate that an error occurred.
What if you want to listen on a socket instead of connect to a remote server? The flow is similar, but instead of invoking ISOCKPORT_Connect, first invoke ISOCKPORT_Bind to bind the socket to a destination address, and then invoke ISOCKPORT_Listen. These methods should be used asynchronously in the same manner as ISOCKPORT_Connect is. Also, be aware that many operators run networks using Network Address Translation, so there may be no way for nodes off the operator network to connect to your server. Consequently, a better strategy is to use SMS wakeup to instruct the handset to connect to a remote server.
Reading & Writing via the ISockPort Instance
Reading and writing are essentially the same as using an ISocket instance: attempt to read or write, and if an error occurs, determine whether the error is an indication that the operation would block and a callback should be scheduled, or a bona fide error that needs to be handled. For example, to attempt writing:
static void TryWrite( CApp *pMe ) { int result = ISOCKPORT_Write( pMe->pISockPort, pMe->abyWrite + pMe->wWritten, pMe->wWrite - pMe->written ); switch( result ) { case AEEPORT_WAIT: ISOCKPORT_WritableEx( pMe->pISockPort, &pMe->cbWritable, (PFNNOTIFY)TryWrite, pMe ); break; case AEEPORT_ERROR: ISHELL_PostEvent( pMe->a.m_pIShell, pMe->a.clsID, EVT_SOCKETERROR, ISOCKPORT_GetLastError( pMe->pISockPort), 0 ); break; case AEEPORT_CLOSED: ISHELL_PostEvent( pMe->a.m_pIShell, pMe->a.clsID, EVT_SOCKETCLOSED, 0, 0 ); break; default: pMe->wWritten += result; if (pMe->wWritten < pme->pMe->wWrite ) { ISOCKPORT_WritableEx( pMe->pISockPort, &pMe->cbWritable, (PFNNOTIFY)TryWrite, pMe ); } else { pMe->wWritten = pMe->wWrite = 0; } } }
This code attempts to write the contents of pMe->abyWrite in chunks, handling the result values in one of four ways:
- Indications that the write would block cause the write operation to be scheduled with the socket when it’s ready via the call ISOCKPORT_WritableEx.
- An error is propagated to the application’s event handler via ISHELL_PostEvent.
- A socket close is propagated to the application’s event handler via ISHELL_PostEvent.
- If a positive value is returned from ISOCKPORT_Write, that indicates the number of characters written, and the pointer to the region to be written is updated and another write scheduled if there’s more data to write.
Reading data is similar/ However, you likely have additional application-specific logic you need to put into place to determine when you’ve read enough data to process a message.
Shutting Down the ISockPort Instance
Closing the socket is easy. Simply cancel any callbacks associated with the socket and release it, like this:
CALLBACK_Cancel(pMe->cbWritable); CALLBACK_Cancel(pMe->cbReadable); if ( pMe->pISockPort ) { ISOCKPORT_Release( pMe->pISockPort ); pMe->pISockPort = NULL; }
You also could first invoke ISOCKPORT_Shutdown or ISOCKPORT_Close to shut a TCP connection gracefully.
Conclusion
Using the new ISockPort interface is easy if you remember the following key points:
- Use IShell, not INetMgr, to create the instance.
- When specifying an address, be sure to specify the address family as well as the address.
- If necessary, select the bearer network for the socket before attempting to connect.
Related Resource
About the Author
Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today’s wireless devices. Ray Rischpater is the author of several books on software development, including eBay Application Development and Software Development for the QUALCOMM BREW Platform, both available from Apress, and is an active amateur radio operator. Contact Ray at ^212ISockPort213^@lothlorien.com.