English

Error: Avoiding firewall port timeouts between ArcIMS and ArcSDE

Error Message

After a period of inactivity, the first request of a map service generates an error and subsequent requests are successful. The logfiles report Network I/O Errors.

Cause

The problem is caused by a firewall closing inactive communication ports. If an application opens a communication port, the firewall will only keep the port open for a limited time and the clock restarts with the conclusion of each communication. ArcIMS does not recieve notification of the closed port and thus, after a period of inactivity, when the first request comes in, ArcIMS attempts to use an inactive port to communicate with ArcSDE, an error is returned and upon the next request, ArcIMS opens a new port.

Solution or Workaround

A number of solutions are listed below.

  • Change firewall settings to keep the port open.
    • Work with your network administrator to change the firewall settings so that the firewall does not kill idle connections between ArcIMS and ArcSDE. You should let the administrator know the machine names and/or ip addresses of each server machine that must communicate across the firewall. By default, an ArcSDE instance listens on port 5151. With this information, your network administrator can either set a longer timeout or remove any timeout.
  • Move the ArcIMS application server and spatial server inside the firewall.
    • As a general rule, avoid placing a firewall between the ArcIMS application and spatial server and the ArcSDE data server. You can keep the web server and any connectors (e.g. Servlet Connector, ActiveX Connnector, Java connector, ColdFusion connector) outside the firewall but move ArcIMS inside the firewall. The connectors do not require a connection to be kept open since they will open connections as required (via port 5300 by default). This type of distributed configuration can become complicated in that it will require the web server to have continued access to the output folder. Most people, in this sort of configuration, deploy the output folder on the webserver and open a one-way hole in the firewall from the ArcIMS spatial server to the web server.
  • Spatial server recycling to close the port and notify ArcIMS that the port has been closed.
    • ArcIMS can be configured to restart the spatial servers on a regular basis. Restarting the spatial server will close the idle firewall port and the next request will open a new port. If you are using ArcIMS 4.0, refer to Knowledge Base Article 22891 for information on configuring recycling. If you are using ArcIMS 4.0.1 or 9.0, refer the the ArcIMS Help topic "Enabling server recycling".
  • Ping the spatial server on a regular basis to keep the port open.
    • Pinging the ArcIMS server on a regular basis will pass the data request onto the ArcSDE server and the firewall port will remain active and open and avoid the timeout errors described above.

      To Ping the ArcSDE server, you must send a request to each virtual server that accesses ArcSDE data. For example, if your application only displays map images, it will suffice to send only a GET_IMAGE request; if your application queries ArcSDE data, you will also need to send a GET_FEATURES request; if your application geocodes against ArcSDE data, you will need to send a GET_GEOCODE request; and if your application uses an extract server to extract ArcSDE data, you will need to send an GET_EXTRACT request.

      A request can be contructed using the API of one of the Connectors or by sending raw ArcXML to a map service. Links in the related documents section provide a solution using the Java Connector (see How To: Use the Java Connector to Send Automated Requests to ArcIMS) and the ActiveX Connector:
       
      Option Explicit
      
      ' modify the constants below to correspond with your mapservice
      ' run from a command line with the:
      ' cscript //nologo <scriptname>.vbs
      ' for example, if you save the script as KeepOpen.vbs...
      ' cscript //nologo KeepOpen.vbs
      
      
      Const SERVER_NAME = "localhost"
      Const SERVICE = "SanFrancisco"
      Const SERVER_PORT = 5300
      
      Const QUERY_LAYER = "highways"
      Const QUERY = "#ID# = 1"
      
      Const GEO_LAYER = "streets"
      Const GEO_ADDRESS = "1575 Ellis St"
      Const GEO_ZONE = "94115"
      Const GEO_XSTREET = ""
      
      Dim ping1
      Set ping1 = New Pinger
      ping1.Initialize SERVER_NAME, SERVICE, SERVER_PORT
      
      WScript.Echo ping1.PingImageServer
      WScript.Echo ping1.PingQueryServer (QUERY_LAYER, QUERY)
      WScript.Echo ping1.PingGeocodeServer (GEO_LAYER, GEO_ADDRESS, GEO_ZONE, GEO_XSTREET)
      WScript.Echo ping1.PingExtractServer
      
      ' ************************************************************
      
      Class Pinger
      	
      	Private initialized
      	Private Cn, map
      	Private mapService
      	
      	Private Sub Class_Initialize()
      		initialized = False
      	End Sub
      	
      	Private Sub Class_Terminate()
      		Set Map = Nothing
      		Set Cn = Nothing
      	End Sub
      	
      	Public Sub Initialize(server, service, port)
      		mapService = service
      		Set Cn = CreateObject("aims.ArcIMSConnector")
      		Cn.ServerName = server
      		Cn.ServerPort = port
      		Set map = CreateObject("aims.Map")
      		map.InitMap Cn, mapService
      		WScript.Echo "map initialized"
      		initialized = True
      	End Sub
      	
      	
      	Function PingImageServer()
      		If Not initialized Then
      			NotInitialized
      			Exit Function
      		End If
      		PingImageServer = map.GetImageAsUrl 
      	End Function
      	
      	
      	Function PingQueryServer(layerName, where)
      		If Not initialized Then
      			NotInitialized
      			Exit Function
      		End If
      
      		Dim activeLayer
      		Set activeLayer = getLayer(layerName)
      
      		Dim Rs
      		Set Rs = activeLayer.Recordset
      		Rs.Filter.WhereExpression = where
      		
      		Dim count
      		count = -1
      		
      		If Rs.MoveFirst() Then
      			count = Rs.Count
      		End If
      
      		PingQueryServer = count
      
      	End Function
      
      
      
      	Public Function PingGeocodeServer(layerName, address, zone, crossStreet)
      
      		If Not initialized Then
      			NotInitialized
      			Exit Function
      		End If
      
      		' get the streets layer for the geocoding
      		Dim streetsLayer
      		Set streetsLayer = getLayer(layerName)
      
      		Dim ami
      		Set ami = streetsLayer.AddressMatchInputs
      		If ami.MoveFirst() Then
      			Do
      				If ami.Id = "STREET" AND address <> "" Then
      					ami.Value = address
      				End If
      				If ami.Id = "ZONE" AND zone <> "" Then
      					ami.Value = zone
      				End If
      				If ami.Id = "CROSSSTREET" AND crossStreet <> "" Then
      					ami.Value = crossStreet
      				End If				
      			Loop While ami.MoveNext()
      		End If
      		ami.AddressMatch 1, 25
      
      		Dim amr
      		Set amr = ami.AddressMatchResults
      		
      		Dim x,y
      		If amr.MoveFirst() Then
      			x = amr.Point.x
      			y = amr.Point.y
      		End If
      
      		PingGeocodeServer = x & ", " & y
      
      	End Function
      	
      	
      	
      	Public Function PingExtractServer()
      		If Not initialized Then
      			NotInitialized
      			Exit Function
      		End If
      	
      		map.Layers.NoDefault = True
      		If Not map.IsMapExtractable Then
      			WScript.Echo "No extractable layers"
      			Exit Function
      		End If
      		map.Extract = True
      		map.Refresh 
      		PingExtractServer = map.GetExtractUrl
      	End Function
      	
      	
      	' get a layer object corresponding to the input layer name
      	Private Function getLayer(layerName)
      		Dim i
      		i = getLayerIndex (layerName)
      		Set getLayer = map.Layers.Item(i)
      	End Function
      
      
      	' Get a layernumber corresponding to the input layername
      	Private Function getLayerIndex(layerName)
      		Dim i, r
      		For i = 1 To map.Layers.Count
      			If LCase(map.Layers.Item(i).Name) = LCase(layerName) Then
      				getLayerIndex = i
      			End If
      		Next
      	End Function	
      	
      	Private Sub NotInitialized()
      		WScript.Echo "Connection Not Initialized, Call obj.Initialize server, service, port"
      	End Sub
      	
      End Class
      
      

      The ActiveX Connector example can be executed on a regular interval using the Windows Scheduled Task Manager, or, if you are using UNIX, a cron job.

Related Information