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



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

剧情透露!我使用了官方教程(使用网络框架实现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 中的控制台只显示:"连接已就绪"。

如您所见,我们称之为"receive_loop 个具有 3 个参数的方法",让我们来看看这个方法:

- (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 zurgsrushmber@gmail.com 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"或"文件"变量中(两者都不起作用,尝试读取 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];
}
});
}
});
}

请有人帮帮我!

更新:receive_loop方法工作正常,我理解它,当打开 tmp 文件夹并看到我的文件内容:"* OK Gimap 准备好接收来自 109.252.29.37 m5mb106667441ljg 的请求",但我无法使用send_loop方法读取此文件...

编辑我创建了一个软件包来帮助使用 TLS 套接字。您可以轻松连接到 NodeJS TLS 服务器,它考虑了完成 TLS 握手的所有最新 iOS 13 限制。也有很多有用的文章的引用 - https://github.com/eamonwhiter73/IOSObjCWebSockets - 如果您是开发人员,请随时拉动和更改,我不是这个主题的专家,但我想创建这个,因为对于网络上的iOS/Objective-C/NodeJS组合来说似乎没有太多这样的内容。

我认为您可能混淆了如何形成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- 当没有什么可读的时,它会发送信号,表明它是最终消息。

我仍在研究我正在创建的包,以使 WebSockets 与 IOS 和基本的Network包变得容易 - 如果需要,我会在完成后进行更新。

最新更新