简体   繁体   中英

NSInputStream:read:maxLength: on a TCP socket stream returns the error “The operation couldn’t be completed. Bad file descriptor”

I am trying to wrap my head around making a simple TCP server in Xcode, Objective-C on OS X.

It seems like the connection is accepted, and I am getting all the events that I am supposed to get through the event handling, but when I try to read from the input stream inside the event that says that there are data available to be read, I get the following error message:

Error Domain=NSPOSIXErrorDomain Code=9 "The operation couldn't be completed. Bad file descriptor"

My full debug log is:

handleConnect: ACCEPT
<< Open Complete
>> Open Complete
<< Has Bytes Available
Error Reading Stream
Error Domain=NSPOSIXErrorDomain Code=9 "The operation couldn’t be completed. Bad file descriptor"
Length: 0
<< Error Occured
Error Domain=NSPOSIXErrorDomain Code=9 "The operation couldn’t be completed. Bad file descriptor"

You can see that both input and output streams sends the "Open Complete" event, then the input stream sends the "Has Bytes Available" event, but reading from the stream fails. After reading fails, the input stream sends an "Error Occured" event.

I am out of ideas what the problem really is. I am trying to connect with a web browser by the way. Here is the code I have written:

The web server:

#import "WebServer.h"

@implementation WebServer

- (id)init {
    self = [super init];

    if (self != nil) {
        connections = nil;
    }

    return self;
}

- (void)start {

    CFSocketRef myipv4cfsock = CFSocketCreate(
                                          kCFAllocatorDefault,
                                          PF_INET,
                                          SOCK_STREAM,
                                          IPPROTO_TCP,
                                          kCFSocketAcceptCallBack, handleConnect, NULL);

    struct sockaddr_in sin;

    memset(&sin, 0, sizeof(sin));
    sin.sin_len = sizeof(sin);
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8080);
    sin.sin_addr.s_addr= INADDR_ANY;

    CFDataRef sincfd = CFDataCreate(
                                kCFAllocatorDefault,
                                (UInt8 *)&sin,
                                sizeof(sin));

    CFSocketSetAddress(myipv4cfsock, sincfd);
    CFRelease(sincfd);

    CFRunLoopSourceRef socketsource = CFSocketCreateRunLoopSource(
                                                              kCFAllocatorDefault,
                                                              myipv4cfsock,
                                                              0);

    CFRunLoopAddSource(
                   CFRunLoopGetCurrent(),
                   socketsource,
                   kCFRunLoopDefaultMode);
}

void handleConnect(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {

    if (callbackType == kCFSocketAcceptCallBack) {
        printf("handleConnect: ACCEPT\n");

        if (connections == nil) {
            connections = [[NSMutableArray alloc] init];
        }

        CFReadStreamRef rs = NULL;
        CFWriteStreamRef ws = NULL;

        CFStreamCreatePairWithSocket(kCFAllocatorDefault, (CFSocketNativeHandle)data, &rs, &ws);

        WebServerConnection *connection = [[WebServerConnection alloc] initWithInputStream:(__bridge NSInputStream *)rs outputStream:(__bridge NSOutputStream *)ws];
        [connections addObject:connection];

        return;
    }
    else {
        printf("handleConnect: UNKNOWN\n");
    }
}

@end

When someone connects:

#import "WebServerConnection.h"

@implementation WebServerConnection

- (id)init {
    self = [super init];

    if (self != nil) {
        nativeSocket = 0;
        readStream = nil;
        writeStream = nil;
    }

    return self;
}

- (id)initWithInputStream:(NSInputStream *)is outputStream:(NSOutputStream *)os {
    self = [self init];

    if (self != nil) {
        readStream = is;
        writeStream = os;
        [readStream setDelegate:self];
        [writeStream setDelegate:self];
        [readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [readStream open];
        [writeStream open];

        data = nil;
    }

    return self;
}

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    NSString *io = @"??";

    if (aStream == readStream) {
        io = @"<<";
    }

    else if (aStream == writeStream) {
        io = @">>";
    }

    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            printf("%s ", [io UTF8String]);
            printf("Open Complete\n");
            break;

        case NSStreamEventHasBytesAvailable:
        {
            printf("%s ", [io UTF8String]);
            printf("Has Bytes Available\n");

            if (data == nil) {
                data = [[NSMutableData alloc] init];
            }

            uint8_t buffer[1024];

            NSInteger actuallyRead = [readStream read:(uint8_t *)buffer maxLength:sizeof(buffer)];

            if (actuallyRead > 0) {
                [data appendBytes:buffer length:actuallyRead];
            }
            else {
                if (actuallyRead == 0) {
                    printf("End of Data\n");
                }
                else {
                    printf("Error Reading Stream\n");
                    NSError *error = [readStream streamError];
                    printf("%s\n", [[error description] UTF8String]);
                }
            }

            printf("Length: %lu\n", [data length]);

            break;
        }

        case NSStreamEventHasSpaceAvailable:
            printf("%s ", [io UTF8String]);
            printf("Has Space Available\n");
            break;

        case NSStreamEventEndEncountered:
            printf("%s ", [io UTF8String]);
            printf("End Encountered\n");
            break;

        case NSStreamEventErrorOccurred:
        {
            printf("%s ", [io UTF8String]);
            printf("Error Occured\n");
            NSError *error = [aStream streamError];
            printf("%s\n", [[error description] UTF8String]);

            [readStream setDelegate:nil];
            [writeStream setDelegate:nil];
            [readStream close];
            [writeStream close];
            [readStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [writeStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

            break;
        }

        case NSStreamEventNone:
        {
            printf("%s ", [io UTF8String]);
            printf("None\n");
            break;
        }

        default:
            printf("%s ", [io UTF8String]);
            printf("Default Clause\n");
            break;
    }
}

@end

Seems I was passing the wrong data to the 'CFStreamCreatePairWithSocket() function.

    CFStreamCreatePairWithSocket(kCFAllocatorDefault, *(CFSocketNativeHandle *)data, &rs, &ws);

Notice that I forgot to cast *(CFSocketNativeHandle *)data , just sending (CFSocketNativeHandle)data .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM