Websocket might be disconnected on iOS 14 beta2

I try to connect to websocket server and send some messages, using NSURLSessionWebSocketTask.

Here is my demo code. This code works well on iOS 13. The Xcode that I uses is Version 12.0 beta 2 (12A6163b)

But the websocket is disconnected automatically (with the close code of NSURLSessionWebSocketCloseCodeProtocolError = 1002) a few seconds after the connection is created on iOS 14 beta2.

If I send short messages (e.g the message length < 10), iOS 14 beta2 might have much better chance to keep the websocket connection

Code Block objective-c
@interface ViewController () <NSURLSessionWebSocketDelegate, NSURLSessionDelegate>
@property (weak, nonatomic) IBOutlet UITextField *outgoing;
@property (weak, nonatomic) IBOutlet UITextView *incoming;
@property (nonatomic) NSURLSession* session;
@property (nonatomic) NSURLSessionWebSocketTask* task;
@property (nonatomic) dispatch_queue_t queue;
@property (nonatomic) NSOperationQueue* operationQueue;
@end
@implementation ViewController
- (void)viewDidLoad {
  [super viewDidLoad];
  NSURLSessionConfiguration* sc = [NSURLSessionConfiguration defaultSessionConfiguration];
   
  self.queue= dispatch_queue_create("WebSocket", DISPATCH_QUEUE_SERIAL);
  self.operationQueue = [[NSOperationQueue alloc] init];
  self.operationQueue.maxConcurrentOperationCount = 1;
  self.operationQueue.underlyingQueue = self.queue;
  self.session = [NSURLSession sessionWithConfiguration:sc delegate:self delegateQueue:self.operationQueue];
   
  self.task = [self.session webSocketTaskWithURL:[NSURL URLWithString:@"wss://echo.websocket.org/"]];
  [self.task resume];
  [self _runReadLoop];
}
- (IBAction)onSend:(id)sender {
  block NSUInteger index = 0;
  [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer * _Nonnull timer) {
    NSUInteger i = index;
    NSURLSessionWebSocketMessage* message = [[NSURLSessionWebSocketMessage alloc] initWithString:[NSString stringWithFormat:@"The first section of this page will let you do an HTML5 WebSocket test against the echo server. The second section walks you through creating a WebSocket application yourself - %@", @(i)]];
    [self.task sendMessage:message completionHandler:^(NSError* error) {
      if (error) {
        NSLog(@"send error: %@, %@", @(i), error);
      } else {
        NSLog(@"send: %@, %@", @(i), @(message.string.length));
      }
    }];
    index;
  }];
}
- (void) _runReadLoop {
    [self.task receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage* message, NSError* error) {
      if (error) {
        NSLog(@"recv error: %@", error);
        return;
      }
      dispatch_async(dispatch_get_main_queue(), ^{
        self.incoming.text = [NSString stringWithFormat:@"%@\n%@",self.incoming.text, message.string];
        [self _runReadLoop];
      });
       
    }];
}
#pragma mark - NSURLSessionDelegate
- (void)URLSession:(NSURLSession*)session didBecomeInvalidWithError:(NSError*)error {
  NSLog(@"Session Invalidated: %@", error);
}
- (void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError*)error {
  NSLog(@"Task Completed: %@, %@", task, error);
}
#pragma mark - NSURLSessionStreamDelegate
- (void)URLSession:(NSURLSession*)session webSocketTask:(NSURLSessionWebSocketTask*)task didOpenWithProtocol:(NSString*)protocol {
  NSLog(@"WebSocket Closed: %@, %@", task, protocol);
}
- (void)URLSession:(NSURLSession*)session webSocketTask:(NSURLSessionWebSocketTask*)task didCloseWithCode:(NSURLSessionWebSocketCloseCode)closeCode reason:(NSData*)reason {
  NSLog(@"WebSocket Closed: %@, %ld, %@", task, (long)closeCode, reason);
}
@end


Here is the log in the console

Code Block language
2020-07-08 23:35:56.597508+0800 WebSocketDemo[420:32598] libMobileGestalt MobileGestaltCache.c:166: Cache loaded with 4563 pre-cached in CacheData and 53 items in CacheExtra.
2020-07-08 23:36:02.645074+0800 WebSocketDemo[420:32600] WebSocket Closed: LocalWebSocketTask <07DE69E4-CDEA-4DD3-AC0A-40EE33A60C37>.<1>, (null)
2020-07-08 23:36:11.609142+0800 WebSocketDemo[420:32594] send: 0, 178
2020-07-08 23:36:12.831746+0800 WebSocketDemo[420:32594] [connection] nw_read_request_report [C1] Receive failed with error "Socket is not connected"
2020-07-08 23:36:12.837520+0800 WebSocketDemo[420:32594] Connection 1: received failure notification
2020-07-08 23:36:12.838900+0800 WebSocketDemo[420:32597] recv error: Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected" UserInfo={NSErrorFailingURLStringKey=https://echo.websocket.org/, NSErrorFailingURLKey=https://echo.websocket.org/}
2020-07-08 23:36:12.839745+0800 WebSocketDemo[420:32753] [connection] nw_connection_copy_connected_local_endpoint [C1] Connection has no connected path
2020-07-08 23:36:12.839931+0800 WebSocketDemo[420:32753] [connection] nw_connection_copy_connected_remote_endpoint [C1] Connection has no connected path
2020-07-08 23:36:12.884086+0800 WebSocketDemo[420:32594] WebSocket Closed: LocalWebSocketTask <07DE69E4-CDEA-4DD3-AC0A-40EE33A60C37>.<1>, 1002, (null)
2020-07-08 23:36:12.884914+0800 WebSocketDemo[420:32594] Task Completed: LocalWebSocketTask <07DE69E4-CDEA-4DD3-AC0A-40EE33A60C37>.<1>, (null)




Answered by adamglo in 622789022
The issue has been resolved in iOS 14 Beta 3
I'm having the exact same problem. Websocket code that worked perfectly on watchOS13 / iOS13 - immediately disconnects and is unable to send messages on iOS14 beta 2 / watchOS 7.
Glad I’m not the only one.

I’m able to connect and maintain a ping/ping status, and receive messages sent from the server, but if I attempt to send a message I’m instantly disconnected.
Code Block
[connection] nw_read_request_report [C1] Receive failed with error "Socket is not connected"
Connection 1: received failure notification
Connection 1: encountered error(1:57)
[connection] nw_connection_copy_connected_local_endpoint [C1] Connection has no connected path
[connection] nw_connection_copy_connected_remote_endpoint [C1] Connection has no connected path


I'm experiencing the same thing. It seems that a new header field has been added:
Code Block
Sec-WebSocket-Extensions permessage-deflate

The issue is not with the connection but with the messages as they are compressed now at certain cases.
Based on the length of the message being sent, it will either send it as text/binary or encoded text/binary.

The limit seems to be at 8 characters or 16 numbers when sent it as a data. 7 characters like "aaaaaaa" works fine as it's only sent as binary, while "aaaaaaaa" comes back with a 1002 error code as it's been sent as encoded binary.

First I thought it's just our WebSocket client but testing it with wss://echo.websocket.org gives the same result.

Anybody from the Apple developers could point us if it's something that we are missing from the server config or is it a bug?
We just met this issue, the by setting the permessage-deflate option on the server side, everything started to work again.

https://tools.ietf.org/html/rfc7692

What we did:
https://github.com/websockets/ws#websocket-compression

Hope it helps ...
It's great news that you made it working but it also seems that there is a bug in the implementation.

In the opening handshake the iOS client sends a “Sec-WebSocket-Extensions: permessage-deflate” header but the server declines it by the same header is not included in the response.

The iOS client doesn’t respect the negotiations as it still tries to send the message compressed by setting the RSV1 bit as 1 in the WebSocket layer. As it’s not what has been negotiated the Websocket Connection fails according to the protocol.

“RSV1, RSV2, RSV3:
1 bit each
MUST be 0 unless an extension is negotiated which defines meanings for non-zero values. If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST Fail the WebSocket Connection.”
Accepted Answer
The issue has been resolved in iOS 14 Beta 3
Hey all,

I have similar issues. I thought iOS 14.2 solved it, but it didn't.
Could you fix your issues?

Websocket might be disconnected on iOS 14 beta2
 
 
Q