I'm developing an application for iOS which is sharing its screen with another device through constant flow of screenshot images sent through TCP sockets connection.
Here's the code I'm written so far:
#implementation newClient {
NSData *data;
NSInputStream *iStream;
NSOutputStream *oStream;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
BOOL boolean;
}
-(void) connectToServerUsingCFStream:(UIImage *) bitmap {
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef) #"192.168.1.9",
8080,
&readStream,
&writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
iStream = (NSInputStream *)CFBridgingRelease(readStream);
[iStream setDelegate:self];
//[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
// forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = (NSOutputStream *)CFBridgingRelease(writeStream);
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
// data = UIImageJPEGRepresentation(bitmap, 50);
boolean = YES;
[self writeToServer:bitmap];
}
}
- (void) writeToServer:(UIImage *)bitmap {
dispatch_async(dispatch_get_main_queue(), ^{
#autoreleasepool {
NSData *data = UIImageJPEGRepresentation(bitmap, 50);
int len;
uint32_t length = (uint32_t)htonl([data length]);
// Sending out the length of the screenshot image and then the image
len=[oStream write:(const uint8_t *)&length maxLength:4];
NSLog(#"len=%d",len);
len=[oStream write:(const uint8_t *)[data bytes] maxLength:[data length]];
NSLog(#"len=%d",len);
//[oStream write:(const uint8_t *)"/n" maxLength:0];
}
});
}
#end
When I click the button which is activating this object in sends the first screenshot and then crashes giving me "Thread 1: EXC_BAD_ACCESS(code=1, address=0xd000000c) in the main.m file of my project.
The server application is running on Android and it's a thread which is constantly running in a loop.
I don't know what is causing the problem.
Related
How do I handle errors in a stream? If the user is connected to the wrong network I want to handle that. Thanks!
Code:
- (void)initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"IP HERE", 7777, &readStream, &writeStream);
_inputStream = (NSInputStream *)CFBridgingRelease(readStream);
_outputStream = (NSOutputStream *)CFBridgingRelease(writeStream);
[_inputStream setDelegate:self];
[_outputStream setDelegate:self];
[_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_inputStream open];
[_outputStream open];
}
I figured out a solution that worked in my case. This code will print out the current network's BSSID in the console, and I simply check if the BSSID matches the one for my preferred network with an if-statement:
#import <SystemConfiguration/CaptiveNetwork.h>
//Checks which network the user is connected to.
CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(#"Connected at: %#", myDict);
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
NSString * BSSID = [myDictionary objectForKey:#"BSSID"];
NSLog(#"BSSID is: %#", BSSID);
//Handling wrong/correct BSSID.
if (![BSSID isEqualToString:#"PREFERRED BSSID HERE"]) {
//Handle error however you want.
}
else {
//If correct BSSID, handle that here however you want.
}
}
I'm trying to send an image to the twisted server and back to my iPhone. My code works on the simulator but does not work on the iPhone. I have no idea why. All I'm doing is sending the data for the image to the server and then immediately back to my iPhone. Here is the relevant code that I'm using.
SERVER SIDE:
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
class IphoneChat(Protocol):
def connectionMade(self):
#self.transport.write("""connected""")
self.factory.clients.append(self)
print "clients are ", self.factory.clients
def connectionLost(self, reason):
self.factory.clients.remove(self)
def dataReceived(self, data):
#print "data is ", data
self.transport.write(data);
def message(self, message):
self.transport.write(message + '\n')
factory = Factory()
factory.protocol = IphoneChat
factory.clients = []
reactor.listenTCP(80, factory)
print "Server Started"
reactor.run()
CLIENT SIDE:
#interface LoginScreen : UIViewController <NSStreamDelegate> {
}
#property (strong, nonatomic) NSMutableData *outputData;
#property (strong, nonatomic) IBOutlet UIImageView *testImage;
#implementation LoginScreen : UIViewController
- (void)initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"avis-mbp", 80, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge 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)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
switch (streamEvent) {
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
long len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSData *output = [[NSData alloc] initWithBytes:buffer length:len];
if (nil != output) {
[self.appDel.outputData appendBytes:buffer length:len];
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
NSLog(#"Event Ended");
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
theStream = nil;
break;
default:
break;
}
}
-(IBAction)runNetworkingTest:(id)sender {
[self initNetworkCommunication];
NSData *pictureData = UIImagePNGRepresentation([UIImage imageNamed:#"shalin.jpg"]);
NSMutableData *mutedData = [[NSMutableData alloc] initWithData:pictureData];
[outputStream write:[mutedData bytes] maxLength:[mutedData length]];
}
-(IBAction)testPicture:(id)sender {
UIImage *image = [UIImage imageWithData:self.outputData];
self.testImage.image = image
}
I found the solution to my problem. It had to do with the space available when writing to the server. The iPhone can only write a specific amount of bytes at a time, so I had to regulate the amount of data that was written in the delegate via the NSStreamEventHasSpaceAvailable case. Here is the missing piece of code that will allow you to write an image to the server and read it back to the client via a TCP connection to a twisted server:
Missing Code to be put in the NSStream Delegate
case NSStreamEventHasSpaceAvailable:
{
if (self.appDel.willWrite && [self.appDel.inputData length] != 0) {
int bufferSize = 1024;
if ([self.appDel.inputData length] > bufferSize){
NSData *sendData = [self.appDel.inputData subdataWithRange:NSMakeRange(0, bufferSize)];
self.appDel.inputData = [[NSMutableData alloc] initWithData:[self.appDel.inputData subdataWithRange:NSMakeRange(bufferSize, [self.appDel.inputData length] - bufferSize)]];
[outputStream write:[sendData bytes] maxLength:[sendData length]];
} else {
[outputStream write:[self.appDel.inputData bytes] maxLength:[self.appDel.inputData length]];
self.appDel.inputData = [[NSMutableData alloc] init];
}
}
}
Modified version of the run networking test
-(IBAction)runNetworkingTest:(id)sender {
[self initNetworkCommunication];
self.appDel.willWrite = YES;
NSData *pictureData = UIImagePNGRepresentation([UIImage imageNamed:#"shalin.jpg"]);
[self.appDel.inputData appendData:pictureData];
}
Code to display the image
-(IBAction)showNetworkingArray:(id)sender {
UIImage *image = [UIImage imageWithData:self.appDel.outputData];
self.testImage.image = image;
}
Notice: I am writing 1024 bytes at a time. IT WILL NOT WORK IF YOU WRITE TOO MANY BYTES AT A TIME. For example, I tried 1024 * 8 and that did not work with a single image. However, when I set the buffer size to be 1024, I was able to send about ten images over without a problem.
I know there are tools available to send push notification that are implemented with php or C++ or something else. I am trying develop my own using NSStream. I have tried the following code.
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 80, &readStream, &writeStream);
NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream *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];
Delegate method:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
NSLog(#"stream:handleEvent: is invoked...");
switch(eventCode) {
case NSStreamEventOpenCompleted:
// it is getting here
break;
case NSStreamEventHasSpaceAvailable:
{
if (stream == oStream) {
NSString * str = [NSString stringWithFormat:
#"{"aps":{"alert":"Hello from APN server.","badge":"1"}}"];
// how do i add device token
const uint8_t * payload =
(const uint8_t *)[str UTF8String];
[oStream write:payload maxLength:strlen(payload)];
[oStream close];
}
break;
}
}
}
Now the problems are:
How do I open the stream with a push certificate
How do I generate the payload with a device token
I'm developing an iOS application which is using socket connection to share it's screen with another device. It's working well and it's sending the screenshots but the memory which the application is allocation keeps on growing and growing until it receives memory warning after several seconds and then it starts sending wrong data to the receiving server so that the server crashes. Here is the code I use right now:
The method in my main ViewController.m:
-(void) activateShareScreen {
newClient *nc = [[newClient alloc] init];
[nc connectToServerUsingCFStream];
dispatch_queue_t backgroundQueue = dispatch_queue_create("No", 0);
dispatch_async(backgroundQueue, ^{
while (true){
#autoreleasepool {
UIGraphicsBeginImageContext(mapView_.frame.size);
[mapView_.layer renderInContext:UIGraphicsGetCurrentContext()];
bitmap = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
[nc writeToServer:UIImageJPEGRepresentation(bitmap, 50)];
}
});
NSLog(#"Share screen button tapped");
}
And it is calling this file:
#import "newClient.h"
#implementation newClient {
NSInputStream *iStream;
NSOutputStream *oStream;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
BOOL boolean;
}
-(void) connectToServerUsingCFStream{
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef) #"192.168.1.9",
8080,
&readStream,
&writeStream);
if (readStream && writeStream) {
//CFReadStreamSetProperty(readStream,
// kCFStreamPropertyShouldCloseNativeSocket,
// kCFBooleanTrue);
//CFWriteStreamSetProperty(writeStream,
// kCFStreamPropertyShouldCloseNativeSocket,
// kCFBooleanTrue);
iStream = (__bridge_transfer NSInputStream *)readStream;
oStream = (__bridge_transfer NSOutputStream *)writeStream;
// [iStream setDelegate:self];
//[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
// forMode:NSDefaultRunLoopMode];
[iStream open];
// [oStream setDelegate:self];
//[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
// forMode:NSDefaultRunLoopMode];
[oStream open];
// data = UIImageJPEGRepresentation(bitmap, 50);
boolean = YES;
(void) writeToServer:(NSData *) data{
int len;
uint32_t length = (uint32_t)htonl([data length]);
//[oStream write:(const uint8_t *) "/n" maxLength:2];
// Sending out the length of the screenshot image and then the image
len=[oStream write:(const uint8_t *)&length maxLength:4];
NSLog(#"len=%d",len);
//[oStream write:(const uint8_t *) "/n" maxLength:2];
len=[oStream write:(const uint8_t *)[data bytes] maxLength:[data length]];
NSLog(#"len=%d",len);
//[oStream write:(const uint8_t *)"/n" maxLength:0];
}
/*- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
{
switch(eventCode) {
case NSStreamEventHasSpaceAvailable:
if(stream == oStream) {
dispatch_async(dispatch_get_main_queue(), ^{
#autoreleasepool {
int len;
uint32_t length = (uint32_t)htonl([data length]);
// Sending out the length of the screenshot image and then the image
len=[oStream write:(const uint8_t *)&length maxLength:4];
NSLog(#"len=%d",len);
if (len<0) {
[oStream write:(const uint8_t *)[data bytes] maxLength:length];
}
[oStream write:(const uint8_t *)"/n" maxLength:0];
}
});
}
}
Am I the only one who has experienced that kind of problem? Am I using wrong methods? Any suggestions how to solve the problem?
Here's a screenshot of Instruments when I run the app in it:
#autoreleasepool {
while (true){
...
}
}
The autorelease is never done, it should be placed INSIDE the while loop
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.