@@ -54,6 +54,11 @@ @interface GCDAsyncSocket ()
5454// TLS
5555@property (nonatomic , assign ) BOOL tlsEnabled;
5656
57+ // Write queue tracking
58+ @property (nonatomic , assign ) NSUInteger pendingWriteCount;
59+ @property (nonatomic , assign ) BOOL flagDisconnectAfterWrites;
60+ @property (nonatomic , assign ) BOOL flagDisconnectAfterReads;
61+
5762@end
5863
5964@implementation GCDAsyncSocket
@@ -674,11 +679,35 @@ - (void)disconnect {
674679}
675680
676681- (void )disconnectAfterWriting {
677- [self disconnect ];
682+ __weak typeof (self) weakSelf = self;
683+ dispatch_async (self.socketQueue , ^{
684+ __strong typeof (weakSelf) strongSelf = weakSelf;
685+ if (!strongSelf) return ;
686+
687+ strongSelf.flagDisconnectAfterWrites = YES ;
688+ // If no writes are in flight, disconnect immediately.
689+ // Otherwise the send-completion handler will disconnect
690+ // once the last pending write finishes.
691+ if (strongSelf.pendingWriteCount == 0 ) {
692+ [strongSelf disconnectInternalWithError: nil ];
693+ }
694+ });
678695}
679696
680697- (void )disconnectAfterReading {
681- [self disconnect ];
698+ __weak typeof (self) weakSelf = self;
699+ dispatch_async (self.socketQueue , ^{
700+ __strong typeof (weakSelf) strongSelf = weakSelf;
701+ if (!strongSelf) return ;
702+
703+ strongSelf.flagDisconnectAfterReads = YES ;
704+ // If no read requests are pending, disconnect immediately.
705+ // Otherwise the read-completion callback will disconnect
706+ // once the last pending read request is fulfilled.
707+ if (strongSelf.readQueue .count == 0 ) {
708+ [strongSelf disconnectInternalWithError: nil ];
709+ }
710+ });
682711}
683712
684713#pragma mark - Reading
@@ -773,9 +802,15 @@ - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)t
773802 strongSelf.socketQueue , timeoutBlock);
774803 }
775804
805+ strongSelf.pendingWriteCount ++;
806+
807+ // is_complete must be false so the TCP stream stays open for
808+ // subsequent writes (e.g. HTTP header followed by body).
809+ // Passing true here would send a TCP FIN after each write,
810+ // closing the write side of the connection prematurely.
776811 nw_connection_send (strongSelf.connection , dispatchData,
777812 NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT,
778- true , ^(nw_error_t _Nullable error) {
813+ false , ^(nw_error_t _Nullable error) {
779814 writeCompleted = YES ;
780815 if (timeoutBlock) {
781816 dispatch_block_cancel (timeoutBlock);
@@ -785,19 +820,28 @@ - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)t
785820 __strong typeof (weakSelf) sself = weakSelf;
786821 if (!sself) return ;
787822
823+ // This completion handler fires on socketQueue (set via
824+ // nw_connection_set_queue), so we can safely mutate state.
825+ sself.pendingWriteCount --;
826+
788827 if (error) {
789828 NSError *nsError = [sself socketErrorWithCode: GCDAsyncSocketErrorConnectionFailed
790829 description: @" Write failed."
791830 reason: @" nw_connection_send failed"
792831 nwError: error];
793- [sself disconnectWithError : nsError];
832+ [sself disconnectInternalWithError : nsError];
794833 } else {
795834 dispatch_async (sself.delegateQueue , ^{
796835 id delegate = sself.delegate ;
797836 if ([delegate respondsToSelector: @selector (socket:didWriteDataWithTag: )]) {
798837 [delegate socket: sself didWriteDataWithTag: tag];
799838 }
800839 });
840+
841+ // Check if we should disconnect after all writes complete
842+ if (sself.flagDisconnectAfterWrites && sself.pendingWriteCount == 0 ) {
843+ [sself disconnectInternalWithError: nil ];
844+ }
801845 }
802846 });
803847 });
@@ -1013,6 +1057,11 @@ - (void)processReadQueue {
10131057 }
10141058 }
10151059 }
1060+
1061+ // Check if we should disconnect after all read requests are fulfilled
1062+ if (self.flagDisconnectAfterReads && self.readQueue .count == 0 ) {
1063+ [self disconnectInternalWithError: nil ];
1064+ }
10161065}
10171066
10181067#pragma mark - Private: Disconnect
@@ -1035,6 +1084,9 @@ - (void)disconnectInternalWithError:(NSError *)error {
10351084
10361085 self.isConnected = NO ;
10371086 self.isReadingContinuously = NO ;
1087+ self.flagDisconnectAfterWrites = NO ;
1088+ self.flagDisconnectAfterReads = NO ;
1089+ self.pendingWriteCount = 0 ;
10381090
10391091#if NW_FRAMEWORK_AVAILABLE
10401092 if (self.listener ) {
0 commit comments