Sockets in Cocoa
|
|
Thread rating:  |
Ulrich Hobelmann - 18 Jun 2005 16:13 GMT Hi, I'm kind of lost in between the sockets. There's tons of connection classes, Ports, Handles, Protocols etc....
I just want to open a TCP connection to some server and be able to send and receive data (i.e. cstrings or bytes) to it. A pipe would be ok too, so I can talk to a local task.
My futile attempt included creating a NSSocketPort (initRemoteWithTCPPort:21 host:@"localhost"), but the socket (file destriptor is -1 (error??). The documentation says, the socket only connects when data is sent. Ok.
So I create an NSFileHandle (initWithFileDescriptor:), register for notifications, call waitForDataInBackgroundAndNotify on the file handle and wait. But now the whole thing blows up, I guess because the file handle wasn't even valid after all.
So: how DO I create a simple socket connection? How do I get notified when data is there and send stuff back? The context is a Cocoa GUI program (for now just textfield, send button, and message field for server responses).
 Signature Don't let school interfere with your education. -- Mark Twain
Sherm Pendley - 18 Jun 2005 17:40 GMT > Hi, I'm kind of lost in between the sockets. > There's tons of connection classes, Ports, Handles, Protocols etc.... FWIW, Objective-C Protocols have *nothing* to do with networking.
> I just want to open a TCP connection to some server and be able to > send and receive data (i.e. cstrings or bytes) to it. A pipe would be > ok too, so I can talk to a local task. > > My futile attempt included creating a NSSocketPort Stop. NSPort and its descendants are used for sending Mach messages. NSSocketPort is for sending Mach messages over BSD socket, not for sending arbitrary network traffic.
> So I create an NSFileHandle (initWithFileDescriptor:), register for > notifications, call waitForDataInBackgroundAndNotify on the file > handle and wait. But now the whole thing blows up, I guess because > the file handle wasn't even valid after all. Now you're on the right track. How did you create the file descriptor you passed to the initializer? For socket communications, you should be creating it with the socket() function - "man socket" for details.
sherm--
Ulrich Hobelmann - 18 Jun 2005 21:39 GMT >>Hi, I'm kind of lost in between the sockets. >>There's tons of connection classes, Ports, Handles, Protocols etc.... > > FWIW, Objective-C Protocols have *nothing* to do with networking. Oh yeah, I know that :)
It's just that there are some protocols that start with NSURL, so I thought they might be relevant...
>>I just want to open a TCP connection to some server and be able to >>send and receive data (i.e. cstrings or bytes) to it. A pipe would be [quoted text clipped - 5 lines] > NSSocketPort is for sending Mach messages over BSD socket, not for > sending arbitrary network traffic. "NSSocketPort can be used as an endpoint for distributed object connections or raw messaging." says the documentation.
At least I thought the mighty Cocoa would have some builtin socket functions, so I guessed it would be that one.
>>So I create an NSFileHandle (initWithFileDescriptor:), register for >>notifications, call waitForDataInBackgroundAndNotify on the file [quoted text clipped - 4 lines] > you passed to the initializer? For socket communications, you should be > creating it with the socket() function - "man socket" for details. Yes, I actually have some socket() wrappers written in C. If there are no socket functions in Cocoa, I'll use them and wrap them in an NSFileHandle.
Thanks.
 Signature Don't let school interfere with your education. -- Mark Twain
Michael Ash - 18 Jun 2005 20:40 GMT > Hi, I'm kind of lost in between the sockets. > There's tons of connection classes, Ports, Handles, Protocols etc.... [quoted text clipped - 7 lines] > destriptor is -1 (error??). The documentation says, the socket > only connects when data is sent. Ok. Is port 21 open on localhost?
> So I create an NSFileHandle (initWithFileDescriptor:), register > for notifications, call waitForDataInBackgroundAndNotify on the [quoted text clipped - 5 lines] > Cocoa GUI program (for now just textfield, send button, and > message field for server responses). As stated in another post, NSSocketPort is not meant for this kind of thing. I've heard of people using it, so it's possible to make it work, but it's really not recommended.
If you don't need to support 10.2 and under (and you really shouldn't), then check out the NSStream APIs. They're very nice for sockets work if you want to connect to a remote port, and initiating the connection is simple.
If you want your code to be a server and listen for a connection, then you can't use NSStream directly. I believe the most direct path is to use CFSocket to listen for connections, then create a CFStream from that, and CFStream is toll-free bridged to NSStream.
There are also plenty of non-Cocoa alternatives, of course. Plain BSD sockets is an obvious choice, and there's also a fairly wide selection of Objective-C sockets classes, with a list here:
http://www.cocoadev.com/index.pl?SocketClasses
Ulrich Hobelmann - 18 Jun 2005 21:46 GMT > Is port 21 open on localhost? Of course, that's why I chose it. Nicely readable text protocol ;)
> If you don't need to support 10.2 and under (and you really shouldn't), > then check out the NSStream APIs. They're very nice for sockets work if [quoted text clipped - 5 lines] > CFSocket to listen for connections, then create a CFStream from that, and > CFStream is toll-free bridged to NSStream. I'll probably have an extra task running (written in C, portable Unix code), so I'll just talk to it over a socket or pipe. My main problem is how to integrate the stream into the Cocoa event model (because select() would block and so I wouldn't respond to Cocoa events anymore). Well, I hope the file handle or Stream approach works; shouldn't be impossible... :)
 Signature Don't let school interfere with your education. -- Mark Twain
Michael Ash - 19 Jun 2005 11:24 GMT >> Is port 21 open on localhost? > > Of course, that's why I chose it. Nicely readable text protocol ;) It's the programming equivalent of asking the user if their computer is plugged in. Silly, but the simple stuff ends up being the problem surprisingly often.
>> If you don't need to support 10.2 and under (and you really shouldn't), >> then check out the NSStream APIs. They're very nice for sockets work if [quoted text clipped - 12 lines] > Cocoa events anymore). Well, I hope the file handle or Stream > approach works; shouldn't be impossible... :) NSFileHandle should do fine for integrating your socket into the runloop. CFSocket is also good for that if you really want to use the BSD sockets API everywhere possible.
You could also run your sockets stuff using select() in another thread if you really want to keep everything nice and POSIXy.
Ulrich Hobelmann - 19 Jun 2005 13:30 GMT > NSFileHandle should do fine for integrating your socket into the runloop. > CFSocket is also good for that if you really want to use the BSD sockets > API everywhere possible. Yes, works nicely :)
> You could also run your sockets stuff using select() in another thread if > you really want to keep everything nice and POSIXy. Well, since it seems I have to call waitForDataInBackgroundAndNotify again and again (and thus create and kill threads all the time) I might actually roll my own there, select()ing and posting notifications on my own... But for now it's fine.
 Signature Don't let school interfere with your education. -- Mark Twain
David Phillip Oster - 18 Jun 2005 22:20 GMT > Hi, I'm kind of lost in between the sockets. > There's tons of connection classes, Ports, Handles, Protocols etc.... > > I just want to open a TCP connection to some server and be able to > send and receive data (i.e. cstrings or bytes) to it. A pipe > would be ok too, so I can talk to a local task. You are in luck. Apple just released sample code that demonstrates how to do exactly what you are trying to do.
Look at:
http://developer.apple.com/samplecode/CocoaSOAP/CocoaSOAP.html
Pay particular attention to TCPServer.m and SOAPClient.m (which depends heavily on NSURLResponse to actually read the data.
Note that this example gives the source code for a miniature, but functional, http server. This does do SOAP, but there is an easier way to do SOAP with Macintosh.
this may also be useful.
http://developer.apple.com/samplecode/XcodeClientServer/XcodeClientServer.html
Of course, if you can use SOAP or XML-RPC, Apple already provides an appleEvent bridge, so just by changing the destination from another program on your local machine, to one on a remote machine, AESend() will take care of marshalling your arguments and unmarshalling the return value.
See:
http://developer.apple.com/documentation/AppleScript/Conceptual/soapXMLRPC/
The web page is Carbon, but the translation to Cocoa is obvious.
If all you need is SMTP client support, NSMailDelivery is even easier:
<file:///Developer/ADC%20Reference%20Library/documentation/AppleApplications/Reference/MessageFrameworkReference/Classes/NSMailDelivery/index.html>
Note that URL is to documentation that is already on your machine.
There is a trick to getting NSMailDelivery to work: you must explicitly add Message.framework to your project, your program will mysteriously do nothing. Unlike most of the Cocoa frameworks, it isn't inside the Cocoa umbrella, so you don't get it by default.
Also, don't forget that libcurl is available on Mac OS: <http://curl.oc1.mirrors.redwire.net/>
 Signature David Phillip Oster
Matthew Barnes - 25 Jun 2005 23:53 GMT > Now you're on the right track. How did you create the file descriptor > you passed to the initializer? For socket communications, you should > be > creating it with the socket() function - "man socket" for details. Apparently, I have started out on the same track Ulrich did at the beginning. I am writting a chat client for a not so known client with a VERY basic protocol already set in place. At first, I was reading Cocoa In a Nutshell and following the Networking chapter pretty closely. Nothing really worked so now I'm reading through Unix Network Programming.
I just got through going through the streams documentation on Apple.com, but haven't gotten anything to work. Obviously I don't have much experience with this, but figured it wouldn't be hard to catch on since I wrote a client for the same protocol a month ago on Windows.
After reading this thread, and looking up CFSocket, I don't think that will work for me unless there's a way to connect to various ports. Also, I think NSOutputStream isn't working because it isn't using a raw socket. Like I said, I'm not experienced so what do I know.
Should I just stick to socket() used in Unix Network Programming?
* posted via http://www.mymac.ws * please report abuse to http://xinbox.com/mymac
Chris Hanson - 26 Jun 2005 01:43 GMT > After reading this thread, and looking up CFSocket, I don't think > that will work for me unless there's a way to connect to various > ports. Also, I think NSOutputStream isn't working because it isn't > using a raw socket. Like I said, I'm not experienced so what do I > know. I'm not sure what you mean by "raw socket" here.
If you can require Panther or later, I strongly recommend you take another look at NSInputStream and NSOutputStream for TCP communications. It's very easy to use once you understand the way it fits together.
Here's the 30-second summary, written off the top of my head:
(1) Use +[NSStream getStreamsToHost:port:inputStream:outputStream:] to get an NSInputStream and an NSOutputStream to the server you want to communicate with. Be sure to retain them, since they're returned to you autoreleased. (After all, they come from a method that isn't an alloc, copy, or retain method.)
(2) Set your controller object to be your input stream and output stream's delegate. This means that it will get callbacks for events that occur on each of the streams via its -stream:handleEvent: method.
(3) Schedule each stream in the current runloop in the default mode. This means that your streams' operations will be asynchronous, and that you don't need to worry much about them blocking execution of your application as long as you handle the delegate callbacks appropriately.
(4) For your input stream, handle the following events in your controller's -stream:handleEvent: method: NSStreamEventHasBytesAvailable (only read when you get one of these, and only read the number of bytes actually available - you can ask the stream for this info), NSStreamEventEndEncountered (e.g. the other side closed the connection), NSStreamEventErrorOccurred.
(5) For your output stream, handle the following events in your controller's -stream:handleEvent: method: NSStreamEventOpenCompleted (don't try to write before you get this), NSStreamEventHasSpaceAvailable (write when you get one of these, and don't write again until you get another one), NSStreamEventErrorOccurred.
(6) Open your streams.
(6) When you're done, close your streams and release them.
There may be more you need to do, or I may have gotten the steps slightly wrong; after all, this is off the top of my head. So check the documentation here: <http://developer.apple.com/documentation/Cocoa/Conceptual/Streams/index.html>
Fundamentally,
streams provide you with a callback-based system that you can use to read and write network data asynchronously. It might be a little more work than you're expecting if you were planning on working synchronously (e.g. write something, read something, write something, read something) since you'll have to implement a basic queuing system and deal with incomplete reads and writes, but once you build some basic infrastructure around it, it'll be very powerful. And it integrates with Cocoa's run loop mechanism and uses the delegate pattern, meaning you don't have to do learn a separate event-handing or registration system.
-- Chris
Geir-Tore Lindsve - 29 Jun 2005 23:03 GMT A little follow up on this issue.
In may, I set up a small game client which communicates with a server by using NSInputStream and NSOutputStream. That works well, but now I have decided to set it up for another "environment".
I'm planning to rewrite the game to be independent of that server. What I then are planning are to give the user the choice of starting a game, or connect to a existing game when the game opens. I guess that the current use of NSInputStream/NSOutputStream works for connecting to a existing game.
But how can I set up a game to wait for connections from another client (which uses NSInputStream/NSOutputStream), and the set up input/outputstreams to that client?
Any advice?
 Signature Mvh Geir-Tore Lindsve
David Phillip Oster - 30 Jun 2005 04:32 GMT > A little follow up on this issue. > [quoted text clipped - 11 lines] > (which uses NSInputStream/NSOutputStream), and the set up > input/outputstreams to that client? <http://developer.apple.com/samplecode/Cocoa/idxNetworking-date.html> has some good resources for you. Take a look at:
CocoaSOAP - Demonstrates implementing a SOAP client and server in Cocoa. at the tcp/ip level, even includes the source code for a small http server.
<http://developer.apple.com/documentation/Cocoa/Networking-date.html> is also useful.
 Signature David Phillip Oster
Michael Ash - 30 Jun 2005 09:28 GMT > But how can I set up a game to wait for connections from another client > (which uses NSInputStream/NSOutputStream), and the set up > input/outputstreams to that client? As you may have noticed, NSStream doesn't have any APIs to wait for a connection, only to make a connection to somebody else who's waiting.
However, CFStream allows you to create a stream pair for an arbitrary native socket, and CFStream and NSStream are bridged. This means that the main issue is doing the whole listen-and-connect dance, because CFStream expects you to have already done that. You could do that in a separate thread using the standard POSIX APIs, or you could use CFSocket to handle that phase, which has nice runloop integration.
So in summary, CFSocket to listen, CFStream once connected, and then cast to NSStream and do what you have been doing.
Geir-Tore Lindsve - 01 Jul 2005 00:38 GMT >> But how can I set up a game to wait for connections from another client >> (which uses NSInputStream/NSOutputStream), and the set up [quoted text clipped - 12 lines] > So in summary, CFSocket to listen, CFStream once connected, and then > cast to NSStream and do what you have been doing. Thanks for the tip from both of you. I'll be looking into these.
 Signature Mvh Geir-Tore Lindsve
|
|
|