7

私はGCDAsyncSocketで動作する簡単な例を取得しようとしていますが、理解が不足していることを発見しました。すばらしい人々がこれを説明するのを手伝ってくれることを願っています。

以下にGCDAsyncSocketを設定しました。

dispatch_queue_t mainQueue = dispatch_get_main_queue();
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];

NSString *host = @"192.168.169.132";
uint16_t port = 2112;

DDLogInfo(@"Connecting to \"%@\" on port %hu...", host, port);
self.viewController.label.text = @"Connecting...";

NSError *error = nil;
if (![asyncSocket connectToHost:host onPort:port withTimeout:5.0 error:&error])
{
    DDLogError(@"Error connecting: %@", error);
    self.viewController.label.text = @"Oops";
}
else
{
    DDLogVerbose(@"Connecting...");
}


- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    DDLogInfo(@"socket:%p didConnectToHost:%@ port:%hu", sock, host, port);
    self.viewController.label.text = @"Connected";

    // We're just going to send a test string to the server.

    NSString *myStr = @"testing...123...\r\n";
    NSData *myData = [myStr dataUsingEncoding:NSUTF8StringEncoding];

    [asyncSocket writeData:myData withTimeout:5.0 tag:0];
}

そして、私のソケットテストサーバーアプリが文字列を受信するのを見ることができます

「テスト中...123... \ r \n」

しかし、ソケットテストサーバーに文字列を返送させると、didReadDataデリゲートが実行されることを素朴に期待していました

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

それでも、冷酷な現実は私が電話するまで私にそれを学ばせました

[asyncSocket readDataWithTimeout:5.0 tag:0];

... didReadDataデリゲートは呼び出されません。

OK、それは大丈夫です。わかった。

ドキュメントをもう少し読むと、それは明確に次のように述べています

AsyncSocketは、RunLoopベースのTCPソケットライブラリです。

だから今、私はこのRunLoopのものを見ています。これは、私の見解では、MicrosoftWindowsのメッセージループのようなものです。iOSはイベント/メッセージ駆動型アーキテクチャ(Win32と同様)であるため、現在使用しているデフォルトのメインスレッドには、イベントを処理するための独自のmsgループがあります。

私の混乱は、iOS RunLoopが、GCDAsyncSocketを正しく機能させるために連携しなければならない別のエンティティのように見えることです。

実行ループモードのデフォルトセットがNSDefaultRunLoopModeであると述べている場合、これはメインスレッドにあります。

まだ混乱していますか?

したがって、Win32では、通信イベント処理コードは次のようになります。

while( sCOMport.hCOMport != INVALID_HANDLE_VALUE )  // ...while the COM port is open...
{
    // Wait for an event to occur on the port.
    WaitCommEvent( sCOMport.hCOMport, &dwCommStatus, NULL );

もちろん、それは独自のスレッドにあります(GCDAsyncSocketを使用してまだそこに到達していません)が、それはある意味で独自の「RunLoop」になります。

[asyncSocket readDataWithTimeout]呼び出しでキューを埋めるポーリングループでスタックしないように、GCDAsyncSocketを使用して同じことを行うにはどうすればよいですか?

このライブラリを使用するには、より良い例が必要だと思います。

4

2 に答える 2

19

わかりました、私はこれを何らかの方法で機能させました。

これが特定の「ベストプラクティス」に反するかどうかを教えてください。

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    // setup as normal...

    // then ...

    // Instigate the first read
    [asyncSocket readDataWithTimeout:-1 tag:0];

.
.
}

次に...データが入ってくると...

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    // Do whatever you need to do with the data ...
.
.
.
    // and at the end ...
.
.
    // Always keep a 'read' in the queue.
    [asyncSocket readDataWithTimeout:-1 tag:0];
}

これにより、タイマーやその他の構成を使用せずにRunLoop操作を実行できます。そして、それ自体のスレッドに含めることができます。(それでもまだ未定です)

于 2011-11-03T16:02:10.750 に答える
1

私はこれが古い質問であり、すでに受け入れられた答えを持っていることを知っていますが、これが私のアプリの1つですでに使用している私の解決策です:

ホストに接続した後、次のディスパッチキューを実行します。

dispatch_queue_t alwaysReadQueue = dispatch_queue_create("com.cocoaasyncsocket.alwaysReadQueue", NULL);

dispatch_async(alwaysReadQueue, ^{
    while(![socket isDisconnected]) {
        [NSThread sleepForTimeInterval:5];
        [socket readDataWithTimeout:-1 tag:0];
    }
});

同じキューを使用して、接続を維持するためだけにハートビート要求を送信することもできます。

于 2014-03-31T09:47:28.417 に答える