I am using a device called a WiFly that has ip address:169.254.1.1 and port 2000. I am trying to connect to this device via an iOS application. I use the following code to connect:
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
UInt32 port = 2000;
CFStringRef host = CFSTR("169.254.1.1");
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
// set the delegates to this view controller
[inputStream setDelegate:self];
[outputStream setDelegate:self];
// Set run loops to continuous receive information
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// Finally, open the connection
[inputStream open];
[outputStream open];
Then I use the following to handle stream events:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can't connect to server");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
break;
default:
NSLog(#"Unknown event");
}
So, I can see that the first two streams are opened correctly. Then it follows immediately with a stream event 4, which from my understanding is to be expected. However, I then try to call a function:
- (IBAction)moveForward
{
NSLog(#"move forward called");
NSString *response = [NSString stringWithFormat:#"2"];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
Which should return "forward" from an arduino uno via the wifly. However, when I click, I get another NSStreamEvent 4 for some reason. I also telnetted in to the device via the terminal with:
telnet 169.254.1.1 2000
and subsequently type a "2"... This returned the desired "forward" immediately. What am I doing wrong from the perspective of the iPad?
Help is much appreciated!
EDIT For some reason, everytime the handler is accessed, the stream that is passed is the output stream, which is completely wrong... Why wouldn't the input stream ever be passed through?
And here is the arduino code:
void loop()
{ while (Serial.available() > 0) {
int next = Serial.parseInt();if(Serial.available() > 0)
{
if(next == 20) {
Serial.println("leftMove");
}}}}
Related
I have implemented socket connection in iOS.
What I want to do is to send string of data to the connected device...
(I am able to receive data when someone sends to my device)
I have tried this code but data is getting received on other device, when I close my app.
- (IBAction)connectToServer:(id)sender {
NSLog(#"Setting up connection to %# : %i", _ipAddressText.text, [_portText.text intValue]);
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef) _ipAddressText.text, [_portText.text intValue], &readStream, &writeStream);
messages = [[NSMutableArray alloc] init];
[self open];
}
- (void)open {
NSLog(#"Opening streams.");
outputStream = (__bridge NSOutputStream *)writeStream;
inputStream = (__bridge NSInputStream *)readStream;
[outputStream setDelegate:self];
[inputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
[inputStream open];
_connectedLabel.text = #"Connected";
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %lu", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
_connectedLabel.text = #"Connected";
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream)
{
uint8_t buffer[1024];
NSInteger len;
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output)
{
NSLog(#"server said: %#", output);
[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Stream has space available now");
break;
case NSStreamEventErrorOccurred:
NSLog(#"error: %#",[theStream streamError].localizedDescription);
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
_connectedLabel.text = #"Disconnected";
NSLog(#"close stream");
break;
default:
NSLog(#"Unknown event");
}
}
/* Sends data to other device */
- (IBAction) sendMessage {
NSLog(#"sendMessage");
NSString *response = [NSString stringWithFormat:#"msg:%#", _dataToSendText.text];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
NSLog(#"[data length] %lu",(unsigned long)[data length]);
[outputStream write:[data bytes] maxLength:[data length]];
}
Where I am making mistake?
After doing hard research i found adding \n at the end of string message indicates my buffer has received whole string.
now start sending a file, so it worked...
To send a String over Sockets, you must mark the end of the string. ie; Add a \n
To mark a string with start and end of string there are ASCII control characters to do so, the octets are \002 & \003 respectively. I would recommend using these control characters instead of just adding a line break, your text could have line breaks and that could create problems.
I have two Applaction one is the client (IOS device) and the other one is the server (PC device).
I would like to get the ip address in the client (IOS device ) Automatically.
I'm using this line of code to type the IP address
NSString *ipAddressText = #"192.168.211.62";
I don't want to keep typing the Ip address, becuase the IP address will change most of the time.
here's my code
-(void)viewDidLoad{
[super viewDidLoad];
NSString *ipAddressText = #"192.148.211.42";
NSLog(#"Setting up connection to %# : %i", ipAddressText, 111);
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef) ipAddressText, 111, &readStream, &writeStream);
messages = [[NSMutableArray alloc] init];
[self open];
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %lu \n ", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
_connectedLabel.text = #"Connected";
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream)
{
uint8_t buffer[1024];
NSInteger len;
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output)
{
NSLog(#"server said: %# \n ", output);
[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Stream has space available now");
break;
case NSStreamEventErrorOccurred:
NSLog(#"%#\n",[theStream streamError].localizedDescription);
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
_connectedLabel.text = #"Disconnected";
NSLog(#"close stream");
break;
default:
NSLog(#"Unknown event");
}
}
- (void)open {
NSLog(#"Opening streams.");
outputStream = (__bridge NSOutputStream *)writeStream;
inputStream = (__bridge NSInputStream *)readStream;
[outputStream setDelegate:self];
[inputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
[inputStream open];
_connectedLabel.text = #"Connected";
}
- (void)close {
NSLog(#"Closing streams.");
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream setDelegate:nil];
[outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;
_connectedLabel.text = #"Disconnected";
}
This is in Swift, but can easily be dropped into any Xcode project.
NB: This example is taken from a GitHub project Host.swift.
Getting information about network interfaces in Unix type systems like iOS or Mac OS X requires using some arcane C APIs. These APIs have varying amounts of visibility in swift and objective-c, ios and mac-os-x.
If you're exclusively targeting macOS with Objective-C, then nshost is your best solution. However, if you require iOS compatibility then you'll have to drop down to lower level C API such as getifaddrs(...) or cfhost.
If you want a swift based solution even getifaddrs(...) comes with some bridging header requirements (making it unusable in a Framework).
Here is an example using CFHost and sockaddr_in struct that will work in Swift and on macOS and iOS (even in Frameworks). See Host.swift on GitHib for a fully working example.
Use CFHost to get the addressing info, this will be a CFArray of CFData objets.
let sockaddrs = CFHostGetAddressing("apple.com", &resolved)?.takeRetainedValue()
Get the first object
let data = CFArrayGetValueAtIndex(sockaddrs, 0)
Cast the bytes into a sockaddr struct
var storage = sockaddr_storage()
data.getBytes(&storage, length: sizeof(sockaddr_storage))
Then force the sockaddr struct into a sockaddr_in struct so we can use it
let addr = withUnsafePointer(&storage) { UnsafePointer<sockaddr_in>($0).memory
Use the inet_ntoa(...) function to turn addr.sin_addr (IP address) into a C-string. We can then use String(Cstring: encoding:) to get a nice String of the IP Address.
let address = String(CString: inet_ntoa(addr.sin_addr), encoding: NSUTF8StringEncoding)
Here is a GitHub project called Host.swift that I created to solve these issues using the technique above.
I am able to connect my iOS program to a java based socket listener and get it connected well. iOS app is also able to send screen shot image data to the socket very well. This is done under NSStreamEventHasSpaceAvailable
My problem is, i do not know, how to send this screenshot image periodically to the socket. I am sending it just once now, its working. But, i want to keep sending this image data to socket one after another screenshot taken programmatically in every certain interval. How to do that? Please advise.
Please refer my complete code below.
-(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
{
NSString *io;
if (theStream == inputStream) io = #">>";
else io = #"<<";
NSString *event;
switch (streamEvent)
{
case NSStreamEventNone:
event = #"NSStreamEventNone";
//statusText.text = #"Can not connect to the host!";
NSLog(#"NSStreamEventNone - Can not connect to the host");
break;
case NSStreamEventOpenCompleted:
event = #"NSStreamEventOpenCompleted";
//pingButton.hidden = NO;
//statusText.text = #"Connected";
NSLog(#"Connected");
break;
case NSStreamEventHasBytesAvailable:
event = #"NSStreamEventHasBytesAvailable";
NSLog(#"NSStreamEventHasBytesAvailable called");
if (theStream == inputStream)
{
//read data
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
NSData *theData = [[NSData alloc] initWithBytes:buffer length:len];
if (nil != output)
{
//do something with data
NSLog(#"NSStreamEventHasBytesAvailable theData: %#", theData);
}
}
}
}
break;
case NSStreamEventHasSpaceAvailable:
event = #"NSStreamEventHasSpaceAvailable";
NSLog(#"NSStreamEventHasSpaceAvailable called");
NSLog(#"space : %d",[outputStream hasSpaceAvailable]);
if (theStream == outputStream)
{
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
UIGraphicsBeginImageContext(appDelegate.window.bounds.size);
[appDelegate.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(image);
[data writeToFile:#"screenshot.png" atomically:YES];
int num = [outputStream write:[data bytes] maxLength:[data length]];
if (-1 == num) {
NSLog(#"Error writing to stream %#: %#", outputStream, [outputStream streamError]);
}else{
NSLog(#"Wrote %i bytes to stream %#.", num, outputStream);
//[outputStream close];
}
}
break;
case NSStreamEventErrorOccurred:
event = #"NSStreamEventErrorOccurred";
//statusText.text = #"Can not connect to the host!";
//pingButton.hidden = YES;
NSLog(#"NSStreamEventErrorOccurred - Can not connect to the host");
break;
case NSStreamEventEndEncountered:
event = #"NSStreamEventEndEncountered";
//statusText.text = #"Connection closed by the server.";
//pingButton.hidden = YES;
NSLog(#"NSStreamEventEndEncountered - Connection closed by the server");
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//[theStream release];
theStream = nil;
break;
default:
event = #"** Unknown";
}
NSLog(#"%# : %#", io, event);
}
Under a Button click, the below code is for connecting the host initially.
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"localhost", 8080, &readStream, &writeStream);
inputStream = (__bridge_transfer NSInputStream *)readStream;
outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
I'm trying to write telnet client on iOS and I can't set up streams correctly. I think that they work, but I am getting empty buffer each time I get delegate method triggered.
Here is how I set up streams:
self.manager = [[TelnetManager alloc] init];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)[[NSURL URLWithString:#"arda.pp.ru"] host], 7000, &readStream, &writeStream);
NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self.manager];
[outputStream setDelegate:self.manager];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
Here is the delegate method that is on manager that handles events:
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
if (eventCode == NSStreamEventHasBytesAvailable) {
NSLog(#"We got some data over here");
if(!_data) {
_data = [NSMutableData data];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)aStream read:buf maxLength:1024];
NSLog(#"length - %d", len);
NSLog(#"buf:%s", buf);
if(len) {
[_data appendBytes:(const void *)buf length:len];
_bytesRead = #([_bytesRead intValue]+len);
} else {
NSLog(#"no buffer!");
}
}
}
It is getting fired, but buffer is always empty and "no buffer" is logged.
What's wrong with my code?
There actually is nothing wrong with your code. I have used it to connect to a telnet server and it worked fine.
In your code, "no buffer" is logged when len is 0, which means the buffer's end has been reached. In the case of a telnet server, this usually means that either the server disconnected your connection or the server quit.
I want to send requests to a server in my network. The first thing I did was to get my IP address. ex. 196.168.16.* . Now that I have my IP address I'll have to send request to all IP in the network with those with listening ports,ex. port 49160. So what i did was to send requests from 192.168.16.1 to 192.168.16.255 and if an IP has a listening port of 49160 they will receive my request. Then a prompt will appear to the PC ... by the way my server is a PC .. then the PC user will accept it,then i will receive a return message from them. I am able to send request in a static manner.but when i try to loop the method im using it doenst work.
Here are sample codes:
static IOStreamHelper *_sharedInstance = nil;
+ (IOStreamHelper*)sharedInstance
{
if (_sharedInstance == nil) {
_sharedInstance = [[IOStreamHelper alloc] init];
}
return _sharedInstance;
}
-(void)initWithIpAddress:(CFStringRef)ipAddr port:(int)port
{
NSString*str = (__bridge_transfer NSString*)ipAddr;
NSLog(#"initWithIP:%#:%i",str,port);
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)ipAddr, port, &readStream, &writeStream);
inputStream = (__bridge_transfer NSInputStream *)readStream;
outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
-(void)sendStringRequest
{
NSString*space = [NSString stringWithFormat:#"%#",[NSByteCountFormatter stringFromByteCount:[self getFreeDiskspace] countStyle:NSByteCountFormatterCountStyleFile]];
NSString *message = [NSString stringWithFormat:#"fishtune_c;%#(%#),1092809312741,jyce09#gmail.com,%#;",[self platformString],[[GetIpAndMacAddress sharedInstance] getMacAddress],space];
messageData = [[NSData alloc] initWithData:[message dataUsingEncoding:NSUTF8StringEncoding]];
[outputStream write:[messageData bytes] maxLength:[messageData length]];
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
{
NSLog(#"stream event %i", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];//1024
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
if (nil != output) {
NSLog(#"server reply: %#", output);
}
}
}
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Stream has space available now");
[self sendStringRequest];
break;
case NSStreamEventErrorOccurred:
{
NSLog(#"Can not connect to the host!");
NSError *theError = [theStream streamError];
NSLog(#"%#",[NSString stringWithFormat:#"%#",[theError localizedDescription]]);
}
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
theStream = nil;
break;
default:
NSLog(#"Unknown event");
}
}
Then i call this method .This is the method that i want to iterate from 1 to 255:
CFStringRef aCFString = (__bridge CFStringRef)#"IPfrom 1 to 255";
[[IOStreamHelper sharedInstance] initWithIpAddress:aCFString port:49160];
I want to know how to iterate this properly. Thanks