0

I am using TCP short connection to send data to a host. When I want to send a data to a host I will instantiate a socket, store the socket instance to an array and then remove it later on when the data is written successfully.

This is how I did it:

- (void)connectAndSendData:(NSData *)data
{
    GCDAsyncSocket *asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];

    [_connectionDataToBeSentMap setObject:data forKey:[NSValue valueWithNonretainedObject:asyncSocket]];

    NSError *error;

    if(![asyncSocket connectToHost:_host onPort:TCP_COMMUNICATION_PORT withTimeout:TCP_CONNECTION_TIMEOUT error:&error])
    {
        NSLog(@"TCPShortConnection - failed when attempting to connect to host with IP %@", _host);
    }

    [_connectionList addObject:asyncSocket];
}

I will disconnect the socket immediately after writing the data. So the connection will be removed afterwards:

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    [_connectionList removeObject:sock];

    if(err.code == GCDAsyncSocketConnectTimeoutError)
    {
//        NSLog(@"TCPShortConnection - on connection timed out");

        [_delegate onTCPShortConnectionTimedOut];
    }

    else
    {
//        NSLog(@"TCPShortConnection - on disconnection of socket with host %@", _host);
    }
}

This line [_connectionList removeObject:sock]; is throwing NSRangeException. And I don't know why. Because I made a test by removing a non existing object in an empty array, no such exception is thrown.

Here's the log:

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x258d9b0b 0x25096dff 0x257ea6d3 0x257ff033 0xfae43 0x1ca94d 0x4ccba7 0x4d8eff 0x4d87f1 0x2560ae0d 0x2560a9fc)
libc++abi.dylib: terminating with uncaught exception of type NSException

The screenshot:

enter image description here

EDIT:

Please take not that this is not 100% occuring.

JLT
  • 3,052
  • 9
  • 39
  • 86
  • plz check your array is not empty – balkaran singh Sep 03 '16 at 05:03
  • Just a guess: `NSMutableArray` is not thread safe, are all your accesses to the array being performed on the same thread? – CRD Sep 03 '16 at 06:01
  • @balkaransingh hi, I did a test by removing an object in an empty array, however, no such exception was thrown. – JLT Sep 03 '16 at 06:11
  • @CRD yes, all of them are being accessed in the same thread. The one thing that's strange for me is that I tested by removing an object in an empty array. No such exception was thrown. – JLT Sep 03 '16 at 06:13
  • I still think you may have a concurrency issue, I've added an answer to explain why and provide a possible solution if so. – CRD Sep 03 '16 at 18:02
  • @SaurabhJain Hi, thank you. but not really that helpful. – JLT Sep 05 '16 at 12:34
  • @JLT Thank you, I have learned new concept. – Saurabh Jain Sep 05 '16 at 12:39

2 Answers2

3

Guess: you are accessing your NSMutableArray from different threads

Explanation:

  1. NSMutableArray is not thread safe, concurrent reading and writing is not safe - as may occur if you are accessing the array from a GCD concurrent queue.

  2. GCDAsyncSocket as its name suggests uses GCD. In particular delegate calls are dispatched on a GCD queue set by the user and this queue may be concurrent.

  3. You are setting one of the global concurrent queues as the delegate queue.

Solution: You should first determine whether you should be using an array, we cannot determine that for you. If you need the array then it needs to be thread safe. A simple way to do this is:

  1. When you create the array also create a sequential GCD queue just for this array.

  2. To perform an array method which reads from the array perform a synchronous dispatch on the array's sequential queue. You can return the result from the dispatched block using a __block variable.

  3. To perform a write operation on the array use an asynchronous dispatch to the array's queue.

This simple model ensures that no two operations on the array can occur concurrently and all reads and writes occur in the order they are dispatched.

As I stated at the start, I'm guessing concurrent access to the array is your problem, you will need to determine that yourself. If it is this should fix it.

HTH

CRD
  • 52,522
  • 5
  • 70
  • 86
0

There are two occurring here:

CASE 1: When successfully add the object in _connectionList array.

   [_connectionList addObject:asyncSocket];

In this case no such exception occur.

[_connectionList removeObject:sock];

Remove the object from _connectionList array successfully.

CASE2: When the object not add successfully in _connectionList array.

In this it throw an exception,

[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]

because the object not added successfully in _connectionList array.

So this problem called reader-writer problem.

because NSMutableArray not thread safe.

Saurabh Jain
  • 1,688
  • 14
  • 28
  • Hi, thank you for the answer. But this is not what I looking for. I want to know a reason as why this would happen. Plus, I already mentioned that I just tested by removing an object in an array that does not contain such object. But the error didn't occur. – JLT Sep 03 '16 at 07:02
  • @JLT you have given a screenshot, this is come in your project or not? – Saurabh Jain Sep 03 '16 at 07:05
  • What I mean is the NSRange exception that I mentioned does not occur all the time. – JLT Sep 03 '16 at 07:11
  • I mentioned in my question that I did a test by removing an object from an empty array. But it didn't even throw such exception. – JLT Sep 03 '16 at 08:06