Method calling - Cocoa/Obj-C - objective-c

I'm learning Cocoa...
I tried different way to do that but I'm still in the black...
I have this method in my implementation:
- (void)closeStream:(NSStream *)theStream {
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
How can I call it from an IBAction in my #synthetize?
- (IBAction)connect:(id)sender {
if ([[connectNOK stringValue] isEqualToString:#"Disconnected"]) {
[connectButton setTitle:#"Disconnect"];
NSString * hostFromField = [hostField stringValue];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)hostFromField, [portField intValue], &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];
[outputStream open];
} else {
[connectButton setTitle:#"Connect"];
// I want to call this method here
}
}

If the closeStream: method is defined in the same class than the connect: method, you'll have to use:
[ self closeStream: someStream ];
Where someStream is the NSStream object you need to pass.
The self keyword refers to the current instance of the class.
If you don't know that, or what it means, I suggest you take a look at the Objective-C language basics before trying to do/code anything, and maybe after, the complete language reference.
EDIT:
I can see in your code that your connect: method «toggles» the connection based on the value of a button label.
This is not a very good design, for you to know, but you'll have other problems here.
I guess you want to close the input and output streams, if necessary.
The problem is, when the connect method is called a second time, the inputStream and outputStream variables are not accessible anymore, as they are local variables.
You probably need to store them as instance variables instead, so you can refer to them later on.
Once again, it seems you really should start by reading some documentation about programming principles, as well as some Object-Oriented programming principles.
Don't try to go too fast. Knowledge is the key to everything, so start by reading the documentation I mentioned before.

Related

NSInputStream from URL doesn't seem to be getting anything

I've got the following code, which I know is being run:
ReadDelegate * del = [[ReadDelegate alloc] init];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)(#"server.com"), port, &readStream, &writeStream);
NSInputStream * readSock = (__bridge_transfer NSInputStream*)readStream;
[readSock setDelegate:del];
[readSock scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
NSOutputStream * writeSock = (__bridge_transfer NSOutputStream*)writeStream;
[writeSock scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
NSLog(#"Open socket");
[readSock open];
[writeSock open];
[writeSock write:(uint8_t*)("request\0\0\0") maxLength:10];
while (YES) {
//I'm skipping over inconsequential stuff
}
NSLog(#"finished reading");
[readSock close];
[writeSock close];
return [del getMessage];
My ReadDelegate class is declared like #interface ReadDelegate : NSObject <NSStreamDelegate> and includes a - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode. That particular function just has a print statement in it to see if it's ever being called. It's not.
I know for a fact that the connection is being opened because my server is receiving the "request\0\0\0" message and the server is sending the file (I have tests in other environments which can receive the file just fine).
However, as mentioned, the ReadDelegate object declared in the beginning (del) never receives the stream message, even once (to say the stream is open or whatever).
Why is the delegate not being called?
It looks like your stream doesn't receive events because of your while loop.
Every new event from a stream can be handled in a new iteration of run loop. But the new iteration can not be started because the current one never finishes.

Running NSStream in background Thread

I'm currently trying to run my whole network stuff in a background thread, as it currently blocks the main thread, when the server is not reachable (i.e.).
I'm currently creating the network connection through the following code. Is there a simple way to run this in a new background thread?
An how can I throw back the received message to the main thread? An how can I send messages through the background thread?
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)ipAdress, port, &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];
here is a tutorial that does exactly what you're talking about. although it focuses more on audio streaming, but the principles are exactly the same (ie in terms of spawning a worker thread, having it talk with the parent thread etc etc).
the idea is simple.. you create a new thread and have it handle the streaming work, and then you schedule the stream reader with the run loop that belongs to the thread you just created. The stream will have callbacks that will be fired when ceratain events happen (ie you get some data, the connect times out etc).. in the callback methods you can alert or communicate with the mainthread (which is the thread that handles the UI).
Here is some code to point you in the right direction, but if you download the code from the above tutorial and follow through.. you'll get it:
// create a new thread
internalThread =
[[NSThread alloc]
initWithTarget:self
selector:#selector(startInternal)
object:nil];
[internalThread start];
// creating a stream inside the 'startInternal' thread*
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
// open stream
CFReadStreamOpen(stream)
// set callback functions
// ie say: if there are bites available in the stream, fire a callback etc
CFStreamClientContext context = {0, self, NULL, NULL, NULL};
CFReadStreamSetClient(
stream,
kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
ASReadStreamCallBack,
&context);
// schedule stream in current thread runloop, so that we DON'T block the mainthread
CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
// create the callback function to handle reading from stream
// NOTE: see where else in the code this function is named (ie CFReadStreamSetClient)
static void ASReadStreamCallBack
(
CFReadStreamRef aStream,
CFStreamEventType eventType,
void* inClientInfo
)
{
//handle events you registered above
// ie
if (eventType == kCFStreamEventHasBytesAvailable) {
// handle network data here..
..
// if something goes wrong, create an alert and run it through the main thread:
UIAlertView *alert = [
[[UIAlertView alloc]
initWithTitle:title
message:message
delegate:self
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles: nil]
autorelease];
[alert
performSelector:#selector(show)
onThread:[NSThread mainThread]
withObject:nil
waitUntilDone:NO];
}
}

manage socket stream in tabBarView

I'm developing an app using UITabBarController. More specifically, using the storyBoard. I want all of my tab to be able to send and receive data from the server.
Problem is i don't know how. Only the first tab that have initNetworkCommunications is able to send and receive from the server. So what should i do in order for my app be to be able to send and receive from the other tabs?
I've found that using NSNotificationCentre to handle the data would work but is there another way?
Here's the code for creating the socket connection
-(void)initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"169.254.1.1", 2000, &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];
[outputStream open];
}
lets say i have 2 tabs. The first tab has a connect button which is used to call initNetworkCommunication. From this tab i'm able to send and receive data. But what do i do with the other tab? Is there a way to link this connection over?
i've tried to import each other's controller and use [FirstController sendMessage]; from the secondViewController but doesn't seem to work.
Creating a singleton is fine, what I've done is instead of making a class function (which would force your network to re-init the connection every time you switch tabs) I make the networkconnector a property on a custom implementation of tabBar:
#import <Foundation/Foundation.h>
#import "NetworkController.h"
#interface NetworkStorageTabBarController : UITabBarController
#property (nonatomic, strong) NetworkController *thisNetworkController;
#end
And the implementation file:
#import "NetworkStorageTabBarController.h"
#implementation NetworkStorageTabBarController
#synthesize thisNetworkController;
#end
Then when I load up my tabbed view, I call this in viewWillAppear of the first view that will appear:
//set up networking
NetworkStorageTabBarController *thisTabBar = (NetworkStorageTabBarController *) self.tabBarController;
self.thisNetworkController = thisTabBar.thisNetworkController;
self.thisNetworkController.delegate = self;
So far, this has worked gloriously for me. Took me forever to figure it out, so I hope this helps!
The simplest way is to create a Singleton, let's call it NetworkCommunications.
To make it Singleton (only one instance will be created):
+(NetworkCommunications *)sharedManager {
static dispatch_once_t pred;
static NetworkCommunications *shared = nil;
dispatch_once(&pred, ^{
shared = [[NetworkCommunications alloc] init];
});
return shared;
}
Then you simply call [NetworkCommunications sharedManager] from your tabs to get access to that single instance.
You put your network code in that instance as well.

Speak to a device using TCP/IP

I try to open a TCP stream to speak to a device with a cocoa app.
I searched the web and found that there is some possibilities to do that but i'm a little bit stuck.
I decided to use the NSStream way (because it's referenced in cocoa-touch, will be usefull if i want to port my app to iPhone i presume), so here is my code:
#implementation AppDelegate
- (IBAction)connect:(id)sender {
[NSStream getStreamsToHost:"192.168.1.4" port:23 inputStream:&inputStream outputStream:&outputStream];
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
// Both streams call this when events happen
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
if (aStream == inputStream) {
[self handleInputStreamEvent:eventCode];
} else if (aStream == outputStream) {
[self handleOutputStreamEvent:eventCode];
}
}
- (void)handleInputStreamEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventHasBytesAvailable:[self readBytes];
break;
case NSStreamEventOpenCompleted:
// Do Something
break;
default:
case NSStreamEventErrorOccurred:
NSLog(#"An error occurred on the input stream.");
break;
}
}
So, when I click on my connect button, it is supposed to open the stream to my host and make my 2 objects (inputstream and outputstream)
The first step I would like to reach is to have the inputStream in a NSTextView and know if the host has been reached or not... but i'm still stuck :(
If someone can light my way, it would be nice! I'm new on Stack Overflow and I'll be glad to help the community on somethings that i know much! :)
I updated my code and it seems that the light is coming, slowly but it's coming :)
I made a stream to a telnet server. I got the "hello" in a texview.
Now, I would like to send the user & password to be able to send commands to the server, but here is my "send user & pass" button code:
- (IBAction)sendusername:(id)sender {
NSString * usernameMsg = [NSString stringWithFormat:#"user #", [usernameField stringValue]];
NSData * usertosend = [[NSData alloc] initWithData:[usernameMsg dataUsingEncoding:NSUTF8StringEncoding]];
[outputStream write:[usertosend bytes] maxLength:[usertosend length]];
}
Follow my searchs, the server should respond me a thing like "user +ok" but nothing...
2 stranges things:
- If I open the socket to a FTP server of SSH server, I've always the "hello" response without problem. But in telnet, 90% of connections respond me a strange hello like this: "ÿýÿýÿûÿû", why?
When I send the user, nothing happen, only an unrocognized event from the handleEvent...
I can suggest you to look at https://github.com/robbiehanson/CocoaAsyncSocket/, a nice Objective-C wrapper for BSD sockets. It allows you to handle the send-receive interactions on the event loop in nice and clean callback manner (it even handles custom "message terminating" symbols for you allowing to concentrate more on actual packet handling rather then on combining and splitting the stuff you receive from wire).

NSStreamEventEndEncountered not happen for NSInputStream when read content from file

I created a NSInputStream to load content from a file(IOS):
NSString* fileName = [[NSBundle mainBundle] pathForResource:#"resource" ofType:#".dat"];
NSInputStream* dataStream = [NSInputStream inputStreamWithFileAtPath:fileName];
if (dataStream == nil) {
NSLog(#"load asset failed");
return;
}
[dataStream setDelegate:self];
[dataStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[dataStream open];
Then, add event handler:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventEndEncountered: {
[stream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
break;
}
}
}
I want to catch the event:NSStreamEventEndEncountered, but not happend. I can only catch NSStreamEventOpenCompleted and NSStreamEventHasBytesAvailable.
Anything wrong? Thanks for any help!
I can't see anything wrong with the code you've posted. Make sure that when you're finished with the stream that you are closing it yourself rather than simply relying on getting an NSStreamEventEndEncountered notification, which you can do simply with something like this:
- (void) disconnect {
// Close all open streams
[inputStream close];
[outputStream close];
}
You'll usually only get NSStreamEventEndEncountered if the connection is closed by the other end of the stream, which depending on what you're doing may be beyond your control.
I just ran in to this. Replace NSStreamEventEndEncountered with 4 in the switch/case statement.
NSStreamEventEndEncountered as an NSStream enum doesn't end up being caught in a case statement.
None of these answers are correct. To trigger the NSStreamEventEndEncountered event, you must attempt to read data from the input stream when there is no data to read (in other words, when the paired output stream stops writing data).