iOS Socket深入剖析

来源:互联网 发布:印度 人口 知乎 编辑:程序博客网 时间:2024/05/21 22:37

1.定义:

Socket是网络服务提供的一种套接字,通信两端都是Socket,网络通信通过Socket进行IO直接的传输,是一种长连接,让应用程序可以进行异步模式,提高速率,需要数据的时候才进行数据接收。

2.既然要了解Socket,就要对传输协议有一定的了解,可以把socket理解为封装TCP/IP协议API,TCP协议属于可靠性传输协议,需要进行三次握手(客户端发送信息到服务器,服务器响应发送到客户端,客户端响应后发送到服务器) UDP协议属于不可靠协议,是因为他不需要进行响应,就像知道一个大概的位置,然后就传送过去,每次能传64K左右的东西,传输速度很快,但是有时会丢包的情况。  所以视频,或则QQ群聊会用到UDP协议,QQ在单人聊天的时候,应该是进行TCP协议,所以个人认为QQ采用的是混合模型。

3.网络通信的三大要素:IP地址,传输协议,端口号。

IP地址:设备的唯一标示符。

传输协议:TCP UDP等

端口号:1-1023都是系统自带,如果个人要用的话需要1024以后的。需要熟记几个端口号,80,25,21等这里就不一一叙述。

4.开始进行代码

iOS主要按照NSStream(流)来发送和接收数据,可以设置流的代理,对流的状态变化做出相应连接建立,设置网络连接,绑定到主机和端口号,设置输入输出代理,监听数据流的状态,将输入输出添加至运行循环,打开输入输出流。

发送消息给服务器,有可读取字节时,读取服务器返回的内容,到达流末尾时,关闭流,同时并从主运行循环中删除。

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{

    

    switch (eventCode) {

        case NSStreamEventOpenCompleted:

            NSLog(@"完成");

            break;

        case NSStreamEventHasBytesAvailable:

            NSLog(@"字节可读");

            [self readData];

            break;

        case NSStreamEventHasSpaceAvailable:

            NSLog(@"可发送字节");

            break;

        case NSStreamEventErrorOccurred:

            NSLog(@"连接错误");

            break;

        case NSStreamEventEndEncountered:

            NSLog(@"断开连接");

            [self closeConnect];

            break;

            

            

        default:

            NSLog(@"异常错误");

            break;

    }

    

}

- (void)closeConnect{



    //1.输入输出流

    [_inputStream close];

    [_outputStream close];

    //跳出运行循环

    [_inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

    [_outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

    

    

    

}

-(void)readData{

    //1.简历一个字节数组用于存放读取数据,大小为1024

    uint8_t buf[1024];

    //返回一个真实的数据大小

    NSInteger len =   [_inputStream read:buf maxLength:sizeof(buf)];

    if (len > 0) {

        NSData  *data = [NSData dataWithBytes:buf length:len];

        NSString *dataString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

        [_dataArray addObject:dataString];

        [self.tableView reloadData];

    }

    

}





- (IBAction)login:(id)sender {

    //iam:定义的用户名协议

    NSString  *userName = @"iam:Snowwing";

    NSData  *data = [userName dataUsingEncoding:NSUTF8StringEncoding];

    

    [_outputStream write:data.bytes maxLength:data.length];

    

}

- (IBAction)connect:(id)sender {

    //主机IP地址

    NSString  *host = @"127.0.0.1";

    //端口号

    int port = 12345;

    //读写数据流  要转换成OC

    CFReadStreamRef readStream;

    CFWriteStreamRef writeStream;

    //绑定到主机和端口

    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);

    //C语言输入输出流转换成OC

    _inputStream = (__bridge NSInputStream *)(readStream);

    _outputStream = (__bridge NSOutputStream *)(writeStream);

    //设置代理

    _inputStream.delegate = self;

    _outputStream.delegate = self;

    //将输入输出流添加至循环

    [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

    [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

    //打开输入输出流

    [_outputStream open];

    [_inputStream open];

   

}


- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{


    NSString *tftext = textField.text;

    NSString  *msg = [NSString stringWithFormat:@"msg:%@",tftext];

    NSData  *data = [msg dataUsingEncoding:NSUTF8StringEncoding];

   

    

    [_outputStream write:data.bytes maxLength:data.length];

    

    textField.text = nil;

    

    return YES;

}

这里的服务器是调用第三方的服务器接口进行描述的

- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket {

    if(![_clientSockets containsObject:newSocket]){

        [_clientSockets addObject:newSocket];

        NSLog(@"%@链接主机成功",newSocket);

        [newSocket readDataWithTimeout:-1 tag:0];

    }

}

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {

    //获得接受字符

    NSString *recverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    

    //对字符串尾部回车做处理

    while ([recverStr hasSuffix:@"\n"] || [recverStr hasSuffix:@"\r"]) {

        recverStr = [recverStr substringToIndex:recverStr.length - 2];

    }

    

    NSUInteger index = [_clientSockets indexOfObject:sock];

    NSString *key = [NSString stringWithFormat:@"%lu",index];

    if ([recverStr hasPrefix:@"iam:"] && _clientNames[key] == nil) {

        recverStr = [recverStr componentsSeparatedByString:@":"][1];

        //将名字设为key以便找到对应socket进行单聊

        [_clientNames setObject:key forKey:recverStr];

        //存入名字方便以后消息发送时查找名字

        _clientNames[key] = [recverStr stringByAppendingString:@":"];

        

        recverStr = [recverStr stringByAppendingString:@" has join!"];


        for (GCDAsyncSocket *client in self.clientSockets) {

            [client writeData:[recverStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

        }

    }else if([recverStr hasPrefix:@"msg:"]){

        NSString *name = _clientNames[key];

        recverStr = [recverStr componentsSeparatedByString:@":"][1];

        if ([recverStr hasPrefix:@"/to "]) {

            NSString *toName = [recverStr componentsSeparatedByString:@" "][1];

            recverStr = [recverStr componentsSeparatedByString:@" "][2];

            recverStr = [name stringByAppendingString:recverStr];

            

            if (_clientNames[toName]) {

                GCDAsyncSocket *socket = [_clientSockets objectAtIndex:[_clientNames[toName] integerValue]];

                [socket writeData:[recverStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

            }

        

        }else {

        

            recverStr = [name stringByAppendingString:recverStr];

            for (GCDAsyncSocket *client in self.clientSockets) {

                [client writeData:[recverStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

            }

        }

    }else if ([recverStr isEqualToString:@"quit"]) {

        //断开连接

        [sock setDelegate:nil];

        [sock disconnect];

        

        [_clientNames removeObjectForKey:key];

        [_clientSockets removeObject:sock];

    }

    

    [sock readDataWithTimeout:-1 tag:0];

}


效果图:


1 0