2

GCDAsyncSocket을 사용하여 iDevice 클라이언트로 일부 이미지 파일 (거의 100MB)을 보내려고합니다.Objective-C의 비동기 작업을 동기화하십시오.

내가 원하는 동 기적으로은 클라이언트에 패킷을 보냅니다. 첫 번째 클라이언트에 100MB의 데이터를 보낸 후 다음 클라이언트로 반복합니다. 그러나 비동기식 GCDAsyncSocket의 성격이 패킷 보내기를 어떻게 serialize 할 수 있는지 알지 못합니다.

이미지를 보내기 전에 어떤 이미지를 보내야하는지 알기 위해 각 클라이언트와 협상하기 때문에 세마포어를 사용할 수 없습니다. 세마포어를 기다리고 신호를 보낼 수있는 깔끔한 방법을 찾을 수 없습니다.

- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag 
{ 
    if ([data length] == 0) return; 

    GCDAsyncWritePacket *packet = [[GCDAsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag]; 

    dispatch_async(socketQueue, ^{ @autoreleasepool { 

     LogTrace(); 

     if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites)) 
     { 
      [writeQueue addObject:packet]; 
      [self maybeDequeueWrite]; 
     } 
    }}); 

    // Do not rely on the block being run in order to release the packet, 
    // as the queue might get released without the block completing. 
} 

그래서 사람이 어떤 생각을 가지고 않습니다 어떻게이 작업을 동기화 할 수 있습니다

:

- (void)sendImagesToTheClients:clients 
{ 
    ... 
    //negotiating with client to know which images should sent 
    ... 

    for(Client* client in clients) 
    { 
     packet = [packet addImages: images]; 
     [self sendPacket:packet toClient:client]; 
    } 
} 

- (void)sendPacket:packet toClient:client 
{ 
// Initialize Buffer 
    NSMutableData *buffer = [[NSMutableData alloc] init]; 
    NSData *bufferData = [NSKeyedArchiver archivedDataWithRootObject:packet]; 

    uint64_t headerLength = [bufferData length]; 
    [buffer appendBytes:&headerLength length:sizeof(uint64_t)]; 
    [buffer appendBytes:[bufferData bytes] length:[bufferData length]]; 

    // Write Buffer 
    [client.socket writeData:buffer withTimeout:-1.0 tag:0]; 
} 

이 AsyncSocket 쓰기 데이터가 어떻게 작동?

소켓 연결에 대한

UPDATE 내가 많이 이벤트 알림에 대한 위임을 사용 GCDAsyncSocket. (GCDAsyncSocket.h 및 GCDAsyncSocket.m) (completionHandler와 방법 없음)를 사용합니다.

소켓 연결과 패킷 보내기를 처리하고 초기화 된 소켓의 대리자로 설정하는 TCPClient라는 클래스를 작성했습니다. 패킷을 작성한 후 대리자 메서드 - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag이 호출됩니다. 그것은 어떤 데이터가 쓰여졌다는 것을 알려주는 것입니다. 여기서 나는 서면 자료를 근거로하여 dispatch_group_leave으로 전화를 할 수 없다. 그래서 위임 메서드를 사용하는 것은 쓸모가 없습니다.

내가 수정 한 [client.socket writeData는을 : -1.0 태그 : withTimeout 버퍼 0] GCDAsyncSocket.h와하는 .m 파일에 completionBlock 받아들이 : [client.socket writeData:buffer withTimeout:-1.0 tag:0 completionBlock:completionBlock] 이 방법을 사용하여 동기화 비동기 작업을 해결하기 위해 나에게 도움이됩니다.

// perform your async task 
      dispatch_async(self.socketQueue, ^{ 
       [self sendPacket:packet toClient:client withCompletion:^(BOOL finished, NSError *error) 
       { 
        if (finished) { 
         NSLog(@"images file sending finished"); 
         //leave the group when you're done 
         dispatch_group_leave(group); 
        } 
        else if (!finished && error) 
        { 
         NSLog(@"images file sending FAILED"); 
        } 

       }]; 

그러나 문제는 GCDAsyncsocket을 업데이트 한 후입니다. 코드가 손상 될 수 있습니다. 여기 GCDAsyncsocket에 완료 핸들러를 직접 수정하지 않고 추가 할 수있는 깔끔한 방법을 찾고 있습니다. 주위에 래퍼를 만들거나 objective-c 런타임의 기능을 사용하는 것과 같습니다. 의견이 있으십니까?

답변

3

디스패치 그룹으로이를 수행 할 수 있습니다. 대리자와 작업 대안

//create & enter the group 
dispatch_group_t group = dispatch_group_create(); 
dispatch_group_enter(group); 

// perform your async task 
dispatch_async(socketQueue, ^{ 

    //leave the group when you're done 
    dispatch_group_leave(group); 
}); 

// wait for the group to complete 
// (you can use DISPATCH_TIME_FOREVER to wait forever) 
long status = dispatch_group_wait(group, 
        dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT)); 

// check to see if it timed out, or comepleted 
if (status != 0) { 
    // timed out 
} 

: 완료 블록와 비동기 태스크

@property (nonatomic) dispatch_group_t group; 

-(BOOL)doWorkSynchronously { 

    self.group = dispatch_group_create(); 
    dispatch_group_enter(self.group); 

    [object doAsyncWorkWithDelegate:self]; 

    long status = dispatch_group_wait(self.group, 
         dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT)); 

    // A 
    if (status != 0) { 
     // timed out 
     return NO 
    } 

    return YES; 
} 

-(void)asyncWorkCompleted {} 

    // after this call, control should jump back to point A in the doWorkSynchronously method 
    dispatch_group_leave(self.group); 
} 
+0

'- (무효) writeData는 (을 NSData *) 데이터 withTimeout (NSTimeInterval) 초과 태그 : (long) tag'는'GCDAyncSocket.m'에있는 메소드이고 그것을 수정해서는 안됩니다. socketQueue를'GCDAsynSocket'에 전달할 수 있습니다. 다른 dispatch_async를 만들어서'sendPacket : client :'를 넣고'dispatch_group_leave (group); '를 호출해야합니까? 왜냐하면 내가 한 것이지만 아무것도 바뀌지 않았다. 여전히 비동기입니다. –

+0

'GCDAsyncSocket'은 위임을 통해 완료를 알립니다 callBack'didWriteDataWithTag : '문제는이 메소드가 데이터를 기반으로 작성된 데이터를 보내지 않습니다 (이미지 일 경우)'dispatch_group_leave (group);'입니다. GCDAsyncSocket은 completionHandler를 통해 나를 알리지 않습니다. –

+0

이 라이브러리의 위임 된 특성을 반영하여 질문을 업데이트하십시오.나는 내 대답을 업데이트 할 것이고, 당신은 delegate 메쏘드에'dispatch_group_leave'를 넣을 수있다. – thelaws

관련 문제