In swift NSNetService class how to send data using getInoutStream - swift

Currently I am working on a program in which i am connecting two IOS devices using NSNetService class. I am able to connect both the devices but i don't know how to send data using getInoutStream function. Can any buddy help me.

Why not use multipeer? It's a much simpler solution to what you're trying to do. Also NSNetService has a few known bugs. However, you send data between the two by setting up a Bonjour connection on NSNetService. The getInputStream method requires arguments of the type UnsafeMutablePointer:
public func getInputStream(inputStream: UnsafeMutablePointer<NSInputStream?>, outputStream: UnsafeMutablePointer<NSOutputStream?>) -> Bool
var inputStream : NSInputStream?
var outputStream : NSOutputStream?
let success = service.getInputStream(&inputStream, outputStream: &outputStream)
Then just write data using a memory stream and it'll get passed to the listener port.
This is the finished code:
This function initiates the connection on 127.0.0.1
func initNetworkCommunication(){
var host : CFString = "127.0.0.1"
var port : UInt32 = 7001
var readstream : Unmanaged<CFReadStream>?
var writestream : Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readstream, &writestream)
inputstream = readstream!.takeRetainedValue()
outputstream = writestream!.takeRetainedValue()
inputstream.delegate = self
outputstream.delegate = self
inputstream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputstream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputstream.open()
And this is for the stream IO:
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { //This is the stream IO function. It allows RW of the stream
switch (eventCode){
case NSStreamEvent.ErrorOccurred:
NSLog("ErrorOccurred")
break
case NSStreamEvent.EndEncountered:
NSLog("EndEncountered")
break
case NSStreamEvent.None:
NSLog("None")
break
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvaible")
var buffer = [UInt8](count: 4096, repeatedValue: 0)
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var len = inputstream.read(&buffer, maxLength: buffer.count)
if(len > 0){
var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
if (output != ""){
NSLog("server said: %#", output!)
}
}
}
}
break
case NSStreamEvent.allZeros:
NSLog("allZeros")
break
case NSStreamEvent.OpenCompleted:
NSLog("OpenCompleted")
break
case NSStreamEvent.HasSpaceAvailable:
NSLog("HasSpaceAvailable")
break
default:
// default code here
break
}

Related

Simple TCP listener on swift

I send messages to applications running on remote computers.
I send messages this ways.
c#:
void web_data_work()
{
try
{
TcpClient client = new TcpClient(address, port);
Byte[] data = Encoding.UTF8.GetBytes(string.Format("{0}", message));
NetworkStream stream = client.GetStream();
try
{
if (message != "")
{
stream.Write(data, 0, data.Length);
Byte[] readingData = new Byte[256];
String responseData = String.Empty;
StringBuilder completeMessage = new StringBuilder();
int numberOfBytesRead = 0;
do
{
numberOfBytesRead = stream.Read(readingData, 0, readingData.Length);
completeMessage.AppendFormat("{0}", Encoding.UTF8.GetString(readingData, 0, numberOfBytesRead));
}
while (stream.DataAvailable);
responseData = completeMessage.ToString();
this.Invoke((MethodInvoker)delegate ()
{
output_list.Items.Add(string.Format("Sended – {0}", responseData));
message = "";
});
}
}
finally
{
stream.Close();
client.Close();
}
}
catch
{
this.Invoke((MethodInvoker)delegate ()
{
output_list.Items.Add("Not sended");
message = "";
});
}
}
swift:
func web_data_work()
{
let address = address_box.stringValue
let port = port_box.intValue
let task = URLSession.shared.streamTask(withHostName: address, port: Int(port))
let data = message.data(using: .utf8)!
task.write(data as Data, timeout: 0)
{
error in
//om = "Not sended"
//self.output_message()
}
task.resume()
}
In c# i can read messages using TcpListener, how do i do it in swift?
Only two parameters are used:
"address" – ip address of the computer to which messages are sent and which is listened to by the TcpListener of that computer.
"port" – port of the sending and receiving between computers.
P.S. Preferably without additional libraries.
You can achieve this with just two classes: SocketPort & FileHandle.
Before you copy & paste the example below, please, read my comments at the end.
import Cocoa
class TcpEchoClient {
var readToEndOfFileCompletionHandler: (() -> Void)?
let fileHandle: FileHandle
var observer: NSObjectProtocol?
init(fileHandle: FileHandle) {
self.fileHandle = fileHandle
// Register observer for the data & EOF
self.observer = NotificationCenter.default.addObserver(forName: .NSFileHandleReadToEndOfFileCompletion,
object: fileHandle,
queue: OperationQueue.main) { [weak self] note in
self?.handleReadToEndOfFileCompletion(notification: note)
}
// Instruct the handle to read till the EOF & notify
fileHandle.readToEndOfFileInBackgroundAndNotify()
}
func handleReadToEndOfFileCompletion(notification note: Notification) {
defer {
// No matter what happens, call the completion handle by the end
readToEndOfFileCompletionHandler?()
}
// Is there an error?
if let errorCode = note.userInfo?["NSFileHandleError"] as? NSNumber {
print("Client \(fileHandle.fileDescriptor) error: File handle error \(errorCode.intValue)")
return
}
// No error, we should have data available
guard let data = note.userInfo?[NSFileHandleNotificationDataItem] as? Data else {
print("Client \(fileHandle.fileDescriptor) error: Unable to get data")
return
}
// Convert them to UTF-8 string
guard let text = String(data: data, encoding: .utf8) else {
print("Client \(fileHandle.fileDescriptor) error: Unable to convert data to UTF-8 string")
return
}
// Print the text
print("Client \(fileHandle.fileDescriptor) received: \(text)")
}
deinit {
// Remove observer for the data & EOF
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
// Close the handle
try? fileHandle.close()
}
}
class TcpServer {
let port: SocketPort
let fileHandle: FileHandle
var clients: Dictionary<Int32, TcpEchoClient>
var observer: NSObjectProtocol?
init?(tcpPort: UInt16) {
guard let socketPort = SocketPort(tcpPort: tcpPort) else {
return nil
}
// Keep the socket port around otherwise you'll get error 38
port = socketPort
// No clients for now
clients = [:]
// Create handle from the socket
fileHandle = FileHandle(fileDescriptor: port.socket)
// Register observer for the connection accepted
observer = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
object: fileHandle,
queue: OperationQueue.main) { [weak self] note in
if let handle = note.object as? FileHandle {
// Ask immediately for another accepted connection notification
handle.acceptConnectionInBackgroundAndNotify()
}
self?.handleConnectionAccepted(notification: note)
}
// Instruct the handle to accept connection & notify
fileHandle.acceptConnectionInBackgroundAndNotify()
}
func handleConnectionAccepted(notification note: Notification) {
// Is there an error?
if let errorCode = note.userInfo?["NSFileHandleError"] as? NSNumber {
print("Server error: File handle error \(errorCode.intValue)")
return
}
// No, we should have received the client file handle
guard let clientFileHandle = note.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else {
print("Server error: Unable to get accepted connection file handle")
return
}
let fileDescriptor = clientFileHandle.fileDescriptor
// Create new client from the file handle
let client = TcpEchoClient(fileHandle: clientFileHandle)
// Once it finishes, remove it from the clients dictionary
client.readToEndOfFileCompletionHandler = { [weak self] in
guard let self = self else { return }
self.clients.removeValue(forKey: fileDescriptor)
print("Server: Client removed \(fileDescriptor) (total \(self.clients.count))")
}
// Store the client in the clients dictionary
clients[fileDescriptor] = client
print("Server: New client \(fileDescriptor) added (total \(self.clients.count))")
}
deinit {
// Remove all clients
clients.removeAll()
// Close the file handle
try? fileHandle.close()
// Invalidate the socket port
port.invalidate()
// Remove connection accepted observer
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
}
Then create a property (var server: TcpServer?) and initialize it (server = TcpServer(tcpPort: 8080)).
Test:
parallel -j 1 echo -n Foo '|' nc 127.0.0.1 8080 ::: {1..4}
Console output:
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Comments:
This example is only a basic skeleton you can start with.
Don't be misleaded with the parallel test command.
I'm using it if I want to run multiple commands at once (same commands).
Everything in this example operates on the main queue.
Read documentation of all these classes, methods and notifications I do use. There're gotchas, you have to be familiar with run loops, etc.

Socket Server via CFNetwork in Swift 4

I am new in Swift.
I am studying to write a pure swift app to be socket server.
code is as below
server = CFSocketCreate(kCFAllocatorDefault, AF_INET,
SOCK_STREAM, IPPROTO_TCP, callbackOpts.rawValue, {
(socket, type, address, data, info) in
print("type ->\(type)")
}, nil)
let size = MemoryLayout<sockaddr_in>.size
addr.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
addr.sin_family = sa_family_t(AF_INET); /* Address family */
addr.sin_port = in_port_t(10001); /* Or a specific port */
addr.sin_addr.s_addr = in_addr_t(INADDR_ANY);
let addrInPtr = withUnsafeMutablePointer(to: &addr) { ptr in
return ptr.withMemoryRebound(to: UInt8.self, capacity: size){
return $0
}
}
let sincfd = CFDataCreate(
kCFAllocatorDefault,
addrInPtr,
size)
let result = CFSocketSetAddress(server, sincfd)
switch result {
case .success:
print("xx sucess")
case .timeout:
print("xx timeout")
case .error:
print("xx error")
}
let runLoopSourceRef = CFSocketCreateRunLoopSource(
kCFAllocatorDefault, server, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(),
runLoopSourceRef, CFRunLoopMode.defaultMode)
I got "xx success" when binder.
I can not connect this server from client.
Does someone give me a hint what problem is and how to do a server?
I know there are some 3rd party library can do that,
but I am studying Swift and try to understand how to use CFNetwork.
I just found my problem.
It was caused by the byte order of port number.
Code should be
addr.sin_port = in_port_t(CFSwapInt16HostToBig(10001))

Send image to printer using sockets on port 9100

I'm currently able to send text to a printer (like EPSON TM20-II) using Swift. The printer receives the data and prints it without any problems. (For that, I'm encoding my string in UTF-8)
My custom class for the socket :
class OutSocket {
var addr: String
var port: Int
var task: NSURLSessionStreamTask!
init(host: String, port: Int){
print("init")
self.addr = host
self.port = port
setupConnection()
}
func setupConnection() {
print("setup")
let session = NSURLSession(configuration: .defaultSessionConfiguration())
task = session.streamTaskWithHostName(addr, port: port)
self.task.resume()
}
func send(data: NSData) {
self.task.writeData(data, timeout: 5.0) { (error) in
if error == nil {
print("Data sent")
} else {
print("Nope")
}
}
}
}
And I'm using it like this :
let socket = OutSocket(host: "192.168.1.42", port: 9100)
socket.send((itemText + "\n").dataUsingEncoding(NSUTF8StringEncoding)!)
I can send an image to the printer, but it only prints me the data characters.
How can I print an image, and not the data characters ?
I saw the Epson SDK but I want it to works with any printer that support TCP/IP printing. I tried to encode my image in base64 but still not print the image.
here the code :
let socket = OutSocket(host: "192.168.1.42", port: 9100)
let image = UIImage(named: "name-logo")
let imgData = UIImagePNGRepresentation(image!)
let imgEncoded = imgData!.base64EncodedDataWithOptions(.Encoding64CharacterLineLength)
socket.send(imgEncoded)
Thanks in advance,

Reading Sensor data indefinitely using TCP in Swift

I am new to Swift and i am trying to read the data from a sensor that connects to my router using Wifi. According to the sensor documentation: "In local communication mode the sensor node works as TCP/IP-server and writes output data in ASCII-format in the defined IP-address port 8080 at 1Hz rate.” So i want to read that data that the sensor is continuously emitting every second and use it to plot a chart on the iPhone. This is how the sample data looks like, the first value on each row(7255,7256.., 7259) is the time that changes every second.
7255,64,6,46,390,555,1,11137,0,0
7256,64,6,46,390,785,1,1108,0,0
7257,64,6,46,390,602,1,1135,0,0
7258,64,6,46,390,847,2,1135,0,0
7259,64,6,47,403,870,2,1669,0,0
I wrote a sample application and I am able to connect to the sensor and read the data but it doesn’t work continuously or consistently. I will receive one record then it will wait for few second and then all of a sudden i will receive the data for 5-8 seconds. Also after working for 25-30 seconds it will wait for a long time and then nothing will be read anymore. I would like to read the data continuously like the free app "WIFI TCP Test Tool" available for free on App Store. This app reads the data perfectly every second from the sensor so i know there is nothing wrong with the sensor or my router. Here is my sample code, would appreciate any help.
import UIKit
import Foundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func readSensorData() {
let addr = "192.168.0.100"
let port = 8080
var buffer = [UInt8](count: 255, repeatedValue: 0)
var inp : NSInputStream?
var out : NSOutputStream?
NSStream.getStreamsToHostWithName(addr, port: port, inputStream: &inp, outputStream: &out)
if inp != nil && out != nil {
let inputStream : NSInputStream = inp!
inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputStream.open()
if inputStream.streamError == nil {
while true {
let readChars: Int = inputStream.read(&buffer, maxLength: buffer.count)
if (readChars > 0) {
let readString: String = NSString(data: NSData(bytes:buffer, length:readChars), encoding: NSUTF8StringEncoding)! as String
print(readString)
} else {
print ("sensor closed connection")
inputStream.close()
break
}
}
} else {
print ("could not create socket")
}
} else {
print("could not initialize stream")
}
}
#IBAction func startSensorReadTapped(sender: AnyObject) {
readSensorData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

Receiving Data from NSInputStream in Swift

I try to send and receive data with NSOutputStream and NSInputStream in Swift. The sending of data is working well, but i have some questions about the receiving.
I found a solution with handling the NSStreamEvent, which i have tried.
First of all my function for initializing the connection:
func initNetworkCommunication(){
var host : CFString = "127.0.0.1"
var port : UInt32 = 7001
var readstream : Unmanaged<CFReadStream>?
var writestream : Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readstream, &writestream)
inputstream = readstream!.takeRetainedValue()
outputstream = writestream!.takeRetainedValue()
inputstream.delegate = self
outputstream.delegate = self
inputstream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputstream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputstream.open()
outputstream.open()
}
This part is working. I have set the delegates to self, so i should be able to handle the NSStreamEvents in this class.
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
switch (eventCode){
case NSStreamEvent.OpenCompleted:
NSLog("Stream opened")
break
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvailable")
break
case NSStreamEvent.ErrorOccurred:
NSLog("ErrorOccurred")
break
case NSStreamEvent.EndEncountered:
NSLog("EndEncountered")
break
default:
NSLog("unknown.")
}
}
In my understanding everytime when some data arrives, it should print "HasBytesAvaible", but it printed "unknown." everytime. So i played around a bit and it worked with this:
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
var buffer = [UInt8](count: 4096, repeatedValue: 0)
while (inputstream.hasBytesAvailable){
let result: Int = inputstream.read(&buffer, maxLength: buffer.count)
}
switch (eventCode){
case NSStreamEvent.OpenCompleted:
NSLog("Stream opened")
break
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvailable")
var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
NSLog("output: %#", output)
receiveMessage(output) //only adds the message to an array
break
case NSStreamEvent.ErrorOccurred:
NSLog("ErrorOccurred")
break
case NSStreamEvent.EndEncountered:
NSLog("EndEncountered")
break
default:
NSLog("unknown.")
}
}
It's working this way, but i wanted to ask you whether this is the right way ? What is the best practice at this point ?
UPDATE
I worked on it again a few weeks ago and figured out my mistakes. So here is the working code.
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) {
switch (eventCode){
case NSStreamEvent.ErrorOccurred:
NSLog("ErrorOccurred")
break
case NSStreamEvent.EndEncountered:
NSLog("EndEncountered")
break
case NSStreamEvent.None:
NSLog("None")
break
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvaible")
var buffer = [UInt8](count: 4096, repeatedValue: 0)
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var len = inputstream.read(&buffer, maxLength: buffer.count)
if(len > 0){
var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
if (output != ""){
NSLog("server said: %#", output!)
}
}
}
}
break
case NSStreamEvent.allZeros:
NSLog("allZeros")
break
case NSStreamEvent.OpenCompleted:
NSLog("OpenCompleted")
break
case NSStreamEvent.HasSpaceAvailable:
NSLog("HasSpaceAvailable")
break
}
You're missing the event hasSpaceAvailable, which I expect is occurring when it says "unknown". It's telling you that it is ready to receive more data.
Generally, I avoid using default in switch statements for enums, since the compiler will tell you when you've missed something.
I am using the code written by hoedding, and noticed a mistake. The line:
var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
should be replaced by:
var output = NSString(bytes: &buffer, length: len, encoding: NSUTF8StringEncoding)
You have to get in the output var only the number of characters returned by the server, and not the full length of the buffer, or you will get garbage from previous requests.