I need a swift sample code to communicate with telnet server. The purpose is to send and recieve signals from telnet server.
swift telnet client
How much of the Telnet protocol do you need to support? In very simple cases you can use a standard TCP API to connect to and interact with a Telnet server. However, the Telnet protocol is surprisingly complex, so if you need to any of those features you will have to use a library that implements the Telnet protocol proper.
Apple’s platforms do not include such a library. If you need one, you’ll have to write it yourself or acquire it from elsewhere.
In terms of standard TCP APIs, there’s a bunch of these available, including:
CFSocketStream (exposed to Swift via
)Stream
NSURLSession (specifically its stream tasks feature)
BSD Sockets (tricky to call from Swift)
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
I simply need a sample code that can recieve and send commands through telnet protocol. I can do the processing of recieving code if i get such a code.
The Telnet protocol is a complex beast (I count 8 core RFCs and way more supplementary ones). Beyond that, many Telnet apps would also want to support terminal emulation, which is a really hard problem in the general case. Do you want to support all of that? Or some subset?
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
Do you mean "the Telnet protocol" or do you mean "just a regular TCP/IP connection sending text line by line"?
telnet (the protocol, or the program) can do a heck of a lot more work than it looks like it does.
sending text line by line through telnet connection. telnet connection as in ip with port 23. i just want to send and recieve strings by connecting to the ip
update: i think swiftsocket is the solution. but I havent been able to fully implement it. i can send login command from the test ios app. but it doesnt show any feedback.
If you’re connecting to port 23 then you’re dealing with the actual Telnet protocol, not a generic line-based protocol. The Telnet protocol adds complications above and beyond what you’d expect in a simple line-based protocol (like IMAP, say).
IMPORTANT The
telnet
command line tool has
two modes of operation. If you connect to port 23 it speaks the Telnet protocol. If you connect to some other port, it operates as a naïve line-based client. To quote the
man page:
When connecting to a non-standard port, telnet omits any automatic initiation of TELNET options.
Consider this:
$ nc 192.168.1.17 23 | hexdump -Cv
00000000 ff fd 01 ff fd 1f ff fb 01 ff fb 03 0d 0d 0a xx |...............x|
00000010 xx xx xx xx xx xx xx xx xx xx xx 20 6c 6f 67 69 |xxxxxxxxxxx logi|
^C
where 192.168.1.17 is the IP address of a Telnet server on my local network.
Note I’ve x’d out a bunch of characters to mask the identity of my device.
The first 12 bytes are a sequence of Telnet escape sequences. In this case these escape sequences occur right at the start of the stream, but that’s not a requirement; they can appear anywhere in the stream. So, if you try to talk to a Telnet server with a naïve line-based client, you can get very confused.
Any Telnet client needs three things:
A TCP transport, to actually make a TCP connection and transfer raw bytes over it (A)
A Telnet framer/unframer, to deal with the Telnet escape sequences (B)
A line framer/unframer, to break that data up into lines (C)
With regards A, you can use any TCP API you like. I generally use CFSocketStream for this (accessed via Swift’s
Stream
type) but it’s really up to you. If you’re coming from a Swift background you might find
URLSessionStreamTask
easier.
With regards B, this is a very Telnet specific issue and you’re unlikely to find code for this outside of a Telnet library. Implementing it yourself won’t take a lot of code but that code is likely to be a bit tricky.
With regards C, framing and unframing lines is also a bit tricky. I’ve pasted in the code I use for this below.
IMPORTANT That code assumes that the lines are composed of UTF-8 bytes. This is not true for Telnet, which does not define a standard text encoding. A Telnet client has to know in advance what text encoding the Telnet server is speaking.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
import Foundation
/// Takes an array of lines and frames them in the standard way used by Internet
/// protocols, that is, adding a CR LF at the end.
///
/// This cannot throw because, hey, you shouldn’t have given it bogus strings
/// in the first place.
///
/// - note: This is a method on a `struct` to match `LineUnframer`, which has to
/// maintain complex state. It’s also possible to imagine a different type
/// of framer that needs to maintain state.
struct LineFramer {
func data(for lines: [String]) -> Data {
return lines.map { "\($0)\r\n" }.joined().data(using: .utf8)!
}
}
/// Takes data and parses it looking for lines.
///
/// This has to maintain a buffer of data looking for line breaks. You
/// initialise it with the maximum line length, measured in bytes of
/// UTF-8 encoded data.
///
/// Also, this will throw if it gets a malformed line, including:
///
/// * Code sequences that aren’t valid UTF-8
/// * The line is too long.
struct LineUnframer {
init(maxLineLength: Int) {
precondition(maxLineLength > 0)
self.maxLineLength = maxLineLength
}
let maxLineLength: Int
private enum State {
case standard(Data)
case expectingLF(Data)
case error(Swift.Error)
}
private var state: State = .standard(Data())
mutating func lines(for data: Data) throws -> [String] {
if case .error(let error) = self.state {
throw error
}
var result: [String] = []
func extendLine(_ lineData: Data, _ bytes: [UInt8]) throws {
if lineData.count + bytes.count > self.maxLineLength {
throw Error(code: .lineTooLong, linesSoFar: result)
}
self.state = .standard(lineData + bytes)
}
for c in data {
switch (self.state, c) {
case (.expectingLF(let lineData), 10):
guard let line = String(bytes: lineData, encoding: .utf8) else {
throw Error(code: .malformedUTF8, linesSoFar: result)
}
result.append(line)
self.state = .standard(Data())
case (.expectingLF(let lineData), let b):
try extendLine(lineData, [13, b])
case (.standard(let lineData), 13):
self.state = .expectingLF(lineData)
case (.standard(let lineData), let b):
try extendLine(lineData, [b])
case (.error, _):
fatalError()
}
}
return result
}
struct Error : Swift.Error, Equatable {
enum Code {
case lineTooLong
case malformedUTF8
}
var code: Code
var linesSoFar: [String]
static func ==(lhs: Error, rhs: Error) -> Bool {
return lhs.code == rhs.code && lhs.linesSoFar == rhs.linesSoFar
}
}
}
The code seems to be a bit tricky for me. I have tried to implement swiftsocket. I am able to send but not recieve the code. I can manage from the point where i can recieve the code. I can break it down and process by myself.
simulator image - https://ibb.co/hSwfHb
telnet image - https://ibb.co/c25wqw
I’m not familiar with SwiftSocket, so I can’t help you with that. My recommendation is that you use one of the built-in TCP APIs, and specifically
URLSessionStreamTask
. It seems that SwiftSocket is built directly on top of BSD Sockets, which is not an approach I recommend.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"