All,
I've run it down to this point by commenting, breakpoints, etc. The program crashes at the marked code.
-(void) initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"192.168.17.1", 2004, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];//WHY MUST YOU CRASH HERE
[outputStream open];//WHY MUST YOU CRASH HERE ALSO!!?!?
NSLog(#"She be opened, sir!");
}
It doesn't crash if I comment out both of those, but it crashes if I comment out either one (so i.e. they are both causing the program to crash). There is no information that gets posted in the debugger either. All it does is send me to main.m and show me
"Thread 1: Program received signal: "EXC_BAD_ACCESS".
Thanks for the help in advance!
Edit: Here is my delegate method, but it doesn't even present the second active line in the log.
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent); //this doesn't post in the log when stream opened...
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 not connect to the host!");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//[theStream release];
theStream = nil;
break;
default:
NSLog(#"Unknown event");
}
}
What is happening is that the instance of the delegate class is being deallocated (causing EXC_BAD_ACCESS in the run loop) either because you didn't retain it, or you are using ARC (pretty likely) and you do not have a reference to it.
The solution is to either call retain on the delegate class, approximately like so:
SomeStreamDelegate *theDelegate = [[SomeStreamDelegate alloc] init];
[theDelegate retain];
Or if you do have ARC enabled, make an instance variable in the class where you alloc the delegate, and store your instance of connection there. That way ARC will not dealloc it, because the instance var counts as a reference.
If you are using ARC, cast the streams like this :
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
This should prevent the crash. And keep in mind that if your streams are owned by a separate thread than the main thread that means the run loop needs to be called manually using run method after opening the streams.
When I placed this in my View Controller (and not in a separate class) it worked perfectly.
I had a similar issue where my app would crash in the -handleEvent callback with a huge streamEvent number. I resolved it by making sure I initialize the NSStream objects (input and output) AND open a connection to the server in the -init method of the NetworkClient object which my VC plans to use.
Try this out once,
NSInputStream * inputStream = objc_unretainedObject(readStream);
May be a casting issue
Related
I find a memory leak when I am testing my app on ios device, look at the code below:
- (void)_startReceive
// Starts a connection to download the current URL.
{
// Open a CFFTPStream for the URL.
CFReadStreamRef ftpStream = CFReadStreamCreateWithFTPURL(NULL, (CFURLRef) url);
assert(ftpStream != NULL);
self.networkStream = (NSInputStream *) ftpStream;
self.networkStream.delegate = self;
[self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:RUNLOOPMODEL];
[self.networkStream open];
CFRelease(ftpStream);
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
// An NSStream delegate callback that's called when events happen on our
// network stream.
{
if (self.networkStream == nil) { //EXC_BAD_ACCESS(code = 1,address=......)
NSLog(#"here");
}
switch (eventCode) {
case NSStreamEventOpenCompleted: {
} break;
case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[LISTDOCBUFFER];
......
}
I use this code to do a ftp request for document information. But only sometimes (one of eight times) the memory leak will happen at the line I note. And On testing on ios simulator, this never happened. I want to know the possible reason and how to fix it?
The reason could be anything but most likely to be invalid memory management. You can analyze your project in your XCode, go to the Project tab and select analyze where the memory leak is actually happening or you can run Profile from the same pathway to detect any particular memory leaks. Check out this link, it is a really cool topic on how to debug memory related issues.
After type casting ftpStream into NSInputStream you are releasing it (CFRelease(ftpStream)) and again using it if (self.networkStream == nil).Do not call CFRelease() on ftpStream and release NSInputStream once you are done with it.
I found some classes on the internet to establish a tcp connection. The link is in here. I want to customize a class and so far I am able to establish a connection send data and receive data which is great. Once I have created and imported the classes used by the first link that I provided I am able to establish a connection using the following method:
-(void) connectToServerUsingCFStream:(NSString *) urlStr portNo: (uint) portNo {
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef) urlStr,
portNo,
&readStream,
&writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
iStream = (NSInputStream *)readStream;
[iStream retain];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = (NSOutputStream *)writeStream;
[oStream retain];
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
}
}
Since I am going to use this class a lot on my application I am creating my own class and I want to create a -(BOOL) connect{} method. I want to return yes is the connection is establish and no otherwise. The problem is that the way I am able to tell if I establish a connection is by creating a connection attempting to send data and then on the server side I have created a method that whenever I receive that I send a string back. If in the next 2 seconds I receive data I know that the connection was establish.
so in my connection method I want to wait 2 seconds and then return a value depending if the BOOL variable didReciveData = YES.
Since you use a NSOutputStream a better approach could be to check the return value of [oStream write:]: if it returns -1 no data has been sent, so there's no connection.
However, if you want to wait two seconds you can use NSTimer to create a timeout. If you receive a response before the timer fires you can invalidate the timer, otherwise the timer will call the related method that will notify the end of the two seconds.
You could try pausing the main run loop.
- (void)test
{
NSLog(#"Test starting.");
BOOL wasSuccessful = [self connect];
NSLog(#"Success: %d", wasSuccessful);
}
- (BOOL)connect
{
// try to connect here, make sure to get a callback on success/failure
// fake callback
[self performSelector:#selector(callback:) withObject:[NSNumber numberWithBool:NO] afterDelay:2.0];
// wait for callback
CFRunLoopRun();
return self.success;
}
- (void)callback:(NSNumber *)successful
{
self.success = [successful boolValue];
CFRunLoopStop(CFRunLoopGetCurrent()); // now we want -connect to return!
}
All,
I have a view controller (let's call it testViewController) that calls a method in another class (class name scan, method name initNetworkCommunication).
Here is the view controller's button method:
-(IBAction) test
{
Scan *canConnect = [[Scan alloc] init];
[canConnect initNetworkCommunication];
}
And here is the class
//scan.h
#interface Scan : NSObject <NSStreamDelegate>
{
NSInputStream *inputStream;
NSOutputStream *outputStream;
}
-(void) scan;
-(void) initNetworkCommunication;
#property (nonatomic, retain) NSInputStream *inputStream;
#property (nonatomic, retain) NSOutputStream *outputStream;
#end
//scan.m
#import "Scan.h"
#implementation Scan
#synthesize inputStream, outputStream;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
-(void) initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"192.168.17.1", 2004, &readStream, &writeStream);
NSLog(#"readStream %#", readStream);
NSLog(#"writeStream %#", writeStream);
inputStream = (NSInputStream *) readStream;
outputStream = (NSOutputStream *) writeStream;//this __strong may work!
NSLog(#"inputStream %#", inputStream);
NSLog(#"outputStream %#", outputStream);
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
NSLog(#"She be opened, sir!");
return;
}
//more code is here, dealloc, etc
I'm getting the EXC_BAD_ACCESS, and when I enable NSZombieEnable, my debugging session looks like this:
2011-07-15 13:09:46.210 Project[1176:f203] readStream <__NSCFInputStream: 0x6e7a2a0>
2011-07-15 13:09:46.212 Project[1176:f203] writeStream <__NSCFOutputStream: 0x6e7a340>
2011-07-15 13:09:46.213 Project[1176:f203] inputStream <__NSCFInputStream: 0x6e7a2a0>
2011-07-15 13:09:46.214 Project[1176:f203] outputStream <__NSCFOutputStream: 0x6e7a340>
2011-07-15 13:09:46.215 Project[1176:f203] She be opened, sir!
2011-07-15 13:09:46.220 Project[1176:f203] *** -[Scan respondsToSelector:]: message sent to deallocated instance 0x6e79b20
Current language: auto; currently objective-c
(gdb)
It crashes at the three asterisks, and that's my zombie message.
What should I do?
EDIT:After running the program in Instruments, I see no memory leaks. The thing that's getting me is showing in Instruments as SocketStream::dispatchSignalFromSocketCallbackUnlocked(SocketStream)
You have forgotten to retain the Scan object that is the stream's delegate.
Until the streams are closed, your stream delegate will receive messages, and if the delegate gets deallocated, then your app will crash.
In your example code (where you're leaking the canConnect object) it should be fine, but in your real code you might be releasing the Scan object too soon.
change inputStream , outputStream to with self with dot syntax everywhere
like this everywhere
because if u synthesize the property u have to use self with dot syntax to retain them .
self.inputStream = (NSInputStream *) readStream;
self.outputStream = (NSOutputStream *) writeStream;//this __strong may work!
i suggest u to change name of function
initNetworkCommunication to some
Just remove inputStream and outputStream from properties and let them stay only class variables.
In case of ARC, it helps.
#interface Scan: NSObject <NSStreamDelegate> {
NSInputStream * inputStream;
NSOutputStream * outputStream;
}
....
#end
In my case, it solved all problems. Was impossible to solve it other way.
i had made the following programming for client server programming but it is not working. the server is not able to receive the request for connection setup.plz help.
#import "clientserverprogramViewController.h"
#import "secondview.h"
#import <CoreFoundation/CFSocket.h>
#include <sys/socket.h>
#include <netinet/in.h>
NSInputStream *iStream;
NSOutputStream *oStream;
#implementation clientserverprogramViewController
#synthesize name,filepath,display;
-(IBAction) print {
NSString *urlStr = serverIP;]
[display setText : urlStr];
if (![urlStr isEqualToString:#""]) {
NSURL *website = [NSURL URLWithString:urlStr];
if (!website) {
NSLog(#"%# is not a valid URL");
return;
}
NSHost *host = [NSHost hostWithName:[website host]];
[NSStream getStreamsToHost:host port:3000 inputStream:&iStream outputStream:&oStream];
[iStream retain];
[oStream retain];
[iStream setDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
[oStream open];
}
}
-(IBAction) settings {
[self presentModalViewController:nextview animated: YES];
}
-(IBAction) cancel {
exit(0);
}
- (void)dealloc {
[super dealloc];
}
#end
You only open streams and don't do anything with them. It's like picking up a phone and not dialing a number. Use NSStreamDelegate protocol to implement data transmission code.
Update:
You have these lines that set the delegate for streams:
[iStream setDelegate:self];
[oStream setDelegate:self];
Now implement methods that are defined in NSStreamDelegate protocol in your own class (AFAIK - there's only one of them). See how to receive/send data from there.
Is there a specific reason you're using streams?
What about using NSURLConnection? Here's a piece of code from a project of mine. Both are in KANetworkManager. KANetworkTransactionType is simply a enum that helps me know how to parse the response.
+ (void) createAndStartUrlConnection:(NSMutableURLRequest *)request type:(KANetworkTransactionType)type target:(id)target callback:(SEL)callback;
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSDictionary *requestDict = [NSDictionary dictionaryWithObjectsAndKeys:request, #"request", [NSNumber numberWithInt:type], #"type", target, #"target", [NSValue valueWithPointer:callback], #"callback", nil];
[KANetworkManager performSelectorInBackground:#selector(makeNetworkCall:) withObject:requestDict];
}
I'm able to made a synchronous network call because I always call this method on its own thread. It's a simpler way to achieve asynchronous network communications without dealing with delegates (although the delegate method provides some benefits). Your parseResponse method would need to be specific to whatever your web service it sending back. parseResponse would notify the callback method. Let me know if you have additional questions regarding this.
+ (void) makeNetworkCall:(NSDictionary *)params
{
// We assume this method won't be called from the main thread, so we need our own NSAutoreleasePool.
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
NSMutableURLRequest *request = [params objectForKey:#"request"];
KANetworkTransactionType type = [(NSNumber *)[params objectForKey:#"type"] intValue];
id target = [params objectForKey:#"target"];
SEL callback = (SEL)[[params objectForKey:#"callback"] pointerValue];
NSURLResponse *response;
NSError *err;
// We make a synchronous request assuming we're on a background thread.
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if (data.length > 0)
{
[self parseResponse:data type:type target:target callback:callback];
}
else
{
NSLog(#"Error occured during network call. %#", err);
}
[autoreleasePool drain];
}
JB gates,
In your code you inform iStream and oStream that your clientserverprogramViewController object is to be the delegate for each. However, a proper delegate needs actual implementation. Your class needs to implement this method:
– stream:handleEvent:
The details are documented here:
Reading From Input Streams
Writing To Output Streams
Also, your code will not work on a real iPhone. There is an updated Core Foundation API for creating the socket pair, details here.
Update
Just wondering if this is not a software issue but maybe the server is behind a firewall. Please give details what the server is, ie webserver, netcat, or simple TCP socket, etc.
Peter
I have read a lot of questions regarding this subject on this website however they didn't quiet answer my question. If you can't be ### about my goal or background skip to the question.
My Goal
Is to build a server that can run on Mac OS X 10.4+ and later, port it to Windows XP/Vista (no idea how to do that yet, but that's a problem for later).
Then let the iPhone be the client that is able to see the computer names that are running the server (through WiFi). The user of the iPhone can then select the computer name to connect to the server on that computer.
After that they can send simple text messages to each other. For example, the iPhone sends 'Knock Knock' and the server responds 'Who is there?'. Or a simple client: 'Ping', server responds 'Pong' will do just fine.
Background
I have worked with sockets in the past, but only in Visual Basic 6 with the WINSOCKET.dll it was very easy to create a TCP/IP server.
server.host = localhost;
server.port = 12203;
server.listen();
With the client I only needed to do the following to connect.
client.connect(localhost, 12203);
There were some callbacks available like connect, close, dataArrival, etc. which I could use to do anything I want.
Perhaps for the iPhone there are libraries written for it, but is it that hard to create this simple application yourself? After doing some research I understand that I have to look in the area of CFNetwork, CFHost, CFSocket, CFStream.
Question
Is there anyone that could guide me to a tutorial or post the code where you have two buttons on the iPhone. [ Start Server ] and [ Connect to Server] where the first will start a TCP/IP server on a certain port and the second connects to it.
After a connection has been made maybe also the code to send a simple 'Ping'-message to the server after the server receives this responds with a 'Pong'-message to the client.
That would really be helpful. But maybe I am asking for to much here.
this tutorial to create a chat sample app works very well and is pretty straightforward (any iphone noob, like me, can make it work, EVEN IN SIMULATOR MODE it connects to external socket server).
i adapted it to talk my socket server and it works like a charm. this is test code, so there's no real concern with loose ends. it only sends one message (your logon id) and receives an answer back, which it shows in the console.
//
// ViewController.m
// zdelSocketTest01a
//
//
#import "ViewController.h"
#implementation ViewController
#synthesize inputNameField;
#synthesize joinView;
- (void)initNetworkCommunication {
uint portNo = 5555;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"227.3.4.56", portNo, &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)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initNetworkCommunication];
messages = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[self setInputNameField:nil];
[self setJoinView:nil];
[self setJoinView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)joinChat:(id)sender {
NSString *response = [NSString stringWithFormat:#"logon,%#", inputNameField.text];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
/*
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent);
}
*/
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
typedef enum {
NSStreamEventNone = 0,
NSStreamEventOpenCompleted = 1 << 0,
NSStreamEventHasBytesAvailable = 1 << 1,
NSStreamEventHasSpaceAvailable = 1 << 2,
NSStreamEventErrorOccurred = 1 << 3,
NSStreamEventEndEncountered = 1 << 4
};
uint8_t buffer[1024];
int len;
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened now");
break;
case NSStreamEventHasBytesAvailable:
NSLog(#"has bytes");
if (theStream == inputStream) {
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);
}
}
}
} else {
NSLog(#"it is NOT theStream == inputStream");
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Stream has space available now");
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
break;
default:
NSLog(#"Unknown event %i", streamEvent);
}
}
/*
- (void) messageReceived:(NSString *)message {
[messages addObject:message];
[self.tView reloadData];
}
*/
#end
your ViewController.h file would contain
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <NSStreamDelegate>
#property (weak, nonatomic) IBOutlet UITextField *inputNameField;
#property (weak, nonatomic) IBOutlet UIView *joinView;
- (IBAction)joinChat:(id)sender;
#end
NSInputStream *inputStream;
NSOutputStream *outputStream;
NSMutableArray * messages;
NOOBS ONLY: you must link your button and text field by pressing CONTROL and dragging the object into the code window. when you do that, the properties above will automatically be created. check this video tutorial if you are stumped
NOOBS ONLY 2: this socket will output in the CONSOLE PANE of XCODE. on the upper right hand corner of your xcode window, click HIDE OR SHOW THE DEBUG AREA (ask for help if necessary).
built and tested (simulator and device) on a macbook with 2GB memory, using xcode 4.2 for snow leopard.
I recommend the the following:
Cocoa Async Socket
There's also a basic example project on the site to get you started. I've had good success working with that framework.
I would expect you would want your server to already be started, and then you would only need a "Connect to Server" button, and then your "Ping". Otherwise, you need a separate process on your server box which responds to the "Start Server" message and starts the server.