文件描述符不适用于Objective-C(创建电子邮件客户端)中的dispatch_read()和dispatch_write()。

问题描述 投票:1回答:1

我想创建使用IMAP协议的电子邮件客户端。我正在使用Apple的Network框架通过imap.gmail.com创建TCP连接。

Spoiler!我使用了official tutorial(使用网络框架实现netcat)。

我的应用程序界面非常简单:

  • 电子邮件占位符
  • 密码占位符
  • 登录按钮

所有写在ViewController.m中的方法

当我按下登录按钮时:

- (IBAction)loginButtonPressed:(id)sender { 
    const char *hostname = "imap.gmail.com";
    const char *port = "imaps";
    NSString *tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"myapptempfile.XXXXXX"];
    const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
    char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
    strcpy(tempFileNameCString, tempFileTemplateCString);
    dispatch_fd_t fd = mkstemp(tempFileNameCString); // Creating temp file descriptor
    free(tempFileNameCString);
    NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:NO]; // Creating tempFile to use this file descriptor
    nw_parameters_t parameters = nw_parameters_create_secure_tcp(NW_PARAMETERS_DEFAULT_CONFIGURATION, NW_PARAMETERS_DEFAULT_CONFIGURATION);
    nw_endpoint_t endpoint = nw_endpoint_create_host(hostname, port);
    nw_connection_t connection = nw_connection_create(endpoint, parameters);
    [self start_connection:connection: tempFileHandle: fd]; // Start connection
}

有人认为没有必要将“ fd”变量传递给我的连接方法。我想您是绝对正确的,但是我在文件描述符中遇到了一些问题,这是我最后一次尝试了解发生了什么情况。

然后我尝试开始连接,一切都就在那里。

- (void)start_connection:(nw_connection_t)connection :(NSFileHandle *)file :(dispatch_fd_t)fd {
    nw_connection_set_queue(connection, dispatch_get_main_queue());
    nw_connection_set_state_changed_handler(connection, ^(nw_connection_state_t state, nw_error_t error) {
        switch (state) {
            case nw_connection_state_waiting:
                NSLog(@"waiting");
                break;
            case nw_connection_state_failed:
                NSLog(@"failed");
                break;
            case nw_connection_state_ready:
                NSLog(@"connection is ready");
                [self receive_loop:connection: file :fd];
                break;
            case nw_connection_state_cancelled:
                NSLog(@"connection is cancelled");
                break;
            default:
                break;
        }
    });
    nw_connection_start(connection);
}

我在Xcode中的控制台仅显示:“连接已准备就绪”。

如您所见,我们将调用“带有3个参数的receive_loop方法”,让我们看一下该方法:

- (void)receive_loop:(nw_connection_t)connection :(NSFileHandle *)file :(dispatch_fd_t)fd {
    nw_connection_receive(connection, 1, UINT32_MAX, ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) {

        dispatch_block_t schedule_next_receive = ^{
            if (is_complete &&
                context != NULL && nw_content_context_get_is_final(context)) {
                exit(0);
            }
            if (receive_error == NULL) {
                [self receive_loop:connection: file :fd];
            } else {
                NSLog(@"Error");
            }
        };

        if (content != NULL) {
            dispatch_write(fd, content, dispatch_get_main_queue(), ^(__unused dispatch_data_t _Nullable data, int stdout_error) {
                if (stdout_error != 0) {
                    NSLog(@"Error in receive loop");
                } else {
                    NSString* str = @"00000001 LOGIN [email protected] mypasswordhere";
                    NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
                    [file writeData:data];
                    [self send_loop:connection: file :fd];
                    schedule_next_receive();
                }
            });
        } else {
            // No content, so directly schedule the next receive
            schedule_next_receive();
        }
    });
}

现在最有趣的是:最初,我在dispath_write()函数中使用STDOUT_FILENO代替了“ fd”或(非常重要的)“ file.fileDescriptor”。好吧,当我使用STDOUT_FILENO时,我的控制台仅打印类似“ * OK Gimap准备好接收来自109.252.29.37 m5mb106667441ljg的请求”的内容如您所见,我正在尝试将其写入“ fd”或“ file”变量中(两者均无效,尝试读取fd或文件后出现“ NULL”)。

所以,这就是我尝试发送信息的方式,但这没关系,因为我无法写入我的fd,因此我无法将数据发送到imap.gmail.com ...

我的send_loop方法:

- (void)send_loop:(nw_connection_t)connection :(NSFileHandle *)file :(dispatch_fd_t)fd {
    dispatch_read(fd, 8192, dispatch_get_main_queue(), ^(dispatch_data_t _Nonnull read_data, int stdin_error) {
        if (stdin_error != 0) {
//            … error logging …
        } else if (read_data == NULL) {
//            … handle end of file …
        } else {
            nw_connection_send(connection, read_data, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t  _Nullable error) {
                if (error != NULL) {
//                    … error logging …
                } else {
                    NSLog(@"send loop %@", (NSData *)read_data);
                    [self send_loop:connection: file :fd];
                }
            });
        }
    });
}

有人请帮助我!

UPDATE:receive_loop方法正常工作,我理解它,当打开tmp文件夹并看到包含以下内容的文件时:“ * OK Gimap准备好接收来自109.252.29.37 m5mb106667441ljg的请求”,但是我无法使用send_loop 方法...

ios objective-c file-descriptor dispatch-async network-framework
1个回答
0
投票

我认为您可能会混淆如何形成send loop,这是您代码中的这一部分:

} else if (read_data == NULL) {
    //… handle end of file …
} else {

您的代码的以上部分对于功能非常重要,我相信您的send_loop应该看起来像这样:

- (void)send_loop:(nw_connection_t)connection {
    dispatch_read(STDIN_FILENO, 8192, dispatch_get_main_queue(), ^(dispatch_data_t _Nonnull read_data, int stdin_error) {
        if (stdin_error != 0) {
            //… error logging …
            NSLog(@"error int: %i", stdin_error);
        } else if (read_data == NULL) {
            nw_connection_send(connection, read_data, NW_CONNECTION_FINAL_MESSAGE_CONTEXT, true, ^(nw_error_t  _Nullable error) {
                if (error != NULL) {
                    NSLog(@"error: %@", error);
                }

                NSLog(@"read_data: %@", [read_data description]);
            });
        } else {
            nw_connection_send(connection, read_data, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t  _Nullable error) {
                if (error != NULL) {
                    NSLog(@"error: %@", error);
                } else {
                    [self send_loop:connection];
                }
            });
        }
    });
}

由于NW_CONNECTION_FINAL_MESSAGE_CONTEXT,我要提到的代码部分很重要-当没有什么要读取的内容时,它发出的信号是最后一条消息。

[我仍在使用IOS和基本的Network软件包简化WebSockets的创建过程中-如有需要,我将进行更新。

© www.soinside.com 2019 - 2024. All rights reserved.