Discussion:
BSD socket recv() Error failure after 14336 bytes transfered 98% of the time
(too old to reply)
OSX Developer
2002-03-19 06:48:03 UTC
Permalink
[ORIGINAL macosx-***@omnigroup.com THREAD SUBJECT:]
Re: BSD socket recv() Error failure after 14336 bytes transferred 98% of the time
___________________________________________________________________________________
Socket Bug Update with example code.

[MAIL PROBLEM APPOLOGIES]
First my apologies for taken so long to respond, it seems that this mail accounts domains provider (maildeliverer.com or something like that) was unable to realize they turned on the option to deny relaying for all the domains it receives email for. Ultimately all incoming mail from outside got relaying denied errors. To anybody replying to me at this time I am sorry.
Also if replying to this thread make sure you send to macosx-***@omnigroup.com also just in case it is still not working correctly all the time, and then I will check the archive later.
So it seems to me right now, even the worlds best domain-names (www.com) free email provider is incapable of configuring a mail program correctly and actually testing it after modifications. So its no surprise to me that there are problems with OSXs socket system, or my ability to code with it (in this case I am using an example socket class to work from). Again sorry for the inconvenience and delay. Also this message has about 6 pages of explanation and 34 pages of source code so sorry for that also (it mostly seemed required, some is not).

[MESSAGE THREAD CONTINUES]
Okay so I spent a couple of days, trying to figure out what I have done wrong, as was suggested (again) repeatedly by everyone in the group (that responded - thanks).
Always I experience an error simply by moving the mouse into the Dock's area and triggering the docks sizing animation. This causes my send and receive socket program test loops to crash. It seemed to me that this was a far more serious problem then something in my code that would case that to happen. (Likely a resource pause=cause and effect, but still people think that it is my fault.) Anyway I am still confused as to how anyone can even begin to think it something in my code under this kind of proof and that of the OSX 10.1.3 Update partly alleviating the problem.

So I figured if it is me I will strip down the problem to enough of a core that I can sent out sample code of the problem (below).
I am doing a PDO (Portable Distributed Objects) System, and to say the least have an extremely complex situation, so to isolate the possible problem I did this in this order (with no solutions uncovered) ...

Initially I removed the Framework (by putting copies of all the framework code into the Hub-Server, Server, and Client programs). Then I removed the Hub_Server (for centralized [and eventually it will be a decentralized or shared] global name registration database control program). Then I deactivated the NSThreading and the other connection registration maintenance threads, (and a few other ingenious features).
That did not leave much that I could strip out, so I started from the other end ... scratch again.
I removed all Connection or main PDO functionality and other object files and was left with a core socket and subclass of it. These I merged into one class and renamed it.

[!A BIG PARAGRAPH (or three) ABOUT THE ORIGINAL SOURCE OF THIS EXAMPLE SMALLSOCKETS!]
[Eventually I discovered the original source of the class SmallSockets was also suffering from this problem. SmallSockets can be found at smallsockets.sourceforge.net I believe. I modified it somewhat and after a fair amount of changes felt it was not necessary to include the formal disclaimers etc. (taking what I needed from it as a lesson I must have figured, and since it was meant as private code such a concept became obsolete clutter at the time). Now the SmallSocket guy got me started, and I thank him for it. I would say that this example, which I have commented out lots of alternate things I tried to make it work with, has been modified some. I added methods and fixed others. Ultimately the main difference is in his readData: method which was to be called in a while loop I added the while loop to the readData method itself (and have suggested to him that he do that to), also I fixed the isReadable method to include an example from a helper in this group who's main diff!
erence in his example was to use select() to test if it was ready for reading. This is claimed to be more efficient/correct, but I do not think it is necessary nor does it affect/resolve this problem/error. I attempted to put a loop around isReadable and called it relentlessly to make sure the data was ready, same with the isWriteable method. Otherwise these unnecessary add ons are the only main difference between SmallSockets classes and MYPDO_Socket class.
Further I can say please do not contact the SmallSocket guy about my bug I have been in contact with him, and will let him know of anything important he should know. (I may send him a Bcc: of this message, and forward any solutions etc.) I think some modification to these Bug Example _main.m files is needed to integrate with the original SmallSocket classes. Mainly you should increase the BufferSize to 2,000,000 (2 Million) bytes so that the while[socket readData]){} call is not needed to recreate this problem (or you'll need to wrap my readData methods with a while loop).
Also important is I changed all the Exception raising parts to output comments and return NO or nil. and included a debugLevel to control the extensive commenting. Sometimes when a connection is not made or something closes a socket it causes itself or the other end to report an error, which can be misleading, but is the normal function of the sockets/program and not an error. Although I believe this is only the case in the full implementation of MYPDO_Framework and Server, Client usage of connecting to the Hub_Server for name registration and the lookup thereof (along with connection/registration verification and port availability tests etc.) I only lately notice something in SmallSockets called a BufferedSocket, but I do not see much need for this, nor have I experimented with it. While a comment does exist that says I should be using it possibly, I cannot see any need for it. Nor do I feel using it would change this problem.]

[SOCKET RECV() BUG REPRODUCTION EXAMPLE]
Okay so I finally put together the most basic example of this. I suspect that the acceptConnectionAndKeepListening method is possibly responsible (but then again I think this error happens with just acceptConnection also). Ultimately I require it done in the form of the acceptConnectionAndKeepListening way. In this way the original socket is left listening and a new socket from it is created for communication. This is about the only more complicated difference then your average simple example that is able to transfer data without a problem (agreed).
The other thing this does (but is probably not necessary either) is it loops the request expecting one of them to eventually fail, or it to fail when you move the mouse into the dock (without even clicking) as occurred every time with my original MYPDO_Framework, Hub_Server, BugTest-Server, and BugTest-Client projects open and running (in an ever increasing amount of data transferring loop, designed to discover the limit and in the case of 10.1.3 new higher limit).

![IMPORTANT]!
The most important thing to mention, is related to the fact that the OSX (Server) 10.1.3 Update has alleviated this problem in comparison to 10.1.2 performance. Originally I claimed/experienced failure with anything over 15KB (Kilobytes) of data, now the bar has been raised to 1,000,000 (one million) bytes (well done Apple, Thanks). So this example works fine with the minimal default "Hello World" string. It is when you include an argument when executing the SocketSend's (the Server, not the Client) main.m files compiled program. This then transmits the data from the file I am not including the file, but the one I use was easy to make by typing and cut and pasting text in the TextEdit Application until the file exceeds 1MB (One Megabyte) but not 2MB (the size of the buffer).
[{[I wanted to add I have NSMutableString class extensions for setting a string safely, because if you had not noticed (very different from the OpenStep spec.) setString: raises an exception if set with nil, so [fileContentsString setString:[NSString stringWithContentsOfFile:@"/tmp/OverAMegabytes.txt"]]; will fail/error/crash without the file being there, unless you wrap it with an [NSString stringWithString:] like so
[fileContentsString setString:[NSString stringWithString:[NSString stringWithContentsOfFile:@"/tmp/OverAMegabytes.txt"]]];. Oh sure you can use the NSFileManager to test for it first, but in a multi-process multi-threaded environment and the fact that anything like [someDictionary objectForKey:@"ValishedAfterTesting"] will also crash a setString: call. I find there is just no guarantee that setString: will not be set to nil so if you want to wrap them with exception handlers instead, go ahead. I found the NSString Class Extension was a better way, but it complains in the compiler (yet another bug, within a bug solution). But we are not here to discuss that, I just felt it important to understand since on of the first lines in the server codes main file does this kind of messy work-around (it's just a temporary fast hack, not perfect coding, like some of this example and it's commented out attempts.)]}]

[OVERLOAD FILE SIZE REQUIREMENTS - CRITICAL TO SIMULATING THIS BUG]
The file I use is 1215500 bytes long with the buffer set to 2000000. The buffer does not need to be that big but it helps because the comments (via NSLoging) show how much data has been received and sent, and with a small buffer you get a large list of receives that are the size of the buffer, so it is easiest if you make sure the buffer exceeds the data file being sent. Further because one of the differences between the (original) SmallSockets and MYPDO_Socket class is the need to call while([socket readData:response]){} in SmallSockets, so putting a large BufferSize means no possible problem with repeated reading [recv()] of one write [send()], and better compatibility for my example _main.m's with the Original SmallSockets Classes.

[INCLUDED]
So Included is the shared MYPDO_Socket.h and MYPDO_Socket.m files, these are needed/included/added in the two Server and Client Foundation Tool Program ProjectBuilder Projects with their respective SocketSend_main.m and SocketRecv_main.m main files (the starting program execution points. [Duh!])
These are separated by 1-4 commented and numbered //#-_________- lines which are not required (omit-able). [Duh! stuff some more]

[INSTRUCTIONS]
Make the SocketSend_main.m and the SocketRecv_main.m files into separate Foundation Tools via ProjectBuilder, which require adding the two MYPDO_Socket.* files MYPDO_Socket.h and MYPDO_Socket.m
Then usually one can press the Apple=Command and Atl=Option modifier keys with the 'e' key to get to the Arguments Tab and Add and select a large text file exceeding one megabyte (mine is 1215500 bytes long) in size.

First run the program with no arguments to verify it works. I noticed sometimes (all the time, unlike OpenStep again) ProjectBuilder can get caught in a loop that will not allow you to stop a running program in which case you must find the PID via the Terminal.app and kill -9 the program, which then usually starts again because you tried to click the stop button but were locked out and it picks it up after it is killed manually from the command line. If you have limited RAM this can actual start swapping so badly there is no way to recover without hitting the reset button. I have 1GB of RAM and still this has happened to me when testing the original size limit finding (size expanding loop) program, possible relating to the fact the OSX does not seem to be properly freeing released memory (more bugs or expected results) from the actual stack/heap (yet as suggested by others, not a topic to discuss here again also), until the program terminates.

Now if you can run it (without problem) it is time to run it with the large file (or larger and larger files until your system hits it's limit). Mine is a G3 Blue and White 300MHz, so if you have Dual G4 1GHz processors you might find the limit is greater, in which case I would like to here about it and collect the data for officially bug reporting it to Apple. This may also be affected by your total RAM and other process activity.
I have (the real) sendmail, a few domains running DNS via "named", and both a Secure and Non-Secure Apache 1.3.20 Web Sever running with maximum waiting/standby threads. This is loaded but not extremely busy. I did get reverse lookups right finally and use itself as the primary DNS for it's own lookups.

[NOTE]
I did just go back now that I have the OSX (Server - NO Classic installed of course, it does not come with Server anyway.) 10.1.3 Update and try the original NSConnections again, which failed to connect to anything but host:nil and that hung when running the client in the Apache cgi-bin, Ouch! Now it works even less in 10.1.3 I found it will not even setProxyObject: (I think it was). So MYPDO is still required (besides it's advantages/pros of global reach and customizability, it does have it's disadvantages/cons), if it can be made to work with large amounts of data reliable, (I still plan to wrap SSL in the socket transmission at some point).

[FINAL WORDS]
I considered getting permission from the Corporation I work for to provide the person with the answer to the problem with a copy of the PDO_Framework (as a reward). Unfortunately it is not really perfect because of other bugs to do with NSMethodSignatures with multiple arguments not being understood by NSArchiver, and NSInvocations not being able to return the passed argument pointers with updated information, so I just return an NSArchive (NSData) of the return value after invoking the NSInvocation passed through MYPDO_DistributedObject via this MYPDO_Socket Class, etc.
So I do not think it is ready yet as a total replacement for PDO, alas it does work for us and our (planned) purposes, ... if I send all arguments to a method as one argument inside a dictionary and normally I an only returning one argument an NSString, but one should be able to return an NSDictionary also but not any other type then an id (NSObject or subclasses thereof). It's safe to say there are so many bugs in OSX only I have found a way to work around the problems and implement this PDO in this way as I/we require, you are likely better off trying to figure out Omnigroups Frameworks, which did not seem to be the right thing, or possible/easy to figure out for my (adaptable) needs, etc. (I think there must be other possibilities I am mostly unaware of.)
However if the person that can solve this problem without another upgrade or patches, but simply by modifying this example bug code, would really like to or needs to have some form of PDO working (everybody!? I figured.) I think I can arrange somehow to get you at least the MYPDO.Framework (under a none re-distributable legal agreement), but not the source code, if you would like it as a reward for your effort. Otherwise I will be in debt to you and owe you one.
I suppose the biggest reward would be to prove me an idiot/wrong and for that I would be grateful. Unfortunately the circumstances of the mouse movement in the dock and 10.1.3 alleviating the problem, my tracking the problem to the recv() function, and other reasons, ... prove without a shadow of a doubt this is a Apple Kernel issue/problem. Which will be submitted to the Apple BugReporter in some fashion eventually (soon).
Now with a public example nobody can say your doing it wrong, and Apple will be able to reproduce this problem and make it more stable (better yet actually fix it so it is 100% reliable, at least within the same host, or two machines without a network problem).

[REQUEST THE PROJECT FILES PERSONALLY]
I can and will send the Project Files in .dmg or .tar.gz format for you upon request if you do not want to put these source files together yourself. Which I kind of expect, but for those who cannot wait they are included below (it's easy). Both the Original SmallSockets and the modified MYPDO_Socket versions exist, so ask for one or the other, or both, and what format (.dmg or .tar.gz) you prefer. (If my mailer can fit it into it's limits.)

[SOURCE FILES - listed Below]
1- MYPDO_Socket.h
2- MYPDO_Socket.m
3- SocketSend_main.m
4- SocketRecv_main.m
[({ Good Luck and thank-you for you time and/or ear })]
Please report to me, osx-***@www.com your findings, be they good or bad.

//1-____________________________________________________________________-1
//
// MYPDO_Socket.h
//
// Created by root on Mon Dec 17 2001.
// Copyright (c) 2001 Illegal Copy - Private/Hidden Corporation. All rights reserved.
//

#import <Foundation/Foundation.h>

// MYPDO_SOCKETS_VERSION is the version number of CoreSockets in binary-coded
// decimal. (ie; version 1.3.2 == 0x0132)

#define MYPDO_SOCKETS_VERSION 0x0111

// SOCKET_DEFAULT_READ_BUFFER_SIZE is the default size of the buffer
// used by readData, which all the other read calls are built upon.
// readData will not read more than this amount in a single call.
// You can change this buffer size on a per-socket basis by
// calling -setReadBufferSize

// #define SOCKET_DEFAULT_READ_BUFFER_SIZE 4096 // original
// #define SOCKET_DEFAULT_READ_BUFFER_SIZE 65536
//#define SOCKET_DEFAULT_READ_BUFFER_SIZE 8192
#define SOCKET_DEFAULT_READ_BUFFER_SIZE 2000000

// SOCKET_MAX_PENDING_CONNECTIONS is the maximum number of pending connections
// that should be allowed during a listen operation before connections start
// being refused. You can specify a different number by using
// -listenOnPort:maxPendingConnections: instead of -listenToPort: which will
// use this default value.

#define SOCKET_MAX_PENDING_CONNECTIONS 2

// The following defines are strings used to raise exceptions.
// The _F versions are formatting strings for the exception's description.
// The %s is replaced by the value of strerror(errno) which usually gives
// a pretty good idea of what went wrong.
// Actually because exceptions suck now they are just the errors and error handling is passed through the requestor chain, and the formatted versions are obsolete.

#define SOCKET_EX_ACCEPT_FAILED @"Socket: Accept failed"
// #define SOCKET_EX_ACCEPT_FAILED_F @"Socket: Accept failed: %s"
#define SOCKET_EX_ALREADY_CONNECTED @"Socket: Already connected"
#define SOCKET_EX_BAD_SOCKET_DESCRIPTOR @"Socket: Bad socket descriptor"
#define SOCKET_EX_BIND_FAILED @"Socket: Bind failed"
// #define SOCKET_EX_BIND_FAILED_F @"Socket: Bind failed: %s"
#define SOCKET_EX_CANT_CREATE_SOCKET @"Socket: Can't create socket"
// #define SOCKET_EX_CANT_CREATE_SOCKET_F @"Socket: Can't create socket: %s"
#define SOCKET_EX_CONNECT_FAILED @"Socket: Connect failed"
// #define SOCKET_EX_CONNECT_FAILED_F @"Socket: Connect failed: %s"
#define SOCKET_EX_FCNTL_FAILED @"Socket: Fcntl failed"
// #define SOCKET_EX_FCNTL_FAILED_F @"Socket: Fcntl failed: %s"
#define SOCKET_EX_HOST_NOT_FOUND @"Socket: Host not found"
// #define SOCKET_EX_HOST_NOT_FOUND_F @"Socket: Host not found: %s"
#define SOCKET_EX_INVALID_BUFFER @"Socket: Invalid buffer"
#define SOCKET_EX_LISTEN_FAILED @"Socket: Listen failed"
// #define SOCKET_EX_LISTEN_FAILED_F @"Socket: Listen failed: %s"
#define SOCKET_EX_MALLOC_FAILED @"Socket: Malloc failed"
#define SOCKET_EX_NOT_CONNECTED @"Socket: Not connected"
#define SOCKET_EX_NOT_LISTENING @"Socket: Not listening"
#define SOCKET_EX_RECV_FAILED @"Socket: Recv failed"
// #define SOCKET_EX_RECV_FAILED_F @"Socket: Recv failed: %s"
#define SOCKET_EX_SELECT_FAILED @"Socket: Select failed"
// #define SOCKET_EX_SELECT_FAILED_F @"Socket: Select failed: %s"
#define SOCKET_EX_SEND_FAILED @"Socket: Send failed"
// #define SOCKET_EX_SEND_FAILED_F @"Socket: Send failed: %s"
#define SOCKET_EX_SETSOCKOPT_FAILED @"Socket: Setsockopt failed"
// #define SOCKET_EX_SETSOCKOPT_FAILED_F @"Socket: Setsockopt failed: %s"

// Default, uninitialized values for instance variables

#define SOCKET_INVALID_PORT 0
#define SOCKET_INVALID_DESCRIPTOR -1

// MYPDO_Socket interface
//
// MYPDO_Socket is an abstract base class, intended to provide functionality
// that is common to its subclasses. You should not be creating MYPDO_Sockets
// in your code. More likely, you want to create a Socket or BufferedSocket,
// both of which inherit from this class.

@interface MYPDO_Socket : NSObject
{
BOOL connected;
BOOL listening;
void *readBuffer;
unsigned int readBufferSize;
NSString *remoteHostName;
unsigned short remotePort;
int socketfd;

int debugLevel;
}

// Class utilities

+ (MYPDO_Socket*)socket;
+ (NSString *)dottedIPFromAddress:(struct in_addr *)address;

// Designated initializer

- (id)init;
- (void)dealloc;

// Private initializer, do not use

- (id)initWithFD:(int)fd remoteAddress:(struct sockaddr_in *)remoteAddress;

// Accessor functions

- (unsigned int)readBufferSize;
- (int)readData:(NSMutableData *)data;
- (NSString *)remoteHostName;
- (unsigned short)remotePort;

// Connection management

- (void)close;
- (BOOL)isConnected;

// Making connections

- (BOOL)connectToHostName:(NSString*)hostName port:(unsigned short)port;
- (BOOL)isThisAnIPAddress:(NSString *)nameOrAddress;

// Receiving connections

- (BOOL)acceptConnection;
- (MYPDO_Socket*)acceptConnectionAndKeepListening;
- (id)setProxyServer;
- (void)setRequestTimeout:(NSTimeInterval)seconds;
- (void)setReplyTimeout:(NSTimeInterval)seconds;

- (BOOL)bindTo:(u_int32_t)address port:(unsigned short)port;
- (void)listenOnPort:(unsigned short)port;
- (BOOL)listenOnPort:(unsigned short)port maxPendingConnections:(unsigned int)maxPendingConnections;

// Reading and writing data

- (BOOL)waitTellIsReadable;
- (BOOL)isReadable;
- (BOOL)waitTellIsWritable;
- (BOOL)isWritable;
- (BOOL)writeData:(NSData *)data;
- (BOOL)writeString:(NSString *)string;

// Utility functions

- (BOOL)setBlocking:(BOOL)shouldBlock;
- (void)setReadBufferSize:(unsigned int)size;

// Internal utility function. You should not call this from client code.

- (BOOL)allocReadBuffer;

- (int)returnSocketfd;

- (int)currentDebugLevel;
- (void)setDebugLevel:(int)level;

@end



//2-____________________________________________________________________-2




//
// MYPDO_Socket.m
//
// Created by root on Mon Dec 17 2001.
// Copyright (c) 2001 Illegal Copy - Private/Hidden Corporation. All rights reserved.
//


#import "MYPDO_Socket.h"

#import <fcntl.h>
#import <netdb.h>
#import <netinet/in.h>
#import <sys/socket.h>
#import <sys/time.h>
#import <sys/types.h>
#import <arpa/inet.h>
#import <unistd.h>


@implementation MYPDO_Socket

+ (MYPDO_Socket*)socket
//
// Creates a Socket for you, and returns it.
// The new Socket will be autoreleased, so be sure to retain it if you
// need to keep it around.
//
// Note: Sockets are blocking by default.
//
{
//return [[[MYPDO_Socket alloc] init] autorelease];
return [[MYPDO_Socket alloc] init];
}

+ (NSString *)dottedIPFromAddress:(struct in_addr*)address;
//
// Utility function that returns a dotted IP address string from a
// 32-bit network address
//
{
/*
if(inet_ntoa(*address)) {
return [NSString stringWithCString:inet_ntoa(*address)];
}
else {
return [NSString stringWithString:@""];
}
*/
return [NSString stringWithCString:inet_ntoa(*address)];
}

- (id)init {
struct timeval tv;
//int sockOptVal = 1;
//struct linger li;

if(![super init]) {
return nil;
}

connected = NO;
listening = NO;
readBuffer = NULL;
readBufferSize = SOCKET_DEFAULT_READ_BUFFER_SIZE;
remoteHostName = NULL;
remotePort = SOCKET_INVALID_PORT;

debugLevel = 2;

// Create socket

if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
//if((socketfd = socket(AF_INET, SOCK_STREAM, SO_ACCEPTCONN)) < 0) {
//[NSException raise:SOCKET_EX_CANT_CREATE_SOCKET format:SOCKET_EX_CANT_CREATE_SOCKET_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_CANT_CREATE_SOCKET, strerror(errno)]);
return nil;
}
//int setsockopt(int s, int level, int optname, const void *optval, int optlen)
tv.tv_sec = 227;
tv.tv_usec = 0;

if(setsockopt(socketfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_SNDTIMEO) failed in MYPDO_Socket-init");
}
if(setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_RCVTIMEO) failed in MYPDO_Socket-init");
}

/*
if(setsockopt(socketfd, SOL_SOCKET, SO_OOBINLINE, &sockOptVal, sizeof(sockOptVal)) != 0) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_OOBINLINE) failed in MYPDO_Socket-init");
}
*/

/*
li.l_onoff = 1;
li.l_linger = 900;
if(setsockopt(socketfd, SOL_SOCKET, SO_LINGER, &li, sizeof(li)) != 0) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_LINGER) failed in MYPDO_Socket-init");
}
*/
//debugTimeout = 0;

[self allocReadBuffer];

return self;
}

- (void)dealloc
//
// Do not call this method directly! Use retain & release.
//
{
[self close];

if ( readBuffer )
{
free(readBuffer);
readBuffer = NULL;
}

[super dealloc];
}

- (id)initWithFD:(int)fd remoteAddress:(struct sockaddr_in*)remoteAddress
//
// Inits a Socket with an existing, connected socket file descriptor. This is really
// only intended for internal use (see -acceptConnectionAndKeepListening)
//
{
if ( ![super init] )
return nil;

connected = YES;
listening = NO;
readBuffer = NULL;
readBufferSize = SOCKET_DEFAULT_READ_BUFFER_SIZE;
remoteHostName = [[MYPDO_Socket dottedIPFromAddress:&remoteAddress->sin_addr] retain];
remotePort = remoteAddress->sin_port;
debugLevel = 2;

socketfd = fd;

[self allocReadBuffer];

/* WARNING: SET BLOCKING to NO is critical for development break points */
/* INTERMITENT SOLUTION Actually it worked three times and that was it, must have been something else. Very strange ! */
//[self setBlocking:YES];

return self;
}

- (BOOL)acceptConnection
//
// Accept a connection on this socket, if it is listening. May block if
// no connections are pending. The existing listening socket will be destroyed, and
// replaced with the socket that is connected to the remote host.
//
// If you want to keep the listening socket around, try acceptConnectionAndKeepListening
//
{
struct sockaddr_in acceptAddr;
int socketfd2 = SOCKET_INVALID_DESCRIPTOR;
int addrSize = sizeof(acceptAddr);

// Socket must be created, not connected, and listening

if(socketfd == SOCKET_INVALID_DESCRIPTOR) {
//[NSException raise:SOCKET_EX_BAD_SOCKET_DESCRIPTOR format:SOCKET_EX_BAD_SOCKET_DESCRIPTOR];
if(debugLevel > 0) NSLog(SOCKET_EX_BAD_SOCKET_DESCRIPTOR);
return NO;
}

if(connected) {
//[NSException raise:SOCKET_EX_ALREADY_CONNECTED format:SOCKET_EX_ALREADY_CONNECTED];
if(debugLevel > 0) NSLog(SOCKET_EX_ALREADY_CONNECTED);
return NO;
}

if(!listening) {
//[NSException raise:SOCKET_EX_NOT_LISTENING format:SOCKET_EX_NOT_LISTENING];
if(debugLevel > 0) NSLog(SOCKET_EX_NOT_LISTENING);
return NO;
}

// Accept a remote connection. Raise on failure

socketfd2 = accept(socketfd, (struct sockaddr*)&acceptAddr, &addrSize);

if(socketfd2 < 0) {
//[NSException raise:SOCKET_EX_ACCEPT_FAILED format:SOCKET_EX_ACCEPT_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_ACCEPT_FAILED, strerror(errno)]);
return NO;
}

// Replace existing socket descriptor with newly obtained one

[self close];

remotePort = acceptAddr.sin_port;
remoteHostName = [[MYPDO_Socket dottedIPFromAddress:&acceptAddr.sin_addr] retain];

socketfd = socketfd2;
connected = YES;
listening = NO;

return YES;
}

- (MYPDO_Socket *)acceptConnectionAndKeepListening
//
// Accept a connection on this socket, if it is listening. May block if
// no connections are pending.
//
// This variant of acceptConnection will return the connection to the remote host
// to you as a new Socket. Meanwhile, this existing Socket will continue to
// listen for connections.
//
{
//MYPDO_Socket *returnNewSocket;
struct sockaddr_in acceptAddr;
int socketfd2 = SOCKET_INVALID_DESCRIPTOR;
int addrSize = sizeof(acceptAddr);

// Socket must be created, not connected, and listening

if(socketfd == SOCKET_INVALID_DESCRIPTOR) {
//[NSException raise:SOCKET_EX_BAD_SOCKET_DESCRIPTOR format:SOCKET_EX_BAD_SOCKET_DESCRIPTOR];
if(debugLevel > 0) NSLog(SOCKET_EX_BAD_SOCKET_DESCRIPTOR);
return nil;
}

if(connected) {
//[NSException raise:SOCKET_EX_ALREADY_CONNECTED format:SOCKET_EX_ALREADY_CONNECTED];
if(debugLevel > 0) NSLog(SOCKET_EX_ALREADY_CONNECTED);
return nil;
}

if(!listening) {
//[NSException raise:SOCKET_EX_NOT_LISTENING format:SOCKET_EX_NOT_LISTENING];
if(debugLevel > 0) NSLog(SOCKET_EX_NOT_LISTENING);
return nil;
}
// Accept a remote connection. Raise on failure

socketfd2 = accept(socketfd, (struct sockaddr*)&acceptAddr, &addrSize);

if (socketfd2 < 0) {
//[NSException raise:SOCKET_EX_ACCEPT_FAILED format:SOCKET_EX_ACCEPT_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_ACCEPT_FAILED, strerror(errno)]);
return nil;
}

//returnNewSocket = [[[MYPDO_Socket alloc] initWithFD:socketfd2 remoteAddress:&acceptAddr] autorelease];
//if(socketfd2 != SOCKET_INVALID_DESCRIPTOR) {
// close(socketfd2);
// socketfd2 = SOCKET_INVALID_DESCRIPTOR;
//}

//return [[[MYPDO_Socket alloc] initWithFD:socketfd2 remoteAddress:&acceptAddr] autorelease];
return [[MYPDO_Socket alloc] initWithFD:socketfd2 remoteAddress:&acceptAddr];
}

- (id)setProxyServer {
return self;
}

- (void)setRequestTimeout:(NSTimeInterval)seconds {
struct timeval tv;

if(seconds > 226) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_RCVTIMEO) limited to 227 MYPDO_Socket-setTimeout");
tv.tv_sec = 226;
}
else {
tv.tv_sec = (int)seconds;
}
tv.tv_usec = 0;

if(setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_RCVTIMEO) failed in MYPDO_Socket-setTimeout");
}
}

- (void)setReplyTimeout:(NSTimeInterval)seconds {
struct timeval tv;

if(seconds > 226) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_SNDTIMEO) limited to 227 MYPDO_Socket-setTimeout");
tv.tv_sec = 226;
}
else {
tv.tv_sec = (int)seconds;
}
tv.tv_usec = 0;

if(setsockopt(socketfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
if(debugLevel > 1) NSLog(@"setsockopt(SO_SNDTIMEO) failed in MYPDO_Socket-setTimeout");
}
}

- (BOOL)allocReadBuffer
//
// Internal utility function. You should not call this from client code.
//
{
// Allocate readBuffer

if ( readBuffer == NULL )
{
readBuffer = malloc(readBufferSize);
if(readBuffer == NULL) {
//[NSException raise:SOCKET_EX_MALLOC_FAILED format:SOCKET_EX_MALLOC_FAILED];
if(debugLevel > 0) NSLog(SOCKET_EX_MALLOC_FAILED);
return NO;
}
}

return YES;
}

- (BOOL)bindTo:(u_int32_t)address port:(unsigned short)port
//
// Bind an address to this socket. You normally do not need to
// call this method. See listenOnPort instead.
//
{
struct sockaddr_in localAddr;
int on = 1;

// Set a flag so that this address can be re-used immediately after the connection
// closes. (TCP normally imposes a delay before an address can be re-used.)

if(setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) {
//[NSException raise:SOCKET_EX_SETSOCKOPT_FAILED format:SOCKET_EX_SETSOCKOPT_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_SETSOCKOPT_FAILED, strerror(errno)]);
return NO;
}

// Bind the address to the socket

localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(address);
localAddr.sin_port = htons(port);

if(bind(socketfd, (struct sockaddr*)&localAddr, sizeof(localAddr)) < 0) {
//[NSException raise:SOCKET_EX_BIND_FAILED format:SOCKET_EX_BIND_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_BIND_FAILED, strerror(errno)]);
return NO;
}

return YES;
}

- (void)close
//
// Closes the socket. You generally do not need to call this, as the socket
// will be automatically closed when it is released.
//
{
if ( socketfd != SOCKET_INVALID_DESCRIPTOR )
{
close(socketfd);
socketfd = SOCKET_INVALID_DESCRIPTOR;
}

if ( remoteHostName != NULL )
{
[remoteHostName release];
remoteHostName = NULL;
}

connected = NO;
listening = NO;
remotePort = SOCKET_INVALID_PORT;
}

- (BOOL)connectToHostName:(NSString*)hostName port:(unsigned short)port
//
// Connect the socket to the host specified by hostName, on the requested port.
//
{
struct hostent *remoteHost;
struct sockaddr_in remoteAddr;

//if(debugLevel > 1) NSLog([NSString stringWithFormat:@"connectToHostName:(NSString*)%@ port:(unsigned short)%d", hostName, port]);
if(socketfd == SOCKET_INVALID_DESCRIPTOR) {
//[NSException raise:SOCKET_EX_BAD_SOCKET_DESCRIPTOR format:SOCKET_EX_BAD_SOCKET_DESCRIPTOR];
if(debugLevel > 0) NSLog(SOCKET_EX_BAD_SOCKET_DESCRIPTOR);
return NO;
}

if(connected) {
//[NSException raise:SOCKET_EX_ALREADY_CONNECTED format:SOCKET_EX_ALREADY_CONNECTED];
if(debugLevel > 0) NSLog(SOCKET_EX_ALREADY_CONNECTED);
return NO;
}

// Look up host

if([self isThisAnIPAddress:hostName] == YES) {
remoteAddr.sin_addr.s_addr = inet_addr([hostName cString]);
}
else {
if((remoteHost = gethostbyname([hostName cString])) == NULL) {
//[NSException raise:SOCKET_EX_HOST_NOT_FOUND format:SOCKET_EX_HOST_NOT_FOUND_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_HOST_NOT_FOUND, strerror(errno)]);
return NO;
}
bzero((char*)&remoteAddr, sizeof(remoteAddr));
bcopy((char*)remoteHost->h_addr, (char*)&remoteAddr.sin_addr.s_addr, remoteHost->h_length);
}

remoteAddr.sin_family = AF_INET;
remoteAddr.sin_port = htons(port);

// Request connection, raise on failure

if((connect(socketfd, (struct sockaddr*)&remoteAddr, sizeof(remoteAddr)) < 0)) {
//[NSException raise:SOCKET_EX_CONNECT_FAILED format:SOCKET_EX_CONNECT_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_CONNECT_FAILED, strerror(errno)]);
return NO;
}
// Note successful connection

remoteHostName = [[NSString alloc] initWithString:hostName];
remotePort = port;

connected = YES;

return YES;
}

- (BOOL)isThisAnIPAddress:(NSString *)nameOrAddress {
NSRange *workRange1 = malloc(sizeof(NSRange));
int workInt1 = -1;

*workRange1 = [nameOrAddress rangeOfString:@"."];
if(workRange1->length == 1) {
workInt1 = [[nameOrAddress substringToIndex:workRange1->location] intValue];
if(workInt1 > 0 && workInt1 < 256) {
*workRange1 = [nameOrAddress rangeOfString:@"." options:NSBackwardsSearch];
if(workRange1->length == 1) {
workInt1 = [[nameOrAddress substringFromIndex:(workRange1->location + 1)] intValue];
if(workInt1 > 0 && workInt1 < 256) {
return YES;
}
}
}
}
return NO;
}

- (BOOL)isConnected
//
// Returns whether the socket is connected
//
{
return connected;
}

- (BOOL)waitTellIsReadable {
int testCount = 0;

while([self isReadable] == NO && testCount < 50) {
testCount++;
if(debugLevel > 0) NSLog(@"isReadable returned NO");
usleep(1000);
}
if([self isReadable] == YES) {
return YES;
}
return NO;
}

- (BOOL)isReadable
//
// Returns whether or not data is available at the Socket for reading
//
{
int count;
fd_set readfds;
struct timeval timeout;

// Socket must be created and connected

if(socketfd == SOCKET_INVALID_DESCRIPTOR) {
//[NSException raise:SOCKET_EX_BAD_SOCKET_DESCRIPTOR format:SOCKET_EX_BAD_SOCKET_DESCRIPTOR];
if(debugLevel > 0) NSLog(SOCKET_EX_BAD_SOCKET_DESCRIPTOR);
return NO;
}

if(!connected) {
//[NSException raise:SOCKET_EX_NOT_CONNECTED format:SOCKET_EX_NOT_CONNECTED];
if(debugLevel > 0) NSLog(SOCKET_EX_NOT_CONNECTED);
return NO;
}

// Create a timeout of zero (don't wait) // that's useless timeout is needed

timeout.tv_sec = 0;
timeout.tv_usec = 500000;

// Create a file descriptor set for just this socket

FD_ZERO(&readfds);
FD_SET(socketfd, &readfds);

// Check socket for data

//if(debugTimeout == 0) {
//if(debugLevel > 1) NSLog(@"debugTimeout disabled - read");
count = select(socketfd + 1, &readfds, NULL, NULL, &timeout);
/*
}
else {
if(debugLevel > 1) NSLog(@"debugTimeout disabled - read");
count = select(socketfd + 1, &readfds, NULL, NULL, nil);
}
*/
// Return value of less than 0 indicates error

if(count < 0) {
//[NSException raise:SOCKET_EX_SELECT_FAILED format:SOCKET_EX_SELECT_FAILED_F, strerror(errno)];
if(debugLevel > 0) NSLog(SOCKET_EX_SELECT_FAILED);
return NO;
}

if(FD_ISSET(socketfd, &readfds)) {
return YES;
}
return NO;
// select() returns number of descriptors that matched, so 1 == has data, 0 == no data

// return (count == 1);
//return count; //would be fine
}

- (BOOL)waitTellIsWritable {
int testCount = 0;

while([self isWritable] == NO && testCount < 50) {
testCount++;
if(debugLevel > 0) NSLog(@"isWritable returned NO");
usleep(1000);
}
if([self isWritable] == YES) {
return YES;
}
return NO;
}

- (BOOL)isWritable
//
// Returns whether or not the Socket can be written to
//
{
int count;
fd_set writefds;
struct timeval timeout;

// Socket must be created and connected

if(socketfd == SOCKET_INVALID_DESCRIPTOR) {
//[NSException raise:SOCKET_EX_BAD_SOCKET_DESCRIPTOR format:SOCKET_EX_BAD_SOCKET_DESCRIPTOR];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_BAD_SOCKET_DESCRIPTOR, strerror(errno)]);
return NO;
}
if(!connected) {
//[NSException raise:SOCKET_EX_NOT_CONNECTED format:SOCKET_EX_NOT_CONNECTED];
if(debugLevel > 0) NSLog(SOCKET_EX_NOT_CONNECTED);
return NO;
}
// Create a file descriptor set for just this socket

FD_ZERO(&writefds);
FD_SET(socketfd, &writefds);

// Create a timeout of zero (don't wait)

timeout.tv_sec = 0;
timeout.tv_usec = 0;

// Check socket for data

//if(debugTimeout == 0) {
//if(debugLevel > 1) NSLog(@"debugTimeout disabled - write");
count = select(socketfd + 1, NULL, &writefds, NULL, &timeout);
/*
}
else {
if(debugLevel > 1) NSLog(@"debugTimeout active - write");
count = select(socketfd + 1, NULL, &writefds, NULL, nil);
}
*/
// Return value of less than 0 indicates error

if(count < 0) {
//[NSException raise:SOCKET_EX_SELECT_FAILED format:SOCKET_EX_SELECT_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_SELECT_FAILED, strerror(errno)]);
return NO;
}

// select() returns number of descriptors that matched, so 1 == write OK

return (count == 1);
}

- (void)listenOnPort:(unsigned short)port
//
// Start the socket listening on the given local port number
//
{
[self listenOnPort:port maxPendingConnections:SOCKET_MAX_PENDING_CONNECTIONS];
}

- (BOOL)listenOnPort:(unsigned short)port maxPendingConnections:(unsigned int)maxPendingConnections
//
// Start the socket listening on the given local port number,
// with the given maximum number of pending connections.
//
{
[self bindTo:INADDR_ANY port:port];

if(listen(socketfd, maxPendingConnections) < 0) {
//[NSException raise:SOCKET_EX_LISTEN_FAILED format:SOCKET_EX_LISTEN_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_LISTEN_FAILED, strerror(errno)]);
return NO;
}

listening = YES;

return YES;
}

- (unsigned int)readBufferSize
//
// Returns this Socket's readBuffer size
//
{
return readBufferSize;
}

- (int)readData:(NSMutableData *)data {
if([self waitTellIsReadable] == YES) {
//
// Append any available data from the socket to the supplied buffer.
// Returns number of bytes received. (May be 0)
//
ssize_t count = 0;
ssize_t totalCount = 0;

int gettingData = 1;
// data must not be null ptr

if(data == NULL) {
//[NSException raise:SOCKET_EX_INVALID_BUFFER format:SOCKET_EX_INVALID_BUFFER];
if(debugLevel > 0) NSLog(SOCKET_EX_INVALID_BUFFER);
return nil;
}
// Socket must be created and connected

if(socketfd == SOCKET_INVALID_DESCRIPTOR) {
//[NSException raise:SOCKET_EX_BAD_SOCKET_DESCRIPTOR format:SOCKET_EX_BAD_SOCKET_DESCRIPTOR];
if(debugLevel > 0) NSLog(SOCKET_EX_BAD_SOCKET_DESCRIPTOR);
return nil;
}

if(!connected) {
//[NSException raise:SOCKET_EX_NOT_CONNECTED format:SOCKET_EX_NOT_CONNECTED];
if(debugLevel > 0) NSLog(SOCKET_EX_NOT_CONNECTED);
return nil;
}

/*
if([self isReadable] == NO) {
return nil;
}
*/
[self allocReadBuffer];

// Request a read of as much as we can. Should return immediately if no data.
while(gettingData > 0) {
// Allocate readBuffer if it has never been allocated, or size changed


//count = recv(socketfd, readBuffer, readBufferSize, MSG_OOB); // Errors
//count = recv(socketfd, readBuffer, readBufferSize, MSG_PEEK);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_DONTROUTE);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_TRUNC);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_CTRUNC);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_WAITALL); //Jams
//count = recv(socketfd, readBuffer, readBufferSize, MSG_DONTWAIT); // FreaksOut
//count = recv(socketfd, readBuffer, readBufferSize, MSG_EOF);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_FLUSH);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_HOLD);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_SEND);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_HAVEMORE);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_RCVMORE);
//count = recv(socketfd, readBuffer, readBufferSize, MSG_COMPAT);
/*
if([self isReadable] == NO) {
return totalCount;
}
*/
/*
if(readBuffer) {
free(readBuffer);
readBuffer = NULL;
}
[self allocReadBuffer];
*/


if([self waitTellIsReadable] == NO) {
NSLog(@"ERROR readData failed isReadable tests while reading");
return 0;
}
count = recv(socketfd, readBuffer, readBufferSize, 0);
//count = read(socketfd, readBuffer, readBufferSize);
//[self waitTellIsReadable];
//usleep(1000);

// HOW ABOUT MULTI-COMBOs
//count = recv(socketfd, readBuffer, readBufferSize, (MSG_HAVEMORE | MSG_RCVMORE));
//unknown method, dumb idea one's read one's write anyway


if(debugLevel > 1) NSLog([NSString stringWithFormat:@"Recieved:%d", count]);
if(count > 0) {
// Got some data, append it to user's buffer
totalCount = totalCount + count;
[data appendBytes:readBuffer length:count];
if(count < readBufferSize) {
gettingData = 0;
}
}
else {
if(count == 0) {
// Other side has disconnected, so close down our socket
gettingData = 0;
[self close];
}
else {
// recv() returned an error.
if(errno == EAGAIN) {
// No data available to read (and socket is non-blocking)
count = 0;
}
else {
//[NSException raise:SOCKET_EX_RECV_FAILED format:SOCKET_EX_RECV_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_RECV_FAILED, strerror(errno)]);
return nil;
}
}
}
}
return totalCount;
}
NSLog(@"ERROR readData failed isReadable tests");
return 0;
}

- (NSString *)remoteHostName
//
// Returns the remote hostname that the socket is connected to,
// or NULL if the socket is not connected.
//
{
/*
if(remoteHostName) {
if([remoteHostName isEqualToString:@"127.0.0.1"]) {
return [NSString stringWithString:@"localhost"];
}
}
*/
return remoteHostName;
}


- (unsigned short)remotePort
//
// Returns the remote port number that the socket is connected to,
// or 0 if not connected.
//
{
return remotePort;
}

- (BOOL)setBlocking:(BOOL)shouldBlock
//
// Switch the socket to blocking or non-blocking mode
//
{
int flags;
int result;

flags = fcntl(socketfd, F_GETFL, 0);

if(flags < 0) {
//[NSException raise:SOCKET_EX_FCNTL_FAILED format:SOCKET_EX_FCNTL_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_FCNTL_FAILED, strerror(errno)]);
return NO;
}

if(shouldBlock) {
// Set it to blocking...
result = fcntl(socketfd, F_SETFL, flags & ~O_NONBLOCK );
}
else {
// Set it to non-blocking...
result = fcntl(socketfd, F_SETFL, flags | O_NONBLOCK);
}

if(result < 0) {
//[NSException raise:SOCKET_EX_FCNTL_FAILED format:SOCKET_EX_FCNTL_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_FCNTL_FAILED, strerror(errno)]);
return NO;
}

return YES;
}

- (void)setReadBufferSize:(unsigned int)size
//
// Change the size of the read buffer at runtime
//
{
readBufferSize = size;

if ( readBuffer )
{
free(readBuffer);
readBuffer = NULL;
}

[self allocReadBuffer];
}

- (BOOL)writeData:(NSData *)data
//
// Writes the given data to the socket
//
{
if([self waitTellIsWritable] == YES) {
const char *bytes = [data bytes];
int len = [data length];
int sent;

//debugLevel = 10;
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"Length To Send:%d", len]);
// Socket must be created and connected

if(socketfd == SOCKET_INVALID_DESCRIPTOR) {
//[NSException raise:SOCKET_EX_BAD_SOCKET_DESCRIPTOR format:SOCKET_EX_BAD_SOCKET_DESCRIPTOR];
if(debugLevel > 0) NSLog(SOCKET_EX_BAD_SOCKET_DESCRIPTOR);
return NO;
}

if(!connected) {
//[NSException raise:SOCKET_EX_NOT_CONNECTED format:SOCKET_EX_NOT_CONNECTED];
if(debugLevel > 0) NSLog(SOCKET_EX_NOT_CONNECTED);
return NO;
}

// Send the data

while(len > 0) {
//sent = send(socketfd, bytes, len, MSG_DONTROUTE);
//sent = send(socketfd, bytes, len, MSG_OOB);
/*
if([self waitTellIsWritable] == NO) {
NSLog(@"ERROR writeData failed isWritable tests while sending");
return 0;
}
*/
sent = send(socketfd, bytes, len, 0);
//sent = write(socketfd, bytes, len);
//[self waitTellIsWritable];
//usleep(1000);

if(sent < 0) {
//[NSException raise:SOCKET_EX_SEND_FAILED format:SOCKET_EX_SEND_FAILED_F, strerror(errno)];
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"%@: %s", SOCKET_EX_SEND_FAILED, strerror(errno)]);
return NO;
}

bytes += sent;
len -= sent;
if(debugLevel > 1) NSLog([NSString stringWithFormat:@"Sent:%d Length Left:%d", sent, len]);
}

return YES;
}

NSLog(@"ERROR writeData failed isWritable test");
return NO;
}

- (BOOL)writeString:(NSString *)string
//
// Writes the given string to the socket
//
{
if([string length] > 0) {
if([self writeData:[string dataUsingEncoding:NSUTF8StringEncoding]] == NO) {
return NO;
}
}

return YES;
}

- (int)returnSocketfd {
return socketfd;
}

- (int)currentDebugLevel {
return debugLevel;
}

- (void)setDebugLevel:(int)level {
//debugLevel = level;
}

@end



//3-___________________________________________________________________-3

//
// SocketSend_main.m
//

#import <Foundation/Foundation.h>

#import "MYPDO_Socket.h"

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

// insert code here...
//NSLog(@"Hello, World!");
MYPDO_Socket *socketSend = [MYPDO_Socket socket];
MYPDO_Socket *listeningSocket;
NSMutableData *response = [[NSMutableData alloc] init];
NSString *filesContents;

if(argc == 2) {
filesContents = [NSString stringWithString:[NSString stringWithContentsOfFile:[NSString stringWithCString:argv[1]]]];
}
else {
filesContents = [NSString stringWithString:@"\nHello World, from SocketSend !\n\n"];
}
[socketSend listenOnPort:22222 maxPendingConnections:1];
while(listeningSocket = [socketSend acceptConnectionAndKeepListening]) {
[listeningSocket readData:response];
NSLog([[NSString alloc] initWithData:response encoding:[NSString defaultCStringEncoding]]);
[response setData:[NSData new]];

//[listeningSocket writeString:@"\nHello World, from SocketSend !\n\n"];
[listeningSocket writeString:filesContents];
[listeningSocket release];
}
[socketSend release];

[pool release];
return 0;
}


//4-___________________________________________________________________-4

//
// SocketRecv_main.m
//

#import <Foundation/Foundation.h>

#import "MYPDO_Socket.h"

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

// insert code here...
//NSLog(@"Hello, World!");
NSMutableData *response = [[NSMutableData alloc] init];
while(1) {
MYPDO_Socket *socketRecv = [[MYPDO_Socket alloc] init];

if([socketRecv connectToHostName:@"192.168.1.104" port:22222] == YES) {
[socketRecv writeString:@"Send IT\n"];

[socketRecv readData:response];

// Display response in context textview

//NSLog([[NSString alloc] initWithData:response encoding:[NSString defaultCStringEncoding]]);

[response setData:[NSData new]];
}
else {
break;
}
[socketRecv release];
}

[pool release];
return 0;
}


end-______________________________________________________________-end





------------------------------------------------------------
WWW.COM - Where the Web Begins! http://www.www.com


---------------------------------------------------------------------
Express yourself with a super cool email address from BigMailBox.com.
Hundreds of choices. It's free!
http://www.bigmailbox.com
---------------------------------------------------------------------
Simon Wigzell
2002-03-19 08:32:00 UTC
Permalink
AFAICT, you still haven't read through a single one of the responses you
got earlier; a quick look through your code (not very thorough...) make
it seem that you are still working under the presumption that a recv()
reads all data sent in one chunk. PLEASE read a book about socket, any
book, Sockets For Dummies or whatever.

Haven't it occured to you that it's strange that so many other
developers have no problems whatsoever with sending large chunks of data?

Simon W.
Jeff Koftinoff
2002-03-19 08:40:01 UTC
Permalink
<snip>
Wow. your code is overly complex and inefficient, kinda like your
emails. You're lucky I'm bored and read through your code.

Here is your problem in your method 'readData:'
if(count > 0) {
// Got some data, append it to user's buffer
totalCount = totalCount + count;
[data appendBytes:readBuffer length:count];
if(count < readBufferSize) {
gettingData = 0;
}
}
you set gettingData to 0 if recv() does not read in a full
'readBufferSize' bytes.

Your code is incorrect.

This causes the receive loop to stop reading before the socket is closed.
You can NEVER expect recv() to always return as many bytes as you
request. The 'len' parameter for recv() only specifies that MAXIMUM
that you can allow recv() to give you at once.
Just because recv() returns a number of bytes less than readBufferSize
does not mean you are done reading the socket. You must call recv()
again and again until it returns 0 marking the socket closure.

I haven't tried compiling your code but my suggestion is for you to
delete the line containing 'gettingData = 0;' :

if(count > 0) {
// Got some data, append it to user's buffer
totalCount = totalCount + count;
[data appendBytes:readBuffer length:count];
}
...etc..

Another quick look at your main programs shows that you are making an
important mistake there too with your line:
[listeningSocket readData:response];

If your 'response' is going to be an ascii string like you are using
then your readData: method is not what you want. readData: is going to
read all the data until the socket is closed.

You will want a buffered socket to read an ascii line of text. As I also
This of course means that your receiving code MUST know beforehand how
many bytes to expect. So typically the transmitter will send a header
specifying how many bytes will be sent.
Please please please read the books on programming for tcp/ip and don't
rant anymore. Throw out most of that code because it is very poorly
organised. There are lots of socket libraries out there already that
you can utilize.

I really feel for the guys at Apple who must deal with all the hostile &
incorrect bug reports like this.


Jeff Koftinoff
***@jdkoftinoff.com
www.jdkoftinoff.com

'My friend alex the troll gets all the chicks!' -- me
James Powell
2002-03-19 09:24:01 UTC
Permalink
This guy is doomed. If he can't even grasp the need to keep calling
recv() until all of the data is read, even after it has been explicitly
explained to him on this list, how will he ever come to understand that
recv() may be interrupted by signals so he needs to check for the case
where recv() returns -1 and errno is set to EINTR. I know that EINTR
has already been explained to him on this list also but, since the
implementation of both of these requirements are still missing from his
code . . .
Post by Jeff Koftinoff
I really feel for the guys at Apple who must deal with all the
hostile & incorrect bug reports like this.
Hear hear.

James

"sockets" - the only word our director of marketing could say after a
couple of consultants spent some weeks working on some network deamon in
the cube next door.
j***@att.net
2002-03-19 09:42:01 UTC
Permalink
James,
Post by James Powell
This guy is doomed. If he can't even grasp the need to keep calling
recv() until all of the data is read, even after it has been explicitly
explained to him on this list, how will he ever come to understand that
recv() may be interrupted by signals so he needs to check for the case
where recv() returns -1 and errno is set to EINTR. I know that EINTR
has already been explained to him on this list also but, since the
implementation of both of these requirements are still missing from his
code . . .
I dunno, I think maybe this person is a somewhat-disgruntled OpenStep
developer (albeit one who never dealt with BSD sockets much) who doesn't
particularly like where Apple is taking his platform, for whatever
reason. Still, when I was learning BSD sockets (still am, actually --
i've got the basics down, but i'm always learning new things) I made the
same mistake with recv(), but a very understanding fellow developer
kindly pointed me in the right direction, and I was (still am) very
grateful. I'm a much better sockets programmer now :-)

Regards,

John

Falling You - exploring the beauty of voice and sound
http://www.mp3.com/fallingyou
OSX Developer
2002-03-19 10:30:06 UTC
Permalink
Hello, thanks for the information.

So what I have done is replacd the code that stopped recieving even if the buffersize was not reached during the transmission (before completion). [A strange/difficult and unexpected qwirk in the design.]

I replaced it with the modified (to work from SmallSockets) isReadable method.
if([self isReadable] == NO) {
gettingData = 0;
return totalCount;
}
This is needed after the first recv or it bugs out (hangs).

Now the timeout setting is 500000 much to slow for every connection to wait to see if it's finished.
I shortened it to 500 and 5000, but still it has problems, I suppose this is not the right solution either.

I tried to let recv() return a count of 0 (zero) but it never does this.

The thing is I am using each socket as a two way multi message connection. Basically the client says "hello" the server response "Server Ready", then the client "are you this registered name" and it says back "yes". then the actual PDO part takes place via passing an archived invocation, which the server invokes and returns the return data.
So the problem is I need to be able to have the writeData: send() method send a count of 0 without closing (after the last of the data has been sent it potentially pieces smaller then the buffer size, even if the buffersize exceeds the total data being sent. I figured maybe the Bug is Apple's OSX is not sending a count of zero after send finishes sending all the data. Is this right/wrong?

How should I maintain the connection but achieve a count of zero from recv()? What happens is it hangs waiting for another send().

[My appoligies to the group and Apple for ranting about this, yet it still seems to be causing me much fustration (and appears buggy, or is designed wrong).]

I still do not understand your comments about being able to read a header telling how much data is being sent of the total, that is not part of SmallSockets abilities.

Sounds like I need BigMultiTwoWaySockets, not SmallSockets which seems to be SmallSingleOneWaySockets !



------------------------------------------------------------
WWW.COM - Where the Web Begins! http://www.www.com


---------------------------------------------------------------------
Express yourself with a super cool email address from BigMailBox.com.
Hundreds of choices. It's free!
http://www.bigmailbox.com
---------------------------------------------------------------------
j***@att.net
2002-03-19 10:54:00 UTC
Permalink
OSXDevPerson,
Post by OSX Developer
So the problem is I need to be able to have the writeData: send()
method send a count of 0 without closing (after the last of the data
has been sent it potentially pieces smaller then the buffer size, even
if the buffersize exceeds the total data being sent. I figured maybe
the Bug is Apple's OSX is not sending a count of zero after send
finishes sending all the data. Is this right/wrong?
This is wrong -- the OSX / Darwin BSD sockets implementation is fine (if
you looked at the source I gave you and actually ran it, you would see
that recv() does return 0). What you need, I think, is some sort of app
protocol. For instance, if you want to keep your socket open, but want
to know when messages are done, you need to somehow send extra info
across the socket so the other end can find out if the message is done.
One common way of doing this is by prepending the length of each message
to the message, i.e. the first 2 or 4 bytes. This way, on the other
end, you read these first 2 or 4 bytes first to determine how many
additional bytes you need to read. This isn't a quirk in the BSD
sockets design, this is simply BSD sockets not getting in your way by
making assumptions about how you want to do what you want to do. Yes,
it's low level, you need to build your protocol on top of it. If you
ever want to get good at BSD sockets, and take your experience to other
platforms, you're going to need to understand this. I'll help as much
as I can (because others helped me when I was starting out).

If you like, i'll gladly make a quick thing that demonstrates this.
It's really not that hard. I really don't mean any offense by this, but
you need to stop assuming the bug is in the OS / compiler / other guy,
and start assuming it's in your code. Only by a _very_ exhaustive
research effort can you even _begin_ to assume that it's an OS /
compiler / someone else's bug.

Regards,

John

Falling You - exploring the beauty of voice and sound
http://www.mp3.com/fallingyou
OSX Developer
2002-03-19 11:58:00 UTC
Permalink
I did try your code, and thanks again. I guess it did not represent the full scope of my problem, I tried using the select() idea(s) from it to see if something was readable but this actually did not help find the end reliably.
This prepending of the transmission length sounds like the right solution, then I will know for sure if it has completed the (entire) transmission.
I will report back after I add this idea of prepending the expected length to the data being transmitted, into this code, (I suspect it will take some time).
I now see that there should be way to flush a 0 through. I tried send(socketfd, nil, 0, 0) but no such luck. Are you sure there is not a way to make recv() understand when the end is the end, without prepending information about how many bytes are to be expected? My assumption of (count < bufferSize) meaning the end had occurred was the misleading mistake I had made.

Thanks for helping, I am surprised and how incomplete sockets are, even the SmallSockets class I found. I see now, that it depends on what you are doing with it. I will attempt to make it usable. Although I suspect if it worked well enough for my/this code it would not be a problem for simpler requirements, and cause less grief in general. (Must be a relic of UNIX and C, because I see no reason why the send() recv() socket functions should be incapable of knowing when the desired amount of data has been sent on there own. (Maybe it's good for streaming or something, to not actually receive all the data send over a socket and have a way to confirm when it is completed, without closing the connection.)

Wish me luck; I am sure this thread will help someone else someday also, so thanks from them too.
Date: Tue, 19 Mar 2002 04:53:01 -0800
OSXDevPerson,
Post by OSX Developer
So the problem is I need to be able to have the writeData: send()
method send a count of 0 without closing (after the last of the data
has been sent it potentially pieces smaller then the buffer size, even
if the buffersize exceeds the total data being sent. I figured maybe
the Bug is Apple's OSX is not sending a count of zero after send
finishes sending all the data. Is this right/wrong?
This is wrong -- the OSX / Darwin BSD sockets implementation is fine (if
you looked at the source I gave you and actually ran it, you would see
that recv() does return 0). What you need, I think, is some sort of app
protocol. For instance, if you want to keep your socket open, but want
to know when messages are done, you need to somehow send extra info
across the socket so the other end can find out if the message is done.
One common way of doing this is by prepending the length of each message
to the message, i.e. the first 2 or 4 bytes. This way, on the other
end, you read these first 2 or 4 bytes first to determine how many
additional bytes you need to read. This isn't a quirk in the BSD
sockets design, this is simply BSD sockets not getting in your way by
making assumptions about how you want to do what you want to do. Yes,
it's low level, you need to build your protocol on top of it. If you
ever want to get good at BSD sockets, and take your experience to other
platforms, you're going to need to understand this. I'll help as much
as I can (because others helped me when I was starting out).
If you like, i'll gladly make a quick thing that demonstrates this.
It's really not that hard. I really don't mean any offense by this, but
you need to stop assuming the bug is in the OS / compiler / other guy,
and start assuming it's in your code. Only by a _very_ exhaustive
research effort can you even _begin_ to assume that it's an OS /
compiler / someone else's bug.
Regards,
John
Falling You - exploring the beauty of voice and sound
http://www.mp3.com/fallingyou
------------------------------------------------------------
WWW.COM - Where the Web Begins! http://www.www.com


---------------------------------------------------------------------
Express yourself with a super cool email address from BigMailBox.com.
Hundreds of choices. It's free!
http://www.bigmailbox.com
---------------------------------------------------------------------
James Powell
2002-03-19 12:12:34 UTC
Permalink
Post by OSX Developer
Hello, thanks for the information.
So what I have done is replacd the code that stopped recieving even if
the buffersize was not reached during the transmission (before
completion). [A strange/difficult and unexpected qwirk in the design.]
This is important because recv() won't always block until it reads all
of the data that you have requested. If you have set the socket to
non-blocking mode, as your setBlocking method can do, then recv() will
only return the amount of data that is available in its buffers right
now. Even if the socket is in blocking mode, which is the default,
recv() is allowed to return anywhere from 0 bytes up to the number of
bytes you have requested.

The usual way to handle this is to run recv() in a loop, along these
lines.
Post by OSX Developer
Begin untested code.
// I want to read one million bytes.
int numBytesToRead = 1000000;
// I haven't read any yet.
int bytesRead = 0;
// No failures yet.
int failed = 0;
int recvResult;
while (!failed && bytesRead < numBytesToRead) {
recvResult = recv(...);
if (recvResult == -1) {
// recv reports an error
if (errno == EINTR) {
// recv was interrupted by a signal. keep on reading.
}
else {
failed = errno;
}
}
else
bytesRead += recvResult;
}
Post by OSX Developer
end untested code
I replaced it with the modified (to work from SmallSockets) isReadable
method.
if([self isReadable] == NO) {
gettingData = 0;
return totalCount;
}
This is needed after the first recv or it bugs out (hangs).
This is by design: recv() is allowed to hang until it reads the amount
of data you have requested.
Post by OSX Developer
Now the timeout setting is 500000 much to slow for every connection to
wait to see if it's finished.
I shortened it to 500 and 5000, but still it has problems, I suppose
this is not the right solution either.
Don't worry about timeouts until everything else is working. Unless the
other side of this socket is very slow (5 minutes or more) the defaults
are fine, and as long as you are checking the return value from recv()
you can simply report an error if a timeout does occur.
Post by OSX Developer
I tried to let recv() return a count of 0 (zero) but it never does this.
It doesn't make sense to read 0 bytes from a socket.
Post by OSX Developer
The thing is I am using each socket as a two way multi message
connection. Basically the client says "hello" the server response
"Server Ready", then the client "are you this registered name" and it
says back "yes". then the actual PDO part takes place via passing an
archived invocation, which the server invokes and returns the return
data.
Standard stuff. If you are going to get very elaborate about the PDO
implementation, think about switching to CORBA now. It handles the
entire process, from specifying the protocol using IDL to things like
delivering exceptions across the wire.
Post by OSX Developer
So the problem is I need to be able to have the writeData: send()
method send a count of 0 without closing (after the last of the data
has been sent it potentially pieces smaller then the buffer size, even
if the buffersize exceeds the total data being sent. I figured maybe
the Bug is Apple's OSX is not sending a count of zero after send
finishes sending all the data. Is this right/wrong?
This is wrong, as j.zorko points out in his reply.
Post by OSX Developer
How should I maintain the connection but achieve a count of zero from
recv()? What happens is it hangs waiting for another send().
You have to abandon this line of thought. recv() can never tell you
when one remote procedure call packet has been fully received because it
doesn't know the size or the format of your packet. This is why people
are telling you that you need to transmit the size of the packet as part
of the header.

I don't think that anyone has mentioned it yet but there are really
three ways to "know" when a packet has been fully received.

1) You can transmit the size of the packet before you start to send the
packet. The client side, which is calling recv(), expects the first two
bytes it reads to be an integer value containing the size of the
packet. You can then substitute this value for the value 1,000,000 in
the example code I gave you above.

2) You can transmit a unique signature at the end of the packet. For
example, if you send the string "+++" at the end of each packet, and
ONLY at the end of each packet, then you can add code to your
recv()-calling function which looks at every byte that is read and
terminates when three pluses are read in a row. Note that this is no
good for arbitrary binary data because +++ could appear at any time.
Generally this solution is not used for this reason.

3) You can declare that your packets are a fixed size, for example 1
kilobyte. Then you guarantee that the sender will always send 1024
bytes of data at a time, and your recv()-calling function can stop with
the knowledge of a job well done after reading 1024 bytes. I am
guessing that this solution will not work for you because remote
procedure calls generally have 0..n arguments which, when packaged for
transmission, may or may not fit into 1024 bytes.

So you can take your choice from these three solutions but recv() will
never return 0 at the end of one of your packets because it doesn't know.
Post by OSX Developer
[My appoligies to the group and Apple for ranting about this, yet it
still seems to be causing me much fustration (and appears buggy, or is
designed wrong).]
Right. Please stop and take some deep breaths before you post, and post
as briefly as possible to get your message across. As j.zorko writes,
the bugs are almost always (%99.99 of the time) in your code.

You can also take some consolation in the fact that sockets does have a
steep learning curve. It has never been easy and it will never be
easy. I and others here have gone through the same process that you are
going through. I remember thinking that recv() must be broken because
it is returning fewer bytes that I have requested, and it even has the
gall to return varying numbers of bytes on different machines and under
different conditions. I also remember spending days trying to figure
out why I was getting these EINTR errors while reading - running around
and hassling everyone to find out how the product used signals and
trying to figure out how I was going to read reliably with these signals
coming in - until I finally read some working code and found out that
ignoring the EINTR errors is the standard way to read.
Post by OSX Developer
I still do not understand your comments about being able to read a
header telling how much data is being sent of the total, that is not
part of SmallSockets abilities.
Sounds like I need BigMultiTwoWaySockets, not SmallSockets which seems
to be SmallSingleOneWaySockets !
My advice to you is to trash SmallSockets and Objective C for now and
write a tiny, plain-C program that works. Actually, write two tiny
plain-C programs, one to be the server and one to be the client.

Besides getting a book about TCP/IP and sockets (hint hint), the best
way to learn about this stuff is to look at the things that work:
telnet, ftp, the Apache web server, sendmail, ssh, OmniWeb, etc. all use
sockets in the exact same way that you are trying to use them, and they
all work fine in all versions of OS X.

Wonderfully, source code is available for all of these applications
except for OmniWeb. Seek and ye shall find. In fact, I'll bet that the
source code to OpenPlay has all of the answers you need.

http://www.publicsource.apple.com/projects/openplay

Cheers,
James
James Powell
2002-03-19 13:53:00 UTC
Permalink
Post by j***@att.net
James,
This guy is doomed. [...]
I dunno, I think maybe this person is a somewhat-disgruntled OpenStep
developer (albeit one who never dealt with BSD sockets much) who
doesn't particularly like where Apple is taking his platform, for
whatever reason. Still, when I was learning BSD sockets (still am,
actually -- i've got the basics down, but i'm always learning new
things) I made the same mistake with recv(), but a very understanding
fellow developer kindly pointed me in the right direction, and I was
(still am) very grateful. I'm a much better sockets programmer now :-)
You're right. This list is a great resource for problem solving, and I
want to help him out too. Sorry for declaring you "doomed", OS X dude.

Cheers,
James
John Haager
2002-03-19 14:04:01 UTC
Permalink
Post by OSX Developer
I did try your code, and thanks again. I guess it did not represent the
full scope of my problem, I tried using the select() idea(s) from it to
see if something was readable but this actually did not help find the
end reliably.
It depends on what you are trying to find the end of. If you want to
know when the socket closes, then you call recv repeatedly until it
returns 0. If you are trying to find the end of your message on the
socket, then you will have to use the size header or some other app
level mechanism to mark the end of your message.

The important thing to remember is that, by default, sockets are in
blocking mode. If you tell it to read 65000 bytes, the call will block
until it reads that many bytes (ignoring signals and such). If you only
actually need to know about the first 30 bytes, then you should call
recv with a size of 30 to get those.
Post by OSX Developer
This prepending of the transmission length sounds like the right
solution, then I will know for sure if it has completed the (entire)
transmission.
I will report back after I add this idea of prepending the expected
length to the data being transmitted, into this code, (I suspect it
will take some time).
I now see that there should be way to flush a 0 through. I tried
send(socketfd, nil, 0, 0) but no such luck. Are you sure there is not a
way to make recv() understand when the end is the end, without
prepending information about how many bytes are to be expected? My
assumption of (count < bufferSize) meaning the end had occurred was the
misleading mistake I had made.
The only "end" that the socket can reliably tell you about is the end of
the socket connection. This is reported to you by recv returning 0
bytes read. This is the *only* case in which it returns 0. In every
other case, it will either return the number of bytes read, or it
returns -1 with errno set appropriately.
Post by OSX Developer
Thanks for helping, I am surprised and how incomplete sockets are, even
the SmallSockets class I found. I see now, that it depends on what you
are doing with it. I will attempt to make it usable. Although I suspect
if it worked well enough for my/this code it would not be a problem for
simpler requirements, and cause less grief in general. (Must be a relic
of UNIX and C, because I see no reason why the send() recv() socket
functions should be incapable of knowing when the desired amount of
data has been sent on there own. (Maybe it's good for streaming or
something, to not actually receive all the data send over a socket and
have a way to confirm when it is completed, without closing the
connection.)
BSD Sockets are not incomplete! TCP sockets provide your application a
stream to communicate with a remote system. What the semantics of that
stream are is entirely dependent on your program. It is almost a
requirement that you define your own protocol if you intend to use a
socket for more than just a pipe you plan on dumping to a file or
something. I know. I've written more socket level apps than I care to
think about.
Post by OSX Developer
Wish me luck; I am sure this thread will help someone else someday
also, so thanks from them too.
-> John Haager <-
Mike Cohen
2002-03-19 14:10:58 UTC
Permalink
Post by j***@att.net
James,
Post by James Powell
This guy is doomed. If he can't even grasp the need to keep calling
recv() until all of the data is read, even after it has been explicitly
explained to him on this list, how will he ever come to understand that
recv() may be interrupted by signals so he needs to check for the case
where recv() returns -1 and errno is set to EINTR. I know that EINTR
has already been explained to him on this list also but, since the
implementation of both of these requirements are still missing from his
code . . .
I dunno, I think maybe this person is a somewhat-disgruntled OpenStep
developer (albeit one who never dealt with BSD sockets much) who doesn't
particularly like where Apple is taking his platform, for whatever
reason. Still, when I was learning BSD sockets (still am, actually --
i've got the basics down, but i'm always learning new things) I made the
same mistake with recv(), but a very understanding fellow developer
kindly pointed me in the right direction, and I was (still am) very
grateful. I'm a much better sockets programmer now :-)
What are the problems & disadvantages of using read() & write() for a socket
instead of recv() & send()?

My application needs to use either a serial/modem connection or an IP
connection to the server. Both of my connection classes are based on a
common subclass which uses read & write to communicate with the server.
--
[ Mike Cohen | http://www.macmegasite.com/ ]
[ ***@topicbox.com | http://www.worldbeatplanet.com/ ]

Sound is the same for all the world - Youssou N'dour, "Eyes Open"
James Powell
2002-03-19 14:44:01 UTC
Permalink
Post by Mike Cohen
What are the problems & disadvantages of using read() & write() for a
socket
instead of recv() & send()?
The advantage is reusability. The same code works for files, named
pipes, and sockets. I don't know of any disadvantage to using
read/write with sockets.

If you're writing a server daemon that is invoked by inetd, which is by
far the easiest way to deal with all of the forking and multithreading
things that servers need to do, then you must use read and write because
your process just reads and writes from stdin/stdout.
Post by Mike Cohen
My application needs to use either a serial/modem connection or an IP
connection to the server. Both of my connection classes are based on a
common subclass which uses read & write to communicate with the server.
I use the Communications Toolbox to do this under OS 9 and earlier and
it works great. OpenPlay is the new CTB in the course of
CTB->NetSprocket->OpenPlay evolution, but it doesn't look like OpenPlay
handles serial comm for OS X. I don't even know what serial is like
under X (/dev/cu.modem?) but I'll bet read/write works.

Cheers,
James
j***@att.net
2002-03-19 14:52:01 UTC
Permalink
John,
Post by John Haager
BSD Sockets are not incomplete! TCP sockets provide your application a
stream to communicate with a remote system. What the semantics of that
stream are is entirely dependent on your program. It is almost a
requirement that you define your own protocol if you intend to use a
socket for more than just a pipe you plan on dumping to a file or
something. I know. I've written more socket level apps than I care to
think about.
Looking back, I think I could have done things a little better to help
this guy. The sample code I sent him was a simple
server-streams-to-client, client-dumps-to-file example i.e. doing 'cmp
source-file destination-file' would be cool. Also, back then, the issue
seemed to be that he could never get more tha 14k bytes or so from a
single recv() -- the code I sent demonstrated that recv() can return
much more than that. Only later did I realize that he expected recv()
to do things that recv() Just Don't Do, i.e. returning the whole message
at once, returning the requested number of bytes at all times. I should
have added these to the source I sent him when I did.

Sigh, I wasn't proactive enough. Damn, damn, double damn :-) OSX
person -- i'm working on an example that illustrates the message length
prepend, but I got _real_ tired (as did my iBook -- battery was down to
12 minutes I think <g>) last night / this morning. I'll have it done
soon, if you're still interested.

Regards,

John

Falling You - exploring the beauty of voice and sound
http://www.mp3.com/fallingyou
j***@att.net
2002-03-19 15:17:33 UTC
Permalink
OSXDevPerson,
Post by OSX Developer
Thanks for helping, I am surprised and how incomplete sockets are, even
the SmallSockets class I found. I see now, that it depends on what you
are doing with it. I will attempt to make it usable. Although I suspect
if it worked well enough for my/this code it would not be a problem for
simpler requirements, and cause less grief in general. (Must be a relic
of UNIX and C, because I see no reason why the send() recv() socket
functions should be incapable of knowing when the desired amount of
data has been sent on there own. (Maybe it's good for streaming or
something, to not actually receive all the data send over a socket and
have a way to confirm when it is completed, without closing the
connection.
It's not that BSD sockets are incomplete, it's that they have _no_ idea
what you want to do and so make no assumptions. They give you the bare
essentials, you build on top of them what you need. Think of recv() as
fread() but with a file on another box that you have no control over.
fread() doesn't _always_ return the # of bytes you ask for -- if there
are only n bytes left in the file, it will return n, even though n may
be less than what you asked for. In sockets-land, if select() says
you've got a FD set (i.e. data waiting), all that means is that you have
at least 1 byte waiting for you to read. What if the other computer is
slow? What if it's on a modem and you're on a LAN? It's generally
considered bad form to hang a thread unless there is a very good reason
(I had some recent issues with the Microsoft Windows Media Format SDK
that resulted in one of their APIs hanging my thread, and I demonstrated
it to them, they admitted the problem and provided a functional
workaround), so BSD sockets try not to, even if it means you get less
than what you asked for. It's not a deficiency in BSD sockets, it was
designed to provide the basis for about any networking / streaming
application, with as few assumptions about said applications as possible.
Post by OSX Developer
Wish me luck; I am sure this thread will help someone else someday
also, so thanks from them too.
Luck really isn't involved -- you'll get it.

Regards,

John

Falling You - exploring the beauty of voice and sound
http://www.mp3.com/fallingyou

Continue reading on narkive:
Loading...