X

NV5 Geospatial Blog

Each month, NV5 Geospatial posts new blog content across a variety of categories. Browse our latest posts below to learn about important geospatial information or use the search bar to find a specific topic or author. Stay informed of the latest blog posts, events, and technologies by joining our email list!



From Image to Insight: How GEOINT Automation Is Changing the Speed of Decision-Making

From Image to Insight: How GEOINT Automation Is Changing the Speed of Decision-Making

4/28/2025

When every second counts, the ability to process geospatial data rapidly and accurately isn’t just helpful, it’s critical. Geospatial Intelligence (GEOINT) has always played a pivotal role in defense, security, and disaster response. But in high-tempo operations, traditional workflows are no longer fast enough. Analysts are... Read More >

Thermal Infrared Echoes: Illuminating the Last Gasp of a Dying Star

Thermal Infrared Echoes: Illuminating the Last Gasp of a Dying Star

4/24/2025

This blog was written by Eli Dwek, Emeritus, NASA Goddard Space Flight Center, Greenbelt, MD and Research Fellow, Center for Astrophysics, Harvard & Smithsonian, Cambridge, MA. It is the fifth blog in a series showcasing our IDL® Fellows program which supports passionate retired IDL users who may need support to continue their work... Read More >

A New Era of Hyperspectral Imaging with ENVI® and Wyvern’s Open Data Program

A New Era of Hyperspectral Imaging with ENVI® and Wyvern’s Open Data Program

2/25/2025

This blog was written in collaboration with Adam O’Connor from Wyvern.   As hyperspectral imaging (HSI) continues to grow in importance, access to high-quality satellite data is key to unlocking new insights in environmental monitoring, agriculture, forestry, mining, security, energy infrastructure management, and more.... Read More >

Ensure Mission Success With the Deployable Tactical Analytics Kit (DTAK)

Ensure Mission Success With the Deployable Tactical Analytics Kit (DTAK)

2/11/2025

In today’s fast-evolving world, operational success hinges on real-time geospatial intelligence and data-driven decisions. Whether it’s responding to natural disasters, securing borders, or executing military operations, having the right tools to integrate and analyze data can mean the difference between success and failure.... Read More >

How the COVID-19 Lockdown Improved Air Quality in Ecuador: A Deep Dive Using Satellite Data and ENVI® Software

How the COVID-19 Lockdown Improved Air Quality in Ecuador: A Deep Dive Using Satellite Data and ENVI® Software

1/21/2025

The COVID-19 pandemic drastically altered daily life, leading to unexpected environmental changes, particularly in air quality. Ecuador, like many other countries, experienced significant shifts in pollutant concentrations due to lockdown measures. In collaboration with Geospace Solutions and Universidad de las Fuerzas Armadas ESPE,... Read More >

1345678910Last
«May 2025»
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
14960 Rate this article:
4.0

Server Side TCP/IP Sockets Officially Documented in IDL 8.5

Jim Pendleton

Although the functionality to create a server-side TCP/IP SOCKET has been present since IDL 6.3, until IDL 8.5 it's not been officially documented.

Imagine, for instance, interactively sharing data, graphics, and code with remote users of IDL without leaving the friendly confines of the IDL Workbench.

Despite its stealthy nature, this feature has been used by our Custom Solutions Group for over a decade on a wide variety of projects. Oh yes. It's very nice.

It's even been discussed in passing in a previous IDL Data Point blog and IDL newsgroup public forums over the years, all the way back to 2006.

Establishing a listener is as simple as adding the "new" /LISTEN keyword. The online help is a little cryptic about what this means:

LISTEN

Set this keyword to tell this socket to listen on the specified port.

The general syntax is

SOCKET, listenerLUN, listenerPort, /GET_LUN, /LISTEN

Unlike the low-level BSD sockets, the IDL form of the listener does not allow for the option of multiple simultaneous connection attempts.For the sake of simplicity,

I'm omitting the various *_TIMEOUT keywords. But in the real world, you will always want to set CONNECT_TIMEOUT, READ_TIMEOUT, and WRITE_TIMEOUT to non-zero values any time you create a SOCKET. The default is to set an "infinite" timeout, which makes debugging in particular more difficult than you would generally want.

The listener socket simply waits for attempted connections on the specified port from a client. The listener socket is not the socket that's responsible for the data transfer.

How does your code know when a connection has been attempted from a client? This isn't obvious from the docs, but the trick is to call the FILE_POLL_INPUT function and check for a non-zero status return value. 

status = FILE_POLL_INPUT(listenerLUN, TIMEOUT = .01)

Select your own TIMEOUT value based on your system requirements. This check is generally performed within the context of a TIMER callback so the main IDL thread doesn't need to block until an asynchronous connection attempt is made from a client.

When the status changes to 1, the server side code then makes a second call to SOCKET, passing the logical unit number used in the first SOCKET call as the argument to the new ACCEPT keyword. This keyword is also cryptically documented.

 

ACCEPT

Set this keyword to only accept communications on a specified LUN.

The listener socket essentially "hands off" the actual data communication between the client and server to this second socket.

IF (status) THEN BEGIN

  SOCKET, clientLUN, ACCEPT = listenerLUN, /GET_LUN, /RAW_IO

ENDIF

All your read and write operations will be over this new clientLUN rather than the listenerLUN.

At this point your server is free to respond to additional connection requests on the listener socket. In this way you can potentially connect to multiple clients simultaneously. But recall that IDL's interpreter loop is single-threaded.  Plan your protocol wisely.

If you're exchanging binary data such as floating point numbers or integers across different platforms, you may wish to set the SOCKET keywords /SWAP_IF_BIG_ENDIAN or /SWAP_IF_LITTLE_ENDIAN on either the client or server, depending on your needs.

The two ends of the connection are complete. Now the processes simply need to exchange data according to a protocol of your choosing via WRITEU and READU, or PRINTF and READF.

The transfer protocol generally proceeds with a different TIMER-driven asynchronous monitoring of the data on the socket, with the same FILE_POLL_INPUT idiom used to check for availability.

At the present time, there is no built-in IDL interpreter for HTML (above and beyond the DOM or SAX XML parsers.) Helpers such as the new IDL-Python bridge, also new in IDL 8.5, may provide tools for rapid development.

Particularly when exchanging "large" chunks of data I recommend that you should use the keyword TRANSFER_COUNT to ensure your data buffer has transferred completely. For example if you have a "large" data buffer, the READU operation over the socket is not guaranteed to transfer the entire buffer at one time.

Recall that on the receiver side, you can't READU into an expression like data[tc:*]. Your code needs to account for that as well, building up the final vector in "chunks" as they arrive.

In a scheme like this, your protocol may predefine data sizes to be passed back and forth so sender and receiver agree. Alternatively, the sender must first send over the socket as part of the protocol the length and type of data prior to sending the data in order for the receiver to build the appropriate input buffer before attempting to read into it.

For small-scale tasks that could benefit from IDL services over sockets, this may represent a substantial portion of the solution you need. For large-scale projects, those that require native multi-threading, enhanced security, or the support of specific protocols such as HTTP, the ENVI Services Engine or Jagwire may be more appropriate for exposing ENVI and IDL functionality to TCP/IP clients.

Below are a pair of routines, one that acts a server and another that acts as a client. They can be executed on the same node, but you should run them in two different IDL processes, perhaps one in the Workbench and the other from the command line. You will likely want to set the !DEBUG_PROCESS_EVENTS system variable to 0 if you want to set breakpoints and observe the code behavior. The routines make heavy use of TIMER functionality and there will be unexpected interactions if !DEBUG_PROCESS_EVENTS is 1 and a breakpoint is reached.

If you want to run these examples on separate nodes, modify the SOCKET call in the IDLClient routine to point to the server box instead of 'localhost'.

The server-side code follows:


Pro ClientCallback, ID, H
Compile_Opt IDL2
Catch, ErrorNumber
If (ErrorNumber ne 0) then Begin
    Catch, /Cancel
    ; Unable to send for some reason.  Try HELP, /LAST_MESSAGE
    ; if you want to know why.  Try again.
    !null = Timer.Set(.01, 'ClientCallback', H)
    Return
EndIf
; Send 10,000 random numbers as integers to the client.
Buffer = UInt(RandomU(seed, 1.e5)*5)
WriteU, H['lun'], Buffer, Transfer_Count = TC
If (TC ne 0) then begin
  Flush, H['lun']
  H['bcount'] = H['bcount'] + 1L
  Print, 'wrote buffer to client ', H['bcount']
EndIf Else Begin
  If (TC ne Buffer.Length) then Begin
    Print, 'Only sent ' + TC.ToString()
  EndIf
EndElse
If (H['bcount'] eq 1000) then Begin
  ; Only reply to the first 1000 requests, then close down the socket.
  Free_LUN, H['lun'], /Force
  !null = Timer.Set(.1, 'ListenerCallback', H['listenerlun'])
  Print, 'Closed client socket, listening for new connection requests'
EndIf Else Begin
  !null = Timer.Set(.01, 'ClientCallback', H)
EndElse
End 

Pro ListenerCallback, ID, ListenerLUN
Compile_Opt IDL2
Status = File_Poll_Input(ListenerLUN, Timeout = .1)
If (Status) then Begin
  Print, 'Made a connection, starting client connection.'
  Socket, ClientLUN, Accept = ListenerLUN, /Get_LUN, /RawIO, $
    Connect_Timeout = 30., Read_Timeout = 30., Write_Timeout = 30.
  !null = Timer.Set(.01, 'ClientCallback', Hash('lun', ClientLUN, $
    'bcount', 0L, 'listenerlun', listenerlun))
EndIf Else Begin
  !null = Timer.Set(.1, 'ListenerCallback', ListenerLUN)
Endelse
End


Pro IDLServer
Compile_Opt IDL2
Port = (UInt(Byte('IDL85Rocks'), 0, 2))[1] 
Socket, ListenerLUN, Port, /Listen, /Get_LUN, $
    Read_Timeout = 60., Write_Timeout = 60., /RawIO
!null = Timer.Set (.1, 'ListenerCallback', ListenerLUN)
End

This is the client-side code:


Pro ServerCallback, ID, H
Compile_Opt IDL2
Catch, ErrorNumber
If (ErrorNumber ne 0) then Begin
    Catch, /Cancel
    Help, /Last_Message
    Return
EndIf
If (File_Poll_Input(H['lun'], Timeout = .01)) then Begin
  ; The protocol is simply to get 10,000 integers from the server
  ; with each "read".  The client doesn't send any data to the server.
  BigBuffer = UintArr(1.e5)
  Length = 0L
  CBuffer = BigBuffer
  Repeat Begin 
    ReadU, H['lun'], CBuffer, Transfer_Count = TC
    If (TC ne 0) then Begin
        BigBuffer[Length] = CBuffer[0:TC - 1]
        Length += TC
        If (Length lt BigBuffer.Length) then Begin
            CBuffer = UintArr(BigBuffer.Length - Length)
        EndIf
    EndIf
  EndRep Until Length eq BigBuffer.Length
  Print, 'Got buffer, total = ' + (Total(BigBuffer)).ToString()
  H['bcount']++
  If (H['bcount'] eq 1000) then begin
    ; Got all 1000 expected buffers of 10,000 integers so stop listening for data on the socket.
    Print, 'Received last buffer'
    Free_LUN, H['lun'], /Force
    Return
  EndIf
EndIf Else Begin
  Print, 'no data on socket'
EndElse
; Get the next buffer
!null = Timer.Set(.001, 'ServerCallback', H)
End


Pro IDLClient
Compile_Opt IDL2
Port = (UInt(Byte('IDL85Rocks'), 0, 2))[1]
Socket, ServerLUN, 'localhost', Port, /Get_LUN, Connect_Timeout = 10., $
  Read_Timeout = 10., Write_Timeout = 10., /RawIO
!null = Timer.Set (.001, 'ServerCallback', Hash('lun', ServerLUN, 'bcount',  0L))
End

Start the server as

IDL> idlserver

In the second IDL process, start the client as

IDL> idlclient

For further reading on the topic of exchanging object data between IDL sessions, including a reference example, see an earlier blog post.

Please login or register to post comments.