coldfusiontelnetcfmllucee

How do I communicate over telnet using cfml?


I have a game server that communicates only via telnet. I have a Lucee server running in the same server room, and wanted to write a small web app that could send commands to the game server. Seemed simple enough, but tens of hours later I had experienced a lot of coming up empty googling and various attempted approaches (like tst10) that didn't work right.


Solution

  • I eventually got this to work by using Java to establish and manage the telnet connection, with a database table as a go-between so my main thread could see what the telnet connection was seeing. My working code is below.

    This code needs substantially cleaned up, but is a working proof of concept showing the basics of how it can be done. Hopefully others that have the same need and have been banging their head against the wall find it useful.

    <cfcomponent>
    
        <cffunction returntype="Struct" name="telnetConnect" access="public" output="no">
            <cfargument name="SID"          type="numeric"  required="yes">
            <cfargument name="telnetAdd" type="string" required="yes">
            <cfargument name="telnetPort" type="numeric" required="yes">
    
            <cfset retVal           = structNew()>
            <cfset retVal.success   = true>
    
            <cftry>
                <cfparam name="application.telnet" default="#arrayNew()#">
                <cfparam name="application.telnet[arguments.SID]" default="#structNew()#">
                <cfparam name="application.telnet[arguments.SID].telnetClient" default="">
                <cfparam name="application.telnet[arguments.SID].lastData" default="#Now()#">
    
                <cfset application.telnet[arguments.SID].telnetData = "">
                <cfset application.telnet[arguments.SID].isReading  = false>
    
                <cfscript>
                    // If there is an existing connection for this server; close it.
                    telnetClose(arguments.SID);
                    sleep(25);
    
                    // Load the Apache Commons Net TelnetClient class
                    application.telnet[arguments.SID].telnetClient = createObject("java", "org.apache.commons.net.telnet.TelnetClient").init();
    
                    // Connect to the Telnet server
                    application.telnet[arguments.SID].telnetClient.connect(arguments.telnetAdd, arguments.telnetPort);
    
                    // Get input and output streams
                    application.telnet[arguments.SID].inputStream = application.telnet[arguments.SID].telnetClient.getInputStream();
                    application.telnet[arguments.SID].outputStream = application.telnet[arguments.SID].telnetClient.getOutputStream();
                </cfscript>
    
                <cfcatch>
                    <cfset retVal.success   = false>
                    <cfset retVal.cfcatch   = cfcatch>
                </cfcatch>
            </cftry>
    
            <cfreturn retVal />
        </cffunction>
    
        <cffunction returntype="Struct" name="telnetSend" access="public" output="no">
            <cfargument name="SID"          type="numeric"  required="yes">
            <cfargument name="message"      type="string"   required="yes">
    
            <cfset retValSend           = structNew()>
            <cfset retValSend.success   = true>
    
            <cftry>
    
                <cfscript>
                    command = "#arguments.message##chr(13)#";
                    application.telnet[arguments.SID].outputStream.write(command.getBytes());
                    application.telnet[arguments.SID].outputStream.flush();
                </cfscript>
    
                <cfcatch>
                    <cfset retValSend.success   = false>
                    <cfset retValSend.cfcatch   = cfcatch>
                </cfcatch>
            </cftry>
    
            <cfreturn retValSend />
        </cffunction>
    
        <cffunction returntype="Struct" name="telnetRead" access="public" output="no">
            <cfargument name="SID" type="string" required="yes">
    
            <cfset var retVal = structNew()>
            <cfset retVal.success = true>
    
            <cftry>
                <cfset application.telnet[arguments.SID].isReading  = true>
    
                <cfscript>
                    // Create an InputStreamReader from the InputStream
                    inputStreamReader = createObject("java", "java.io.InputStreamReader").init(application.telnet[arguments.SID].inputStream, "UTF-8");
                    
                    // Create a char array for reading
                    charBuffer = createObject("java", "java.lang.reflect.Array").newInstance(createObject("java", "java.lang.Character").TYPE, 1024);
                    charsRead = 0;
                    totalRead = 0;
                    maxRead = 5120; // Max chars to read at a time
                </cfscript>
    
                <cfloop condition="totalRead lt maxRead">
                    <cfset charsRead = inputStreamReader.read(charBuffer, 0, 1024)>
                    <cfif charsRead is -1>
                        <cfbreak>
                    </cfif>
    
                    <cfset dataChunk = createObject("java", "java.lang.String").init(charBuffer, 0, charsRead)>
                    <cfset totalRead += charsRead>
    
                    <cfset application.telnet[arguments.SID].telnetData &= dataChunk>
    
                    <cfif dataChunk contains chr(13)>
                        <cfloop list="#dataChunk#" delimiters="#chr(13)#" index="thisLine">
                            <cfquery name="saveChunk">
                                insert into telnetMessages  (mdate, message)
                                            values          (Now(),
                                                             <cfqueryparam cfsqltype="varchar" value="#thisLine#">)
                            </cfquery>
                            <cfset application.telnet[arguments.SID].lastData   = Now()>
                        </cfloop>
                    <cfelse>
                        <cfquery name="saveChunk">
                            insert into telnetMessages  (mdate, message)
                                        values          (Now(),
                                                         <cfqueryparam cfsqltype="varchar" value="#dataChunk#">)
                        </cfquery>
                        <cfset application.telnet[arguments.SID].lastData   = Now()>
                    </cfif>
                </cfloop>
    
                <cfset application.telnet[arguments.SID].isReading  = false>
    
                <cfcatch>
                    <cfset application.telnet[arguments.SID].isReading  = false>
                    <cfset retVal.success = false>
                    <cfset retVal.cfcatch = cfcatch>
                </cfcatch>
                <cffinally>
                    <cfset application.telnet[arguments.SID].isReading  = false>
                </cffinally>
            </cftry>
    
            <cfreturn retVal />
        </cffunction>
    
        <cffunction returntype="Struct" name="telnetClose" access="public" output="no">
            <cfargument name="SID"          type="numeric"  required="yes">
    
            <cfset retValClose          = structNew()>
            <cfset retValClose.success  = true>
    
            <cftry>
                <cfscript>
                    if (application.telnet[arguments.SID].telnetClient.isConnected()) {
                        application.telnet[arguments.SID].telnetClient.disconnect();
                    }
                </cfscript>
    
                <cfcatch>
                    <cfset retValClose.success  = false>
                    <cfset retValClose.cfcatch  = cfcatch>
                </cfcatch>
            </cftry>
    
            <cfreturn retValClose />
        </cffunction>
    
    </cfcomponent>