Mobile Development 10 min read

How to Make Long‑TCP Connections as Simple as Short Requests on iOS

This article explains a technique for writing long‑TCP connection business code on mobile devices that is as efficient and concise as short‑link code, using request_id mapping, protobuf messages, and a lightweight SendCore layer with timeout handling.

BaiPing Technology
BaiPing Technology
BaiPing Technology
How to Make Long‑TCP Connections as Simple as Short Requests on iOS

Recently, under pressure to deliver quickly, the author revisited an old code repository and found a practical method for making long‑TCP link business code as efficient and concise as short‑link code on mobile platforms.

The article does not dive into TCP protocol details, socket libraries, or data transmission formats; instead it focuses on writing high‑performance, clean mobile code for TCP‑based services.

Short‑Link Example

Using AFNetworking in Objective‑C, a typical short‑link request looks like this:

NSString *url = @"http://api.xxx.com/method";
[[self shareAFNManager] GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
    NSLog(@"responseObject-->%@", responseObject);
    UpdateUI();
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
    NSLog(@"error-->%@", error);
}];

The block syntax acts like a function pointer, allowing direct UI updates after the network response, embodying the short‑link principle R:Q = N:N = 1.

Long‑Link Challenges

Long‑link code must handle socket management, thread switching, UI lifecycle, data notifications, and more, often leading to complex or error‑prone implementations.

Solution

By abstracting the request/response relationship and using a unique request_id, long‑link handling can be simplified to the short‑link style.

Refine request & response, enforce R:Q = N:N = 1.

Define protobuf messages that pair each request with a reply:

message ping{
    string request_id = 1;
}

message ping_reply{
    string request_id = 1;
}

Implement a SendCore singleton to manage sending, mapping, and callbacks:

typedef void (^SocketDidReadBlock)(NSError * __nullable error, id __nullable data);

@interface SendCore : NSObject
+ (nullable SendCore *)sharedManager;
- (void)sendEnterChatRoom:(nullable EnterChatRoom *)data completion:(__nullable SocketDidReadBlock)callback;
- (void)CloseSocket;
@end

Example method for entering a chat room creates a request_id, stores the callback, and sends the protocol:

- (void)sendEnterChatRoom:(nullable EnterChatRoom *)data completion:(__nullable SocketDidReadBlock)callback {
    if (data == nil) { return; }
    NSString *blockRequestID = [self createRequestID];
    data.requestId = blockRequestID;
    if (callback) { [self addRequestIDToMap:blockRequestID block:callback]; }
    [self sendProtocolWithCmd:CmdType_Enter1V1MovieRoom withCmdData:[data data] completion:callback];
}

When a response arrives, the handler extracts the request_id, retrieves the stored block, executes it, and cleans up:

- (void)handerProtocol:(CmdType)protocolID packet:(NSData *)packet {
    NSError *error = nil;
    id resultData = nil;
    NSString *requestId = nil;
    switch (protocolID) {
        case CmdType_Enter1V1MovieRoomReply:
            error = nil;
            SyncObjInfo *syncObjInfo = [SyncObjInfo parseFromData:packet error:&error];
            resultData = syncObjInfo;
            requestId = syncObjInfo.requestId;
            SocketDidReadBlock didReadBlock = [self getBlockWith:requestId];
            break;
    }
    if (didReadBlock) { didReadBlock(error, resultData); }
    if (requestId && requestId.length > 0) { [self removeRequestIDFormMap:requestId]; }
}

Timeout handling uses a second map storing request timestamps; a periodic timer checks for overdue requests and invokes their callbacks with a timeout error:

- (void)checkRequestProcessTimeout {
    NSError *timeoutError = [NSError errorWithDomain:@"_Socket_WAIT_PROCESS_TIMEOUT_SECOND_" code:408 userInfo:nil];
    NSMutableArray *timeoutRequestIDs = [NSMutableArray array];
    NSDate *now = [NSDate date];
    for (NSString *requestID in [self.requestsTimeMap allKeys]) {
        NSDate *fireDate = [self.requestsTimeMap objectForKey:requestID];
        NSDate *timeOutDate = [NSDate dateWithTimeInterval:_Socket_WAIT_PROCESS_TIMEOUT_SECOND_ sinceDate:fireDate];
        if ([timeOutDate compare:now] == NSOrderedAscending) { [timeoutRequestIDs addObject:requestID]; }
    }
    for (NSString *requestID in timeoutRequestIDs) {
        SocketDidReadBlock block = [self getBlockWith:requestID];
        block(timeoutError, nil);
        [self removeRequestIDFormMap:requestID];
    }
}

Generating a unique request_id is straightforward:

- (NSString *)createRequestID {
    NSInteger timeInterval = [NSDate date].timeIntervalSince1970 * 1000000;
    NSString *randomRequestID = [NSString stringWithFormat:@"%ld%d", timeInterval, arc4random() % 100000];
    return randomRequestID;
}

With this architecture, long‑link business code becomes as clean and efficient as short‑link code, achieving the goal of indistinguishable implementation while retaining TCP’s advantages.

Conclusion

All functionalities are completed; the business code shows no difference between long‑link and short‑link implementations, delivering the expected simplicity and performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

iOSTCPMobile Networkinglong-connectionObjective‑CRequest ID
BaiPing Technology
Written by

BaiPing Technology

Official account of the BaiPing app technology team. Dedicated to enhancing human productivity through technology. | DRINK FOR FUN!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.