iOS GCDAsyncSocket 發(fā)送和接收粘包問題解決方案

服務(wù)端

@property (nonatomic, strong) GCDAsyncSocket *serverSocket; //服務(wù)端socket
@property (nonatomic, strong) GCDAsyncSocket *clientSocket;//客戶端連接過來的socket

//創(chuàng)建服務(wù)端并啟動(dòng)服務(wù)
- (void)createLocalServer {
    if (self.serverSocket == nil) {
        self.serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    NSError *error = nil;
    BOOL isa = [self.serverSocket acceptOnPort:[SocketServerPort integerValue] error:&error];
    NSLog(@"----%d----",isa);
}

#pragma mark - 代理方法(GCDAsyncSocketDelegate)
//連接成功
- (void)socket:(GCDAsyncSocket *)sender didAcceptNewSocket:(GCDAsyncSocket *)newSocket {
    self.clientSocket = newSocket; //接收客戶端連接過來的socket對(duì)象
    self.clientSocket.delegate = self;//設(shè)置代理
    //讀取數(shù)據(jù),以CRLFData結(jié)尾即表示是一條數(shù)據(jù)(以此來斷開多個(gè)包一起發(fā)過來的粘包問題)
    [self.clientSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
}
//斷開連接或連接失敗
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    NSLog(@"-Socket斷開連接--err = %@ --- code = %ld",err.localizedDescription,(long)err.code);
}
//讀取數(shù)據(jù)
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    //接收到的消息處理
    NSData *newData = [data subdataWithRange:NSMakeRange(0, data.length-2)];
    //先嘗試轉(zhuǎn)字典
    NSDictionary *result = [NSJSONSerialization JSONObjectWithData:newData options:kNilOptions error:nil];
    if (result) {
              //處理獲取到的字典數(shù)據(jù)
        }
    } else {
        //如果字典轉(zhuǎn)換失敗,則嘗試轉(zhuǎn)換字符串
        NSString *content = [[NSString alloc] initWithData:newData encoding:NSUTF8StringEncoding];
        NSLog(@"--接收到的消息-- content = %@",content);
        //接收到的字符串處理
    }
    //持續(xù)接收數(shù)據(jù)  以CRLFData結(jié)尾即表示是一條數(shù)據(jù)(以此來斷開多個(gè)包一起發(fā)過來的粘包問題)
    //如果不在讀取完以后寫這個(gè)方法,那就只讀取一次數(shù)據(jù),不會(huì)持續(xù)讀取
    [self.clientSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
}

#pragma mark - 寫數(shù)據(jù)成功回調(diào)
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"------發(fā)送完成---------");
}

#pragma mark - 發(fā)送數(shù)據(jù)
 //發(fā)送文本類型消息
- (void)sendTextMessage:(NSString *)text {
    
    if (text.length == 0 || text == nil) {
        return;
    }
    if (!_clientSocket) {
        NSLog(@"socket未連接");
        return;
    }
    NSLog(@"發(fā)送的數(shù)據(jù)---(%@)",text);
    NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData *printData = [[NSMutableData alloc] initWithData:data];
    //拼接結(jié)束符
    [printData appendData:[GCDAsyncSocket CRLFData]];
    // withTimeout -1 : 無窮大,一直等
    [self.clientSocket writeData:printData withTimeout:-1 tag:0];
}
//發(fā)送字典數(shù)據(jù)
- (void)sendTextMessageWithDict:(NSDictionary *)dict {
    if (dict == nil || dict.count == 0) {
        return;
    }
    if (!_clientSocket) {
        NSLog(@"socket未連接");
        return;
    }
    
    NSLog(@"發(fā)送的數(shù)據(jù)---(%@)",dict);
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&error];
    //字典轉(zhuǎn)data結(jié)果判斷
    if (error) {
        return;
    }
    
    NSMutableData *printData = [[NSMutableData alloc] initWithData:jsonData];
     //拼接結(jié)束符
    [printData appendData:[GCDAsyncSocket CRLFData]];
    // withTimeout -1 : 無窮大,一直等
    [self.clientSocket writeData:printData withTimeout:-1 tag:0];
}

客戶端

#import <ifaddrs.h>
#import <arpa/inet.h>
#import <net/if.h>

@property (nonatomic, strong) GCDAsyncSocket *socketClient;


/*
 lo     本地回環(huán) (Loopback)
 en     無線網(wǎng) (WiFi)
 pdp_ip 蜂窩網(wǎng)絡(luò) (Cellular)
 awdl   通信Wi-Fi (Apple Wireless Direct Link,用于AirDrop、AirPlay、GameKit 和 Apple Pencil )
 utun   虛擬隧道 (VPN)
 ipsec  虛擬隧道 (VPN)
 llw    低延遲無線局域網(wǎng)(Low-latency WLAN)
 */
/// 獲取ip地址
/// - Parameter isCellular: 是否獲取蜂窩網(wǎng)絡(luò)ip
- (NSString *)deviceIPAdressIsCellular:(BOOL)isCellular {
    NSString *address = @"";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    success = getifaddrs(&interfaces);
    if (success == 0) { // 0 表示獲取成功
        temp_addr = interfaces;
        while (temp_addr != NULL) {
            if( temp_addr->ifa_addr->sa_family == AF_INET) {
                if (isCellular) {
                    if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"pdp_ip0"]) {
                        // 找到4G網(wǎng)絡(luò)接口
                        address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                    }
                } else {
                    // Check if interface is en0 which is the wifi connection on the iPhone
                    if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                        // Get NSString from C String
                        address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                    }
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    freeifaddrs(interfaces);
    return address;
    
}


- (void)createLocalClient {
    
    NSString *ip = [self deviceIPAdressIsCellular:NO];
    NSString *port = @"84999";//設(shè)置自己的端口
    
    if (self.socketClient == nil) {
        
        self.socketClient = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
        if (self.socketClient.isConnected == NO) {
            
            BOOL isSuccess = [self.socketClient connectToHost:ip onPort:[port integerValue] error:nil];
            if (!isSuccess) {
                NSLog(@"連接失敗");
            }
        }
    } else {
        BOOL isSuccess = [self.socketClient connectToHost:ip onPort:[port integerValue] error:nil];
        if (!isSuccess) {
            NSLog(@"連接失敗");
        }
    }
}

#pragma mark - GCDAsyncSocketDelegate -
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
    NSLog(@"Socket連接成功");
    if (_socketClient) {
        self.reconnectionsNumber = 0;
        self.isConnect = YES;
        // -1 永不失效
        [_socketClient readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
    }
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    NSLog(@"--%@-Socket斷開連接--err = %@ --- code = %ld",[NSDate date],err.localizedDescription,(long)err.code);
 
    //這里可以做嘗試重連
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self createLocalClient];
    });
}

//獲取數(shù)據(jù)失敗時(shí),會(huì)被調(diào)用
- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error {
    NSLog(@"connection failed: %@",[error localizedDescription]);
}

#pragma mark - 讀數(shù)據(jù)
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
     //接收到的消息處理
    NSData *newData = [data subdataWithRange:NSMakeRange(0, data.length-2)];
    //先嘗試轉(zhuǎn)字典
    NSDictionary *result = [NSJSONSerialization JSONObjectWithData:newData options:kNilOptions error:nil];
    if (result) {
              //處理獲取到的字典數(shù)據(jù)
        }
    } else {
        //如果字典轉(zhuǎn)換失敗,則嘗試轉(zhuǎn)換字符串
        NSString *content = [[NSString alloc] initWithData:newData encoding:NSUTF8StringEncoding];
        NSLog(@"--接收到的消息-- content = %@",content);
        //接收到的字符串處理
    }
    //持續(xù)接收數(shù)據(jù)  以CRLFData結(jié)尾即表示是一條數(shù)據(jù)(以此來斷開多個(gè)包一起發(fā)過來的粘包問題)
    //如果不在讀取完以后寫這個(gè)方法,那就只讀取一次數(shù)據(jù),不會(huì)持續(xù)讀取
    [self.socketClient readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
}

#pragma mark - 寫數(shù)據(jù)成功回調(diào)
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"------發(fā)送完成---------");
}


#pragma mark 發(fā)送文本類型消息
- (void)sendTextMessage:(NSString *)text {
    if (text.length == 0 || text == nil) {
        return;
    }
    
    if (!_socketClient) {
        NSLog(@"socket未連接");
        return;
    }

    NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData *printData = [[NSMutableData alloc] initWithData:data];
    [printData appendData:[GCDAsyncSocket CRLFData]];

    // withTimeout -1 : 無窮大,一直等
    [self.socketClient writeData:printData withTimeout:-1 tag:0];
}


/// 發(fā)送數(shù)據(jù)(字典類型)
/// - Parameter dict: 發(fā)送的字典數(shù)據(jù)
- (void)sendTextMessageWithDict:(NSDictionary *)dict {
    
    if (dict == nil || dict.count == 0) {
        return;
    }
    if (!_socketClient) {
        NSLog(@"socket未連接");
        return;
    }
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&error];
    if (error) {
        return;
    }
    NSMutableData *printData = [[NSMutableData alloc] initWithData:jsonData];
    [printData appendData:[GCDAsyncSocket CRLFData]];
    // withTimeout -1 : 無窮大,一直等
    [self.socketClient writeData:printData withTimeout:-1 tag:0];
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容