gpt4 book ai didi

objective-c - iOS 客户端/服务器实现和 "Operation now in progress"错误

转载 作者:可可西里 更新时间:2023-11-01 05:00:13 27 4
gpt4 key购买 nike

背景

我一直在通过各种示例代码和经典的 Unix 网络编程 教科书自学套接字和网络编程,同时尝试将这些知识应用到我正在开发的应用程序中马上。我目前正在处理需要简单客户端-服务器设置的应用程序的一部分。

现在是这样(好吧,应该是这样):

  1. 服务器使用 NSNetService 发布自己并使用 CFSocketCreateWithNative() 创建套接字
  2. 客户端通过NSNetServiceBrowser找到服务器
  3. 客户端解析发现的服务
  4. 服务器从 CFSocket 获得回调,创建类的新实例 (MyConnection) 来处理连接。使用 CFStreamCreatePairWithSocket() 获得连接的读写流。
  5. 客户端向服务器发送消息 (@"hi")
  6. 服务器将从客户端接收到的数据发送回客户端(这是我的问题所在)
  7. 客户端在 UIAlertView 中显示字符串

两个问题

  1. 当我尝试让服务器将数据发送回客户端时,出现“操作正在进行中”错误,如下面的连接代码中所注释。我相信这是因为 NSOutputStream 没有可用空间。处理这个问题的最佳方法是什么?我知道它应该等待 NSStreamEventHasSpaceAvailable 事件,但它似乎没有发生......
    编辑:呃......当我收到这个错误时,我只用 iPhone 模拟器测试应用程序并让它充当服务器和客户端,因为我的新公寓还没有互联网 :P 这似乎是一个使用两个真实设备时没有问题。

  2. 是否可以让这个服务器在每个连接对象的数据发送和接收中不阻塞其他连接对象的数据发送和接收?每个新的连接对象是否需要在新的运行循环或线程或类似的东西上?我查了 apple 并发文档,但什么也没有跳出来......目标是尽可能快地向客户端发送回复,无论有多少其他客户端连接到服务器。
    更新:我正在考虑将连接排队并一次处理一个,而不是允许与该服务器的并发连接,因为需要发送到每个客户端的数据量非常小。这是最好的决定吗?如果队列中有数百个客户怎么办?转念一想,这可能是个坏主意,因为在快速本地网络上建立连接需要一两秒钟,而使用蓝牙则需要更长的时间……我希望就此事获得一些专家建议:)

相关代码

注意:APNetService 和 APNetServiceBrowser 类似于 NSNetService 和 NSNetServiceBrowser

服务器代码

- (void) startServerForGroup:(NSString *)name
{
self.groupName = name;

NSInteger port = [self prepareListeningSocket];

self.service = [[APNetService alloc] initWithDomain:@"local."
type:@"_example._tcp."
name:self.groupName
port:port];
self.service.delegate = self;
[self.service publish];
}

- (NSInteger) prepareListeningSocket
{
int listenfd, err, junk, port;
BOOL success;

struct sockaddr_in addr;

port = 0;

listenfd = socket(AF_INET, SOCK_STREAM, 0);
success = (listenfd != -1);

if (success) {
bzero(&addr, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = INADDR_ANY;
err = bind(listenfd, (const struct sockaddr *) &addr, sizeof(addr));
success = (err == 0);
}
if (success) {
err = listen(listenfd, 5);
success = (err == 0);
}
if (success) {
socklen_t addrLen;

addrLen = sizeof(addr);
err = getsockname(listenfd, (struct sockaddr *) &addr, &addrLen);
success = (err == 0);

if (success) {
assert(addrLen == sizeof(addr));
port = ntohs(addr.sin_port);
}
}
if (success) {
CFSocketContext context = { 0,(__bridge void*) self, NULL, NULL, NULL };

CFSocketRef socket = CFSocketCreateWithNative(
NULL,
listenfd,
kCFSocketAcceptCallBack,
AcceptCallback,
&context
);
if (socket) {
self.listeningSocket = socket;
CFRelease(socket);
success = YES;
}

if (success) {
CFRunLoopSourceRef rls;

listenfd = -1;

rls = CFSocketCreateRunLoopSource(NULL, self.listeningSocket, 0);
assert(rls != NULL);

CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);

CFRelease(rls);
}
}

if ( success ) {
return port;
}
else {
NSLog(@"FAILED TO START SERVER");

if (listenfd != -1) {
junk = close(listenfd);
assert(junk == 0);
}
return -1;
}
}

#pragma mark - Callback

// Called by CFSocket when someone connects to the listening socket
static void AcceptCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
MyServer * obj;

obj = (__bridge MyServer *) info;

assert(s == obj->_listeningSocket);

MyConnection *newCon = [[MyConnection alloc] initWithFileDescriptor:*(int*)data];

[newCon startReceive];

//add the new connection object to the servers mutable array of connections
[obj.connections addObject:newCon];

}

连接代码

- (void) startReceive
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;

CFStreamCreatePairWithSocket(NULL, self.fd, &readStream, &writeStream);

self.inputStream = (__bridge_transfer NSInputStream *) readStream;
self.outputStream = (__bridge_transfer NSOutputStream*) writeStream;

[self.inputStream setProperty:(id)kCFBooleanTrue forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket];
[self.outputStream setProperty:(id)kCFBooleanTrue forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket];

self.inputStream.delegate = self;
self.outputStream.delegate = self;


[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

[self.inputStream open];
[self.outputStream open];
}


#pragma mark - NSStreamDelegate

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {

case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[32768];

bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)];
if (bytesRead == -1)...
else if (bytesRead == 0)...
else {
NSData *data = [NSData dataWithBytes:buffer length:bytesRead];
[self didReceiveData:data];
}
} break;
case NSStreamEventHasSpaceAvailable: {
self.space = YES;
} break;

. . .
}
}

- (void) didReceiveData:(NSData *)data
{
if (self.space)
NSLog(@"SPACE");
else
NSLog(@"NO SPACE"); //this gets printed

NSInteger i = [self.outputStream write:data.bytes maxLength:data.length];

if (i < 0) {
printf("%s",strerror(errno)); //"Operation now in progress" error
}
}

客户端代码

#pragma mark - APNetServiceBrowserDelegate

- (void) browser:(APNetServiceBrowser *)browser didAddService:(APNetService *)service moreComing:(BOOL)moreComing
{
//omitting checks that determine which server to connect to, if multiple

service.delegate = self;
[service resolveWithTimeout:20];
}


#pragma mark - APNetServiceDelegate

- (void) netServiceDidResolveAddress:(APNetService *)service
{
NSInputStream *input;
NSOutputStream *output;

[service getInputStream:&input outputStream:&output];

self.inputStream = input;
self.outputStream = output;

self.inputStream.delegate = self;
self.outputStream.delegate = self;

[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

[self.inputStream open];
[self.outputStream open];
}


#pragma mark - NSStreamDelegate

- (void) stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {

case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[32768];

bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)];

if (bytesRead == -1) NSLog(@"Error reading data");
else if (bytesRead == 0) NSLog(@"no bytes read");
else {
NSData *data = [NSData dataWithBytes:buffer length:bytesRead];
[self didReceiveData:data];
}
} break;

case NSStreamEventHasSpaceAvailable: {
if (!self.isWaitingForReply) {
[self sendHelloMessage];
}
} break;
//omitted other NSStreamEvents
}
}

- (void) sendHelloMessage
{
NSData *d = [NSKeyedArchiver archivedDataWithRootObject:@"hi"];

[self.outputStream write:d.bytes maxLength:d.length];
self.isWaiting = YES;
}


- (void) didReceiveData:(NSData *)data
{
NSString *string = [NSKeyedUnarchiver unarchiveObjectWithData:data];

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Message"
message:string
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}

最佳答案

据我所知,您想制作一个可以非常快速地处理许多连接的服务器。 c10k 页面是一个很好的起点文档:http://www.kegel.com/c10k.html .是的,排队可能是个好主意,它比 fork 消耗的资源少得多,而且响应速度更快。然而,这也意味着您的计算保持相对较小,因此您仍然能够快速回复。也许是一个好的开始http://libevent.org/ ,检查它是如何做到的,它是为这种用途而设计的。

祝你好运。

关于objective-c - iOS 客户端/服务器实现和 "Operation now in progress"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11006673/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com