Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TCP Transport Rewrite #946

Merged

Conversation

shoamano83
Copy link
Contributor

This PR is a part of implementation of #900. It also fixes issue #911.

This PR is ready for review.

Risk

This PR makes minor API changes: addition of NSNotification.

Testing Plan

Unit tests are included in the PR.

Summary

This PR rewrites TCP transport as requested by project maintainer (see smartdevicelink/sdl_evolution#405 (comment)).

The project maintainer suggested to use NSURLSessionStreamTask API and I had implemented with it. Then I found a few issues. 1) it didn't work with my iOS 9.0.2 phone, and 2) the API doesn't seem to provide "TCP connected" event. After a short offline discussion with the project maintainer, I decided to use CFNetwork API instead.

Changelog

Breaking Changes
  • None.
Enhancements
  • TCP transport is updated to use CFNetwork and NSStream API. The run loop runs on dedicated thread.
  • TCP connection errors are now notified to the app through NSNotification.
  • New delegates are added in TCPTransportDelegate and SDLProtocolListener. (The header files were once public, now are private.)
Bug Fixes
  • None.

CLA

@@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)onProtocolOpened;
- (void)onProtocolClosed;
- (void)onError:(NSString *)info exception:(NSException *)e;
- (void)onTransportError:(NSError *)error;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "onError:exception:" delegate (one line above) isn't and hasn't been used anywhere in the project. We might want to remove it to avoid confusion.

@shoamano83
Copy link
Contributor Author

A few notes:

  • This PR still lacks retry logic in the case of connection error. I'm wondering if we should add it, since TCP connection errors are mostly caused by wrong IP address and/or port number and reconnection doesn't usually make sense in such cases.
  • Please feel free to suggest better naming of the new delegates and error enums.

Thanks!

@joeljfischer joeljfischer changed the title Feature/rewrite tcp transport TCP Transport Rewrite Apr 26, 2018
@joeljfischer joeljfischer added enhancement proposal Accepted SDL Evolution Proposal labels Apr 26, 2018
@joeljfischer joeljfischer added this to the 6.1.0 milestone May 24, 2018
If a TCP connection is refused or timed out, onError is notified
from the transport. Then an error will be delivered to the app
through NSNotification.
@shoamano83 shoamano83 force-pushed the feature/rewrite_tcp_transport branch from 5cf9017 to 6bf797f Compare July 5, 2018 07:36
@shoamano83
Copy link
Contributor Author

I have rebased the commits onto latest develop branch and resolved conflicts.

@@ -65,6 +66,13 @@ extern SDLErrorDomain *const SDLErrorDomainChoiceSetManager;
+ (NSError *)sdl_choiceSetManager_choiceDeletionFailed:(NSDictionary *)userInfo;
+ (NSError *)sdl_choiceSetManager_choiceUploadFailed:(NSDictionary *)userInfo;

#pragma mark Transport

+ (NSError *)sdl_transport_OthersError;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename sdl_transport_unknownError

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 72e4cb3.

/**
* Connection cannot be established due to a reason not listed here.
*/
SDLTransportErrorOthers = -1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDLTransportErrorUnknown

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 72e4cb3.

@@ -27,6 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
* The port number of Core
*/
@property (strong, nonatomic) NSString *portNumber;
@property (nonatomic, assign) NSUInteger receiveBufferSize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this public facing, can it just be in the private interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it should be private, fixed with commit 4aa7820.

[self disconnect];
}

#pragma mark - SDLAbstractTransport methods
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no more SDLAbstractTransport

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed with commit 72e4cb3.

}

self.ioThread = [[NSThread alloc] initWithTarget:self selector:@selector(sdl_tcpTransportEventLoop) object:nil];
[self.ioThread setName:TCPIOThreadName];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.ioThread.name = TCPIOThreadName;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 72e4cb3.

[self.delegate onError:error];
self.transportErrorNotified = YES;
} else {
SDLLogW(@"Unhandled stream error! %@", stream.streamError);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an error log?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 4aa7820.

// of NSPOSIXErrorDomain are actually errno values.
NSError *error;
switch (stream.streamError.code) {
case ECONNREFUSED:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use braces as noted in the comment above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 72e4cb3.

error = [NSError sdl_transport_OthersError];
break;
}
[self.delegate onError:error];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: All the errors here are errors causing us to not be able to connect, should we be checking anything here to make sure we're disconnected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't catch your question. Are you suggesting that we should verify stream.streamStatus property here and make sure that the stream is closed?

Well, once sdl_onStreamError: is triggered, the ioThread is cancelled and eventually sdl_teardownStream: will be called for both streams. So I think there may not be much point adding extra logic inside sdl_onStreamError:.

}
}

- (void)sdl_doNothing {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one line - (void)sdl_doNothing {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 72e4cb3.

- (void)onClientError;
@end

@interface TestTCPServer : NSObject {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put this class in its own file(s), similar to how the TestConnectionManager is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, done in commit 713f409.

- Rename an error enum
- Update / remove comments, pragmas and logs
- Resolve styling issues
- Change receiveBufferSize to private
- Remove `default` case from switch statement
- Simplify some if statements
- Update logs
- Use NSAssert to check a flag that should be always false
Conflicts:
	SmartDeviceLink-iOS.xcodeproj/project.pbxproj
Conflicts:
	SmartDeviceLink-iOS.xcodeproj/project.pbxproj
@shoamano83
Copy link
Contributor Author

Hi @joeljfischer , I think I resolved all of your review comments. I also merged latest develop branch to get rid of the merge conflicts. Please kindly review again when you have time.

gethostname(localhost, sizeof localhost);
hostname = (const char *)&localhost;
#pragma mark - Stream event handlers
// these methods run only on the I/O thread (i.e. invoked from the run loop)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be an assert statement in each method making sure they're running on the correct thread?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, added in commit c1883ff.

fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
return (-1);
NSData *data = [NSData dataWithBytesNoCopy:buffer length:(NSUInteger)readBytes freeWhenDone:YES];
[self.delegate onDataReceived:data];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This delegate will be called on the io thread. Should we dispatch to a different queue first or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the design of iAP transport where onDataReceived is invoked from its I/O thread. I don't think we need to dispatch to another queue.

- Add assertions in I/O callback methods
@joeljfischer joeljfischer merged commit ef0cfa3 into smartdevicelink:develop Aug 14, 2018
@joeljfischer joeljfischer mentioned this pull request Aug 14, 2018
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Accepted SDL Evolution Proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants