美文网首页
iOS GCDAsyncSocket 发送和接收粘包问题解决方案

iOS GCDAsyncSocket 发送和接收粘包问题解决方案

作者: 蓝雪清晨 | 来源:发表于2024-07-02 11:45 被阅读0次

服务端

@property (nonatomic, strong) GCDAsyncSocket *serverSocket; //服务端socket
@property (nonatomic, strong) GCDAsyncSocket *clientSocket;//客户端连接过来的socket

//创建服务端并启动服务
- (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对象
    self.clientSocket.delegate = self;//设置代理
    //读取数据,以CRLFData结尾即表示是一条数据(以此来断开多个包一起发过来的粘包问题)
    [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);
}
//读取数据
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    //接收到的消息处理
    NSData *newData = [data subdataWithRange:NSMakeRange(0, data.length-2)];
    //先尝试转字典
    NSDictionary *result = [NSJSONSerialization JSONObjectWithData:newData options:kNilOptions error:nil];
    if (result) {
              //处理获取到的字典数据
        }
    } else {
        //如果字典转换失败,则尝试转换字符串
        NSString *content = [[NSString alloc] initWithData:newData encoding:NSUTF8StringEncoding];
        NSLog(@"--接收到的消息-- content = %@",content);
        //接收到的字符串处理
    }
    //持续接收数据  以CRLFData结尾即表示是一条数据(以此来断开多个包一起发过来的粘包问题)
    //如果不在读取完以后写这个方法,那就只读取一次数据,不会持续读取
    [self.clientSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
}

#pragma mark - 写数据成功回调
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"------发送完成---------");
}

#pragma mark - 发送数据
 //发送文本类型消息
- (void)sendTextMessage:(NSString *)text {
    
    if (text.length == 0 || text == nil) {
        return;
    }
    if (!_clientSocket) {
        NSLog(@"socket未连接");
        return;
    }
    NSLog(@"发送的数据---(%@)",text);
    NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData *printData = [[NSMutableData alloc] initWithData:data];
    //拼接结束符
    [printData appendData:[GCDAsyncSocket CRLFData]];
    // withTimeout -1 : 无穷大,一直等
    [self.clientSocket writeData:printData withTimeout:-1 tag:0];
}
//发送字典数据
- (void)sendTextMessageWithDict:(NSDictionary *)dict {
    if (dict == nil || dict.count == 0) {
        return;
    }
    if (!_clientSocket) {
        NSLog(@"socket未连接");
        return;
    }
    
    NSLog(@"发送的数据---(%@)",dict);
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&error];
    //字典转data结果判断
    if (error) {
        return;
    }
    
    NSMutableData *printData = [[NSMutableData alloc] initWithData:jsonData];
     //拼接结束符
    [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     本地回环 (Loopback)
 en     无线网 (WiFi)
 pdp_ip 蜂窝网络 (Cellular)
 awdl   通信Wi-Fi (Apple Wireless Direct Link,用于AirDrop、AirPlay、GameKit 和 Apple Pencil )
 utun   虚拟隧道 (VPN)
 ipsec  虚拟隧道 (VPN)
 llw    低延迟无线局域网(Low-latency WLAN)
 */
/// 获取ip地址
/// - Parameter isCellular: 是否获取蜂窝网络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网络接口
                        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";//设置自己的端口
    
    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];
    });
}

//获取数据失败时,会被调用
- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error {
    NSLog(@"connection failed: %@",[error localizedDescription]);
}

#pragma mark - 读数据
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
     //接收到的消息处理
    NSData *newData = [data subdataWithRange:NSMakeRange(0, data.length-2)];
    //先尝试转字典
    NSDictionary *result = [NSJSONSerialization JSONObjectWithData:newData options:kNilOptions error:nil];
    if (result) {
              //处理获取到的字典数据
        }
    } else {
        //如果字典转换失败,则尝试转换字符串
        NSString *content = [[NSString alloc] initWithData:newData encoding:NSUTF8StringEncoding];
        NSLog(@"--接收到的消息-- content = %@",content);
        //接收到的字符串处理
    }
    //持续接收数据  以CRLFData结尾即表示是一条数据(以此来断开多个包一起发过来的粘包问题)
    //如果不在读取完以后写这个方法,那就只读取一次数据,不会持续读取
    [self.socketClient readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
}

#pragma mark - 写数据成功回调
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"------发送完成---------");
}


#pragma mark 发送文本类型消息
- (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];
}


/// 发送数据(字典类型)
/// - Parameter dict: 发送的字典数据
- (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];
}

相关文章

  • 2019-11-19

    GCDAsyncSocket解决粘包问题 发送端 数据源头部添加文字总数 - (void)sendMessage:...

  • 7.tcp粘包处理1

    1.什么是 TCP 粘包?TCP 粘包是指发送方发送的若干包数据 到 接收方接收时粘成一包,从接收缓冲区看,后一包...

  • TCP 半包粘包问题

    什么是粘包现象 TCP 粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着...

  • day32-粘包问题和报头的定制

    粘包问题 TCP协议作为流式协议,只有TCP协议存在粘包问题。 发送端可以是一K一K地发送数据,而接收端的应用程序...

  • TCP粘包现象

    什么是粘包现象 TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前...

  • 关于Socket 中粘包的处理

    粘包:指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。出现粘包...

  • tcp粘包 go和swoole

    tcp 粘包及处理 go 和swoole tcp粘包形成的原因 tcp在发送和接收时会有个缓存,当短时间内发送大量...

  • 粘包问题和解决方法

    什么是粘包问题 发送方发送的若干包数据到接收方时粘成一包,从接受缓冲区来看就是后一包数据的头紧接着前一包数据的尾。...

  • 无法使用GCDAsyncSocket接收发送的UDP数据包的响应

    转载自:无法使用GCDAsyncSocket接收发送的UDP数据包的响应? 场景: 客户端向服务端的端口A发送数据...

  • 即时通讯

    iOS即时通讯,从入门到“放弃”?socket的半包,粘包与分包的问题iOS 处理socket粘包问题iOS___...

网友评论

      本文标题:iOS GCDAsyncSocket 发送和接收粘包问题解决方案

      本文链接:https://www.haomeiwen.com/subject/ghpvcjtx.html