iOS 网络编程socket NSSteam的使用

Socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等等。而TCP和UDP协议属于传输层 。而http是个应用层的协议,它实际上也建立在TCP协议之上。  (HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。)Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口。

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket .套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

1. 服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

2. 客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

3. 连接接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

以下是代码部分:


#import "ViewController.h"  

@interface ViewController () <NSStreamDelegate, UITextFieldDelegate, UITableViewDataSource>  
{  
    NSInputStream       *_inputStream;      // 输入流  
    NSOutputStream      *_outputStream;     // 输出流  
      
    NSMutableArray      *_dataList;  
}  
@property (weak, nonatomic) IBOutlet UITextField *hostName;  
@property (weak, nonatomic) IBOutlet UITextField *portText;  
@property (weak, nonatomic) IBOutlet UITextField *nickNameText;  
@property (weak, nonatomic) IBOutlet UITextField *messageText;  
@property (weak, nonatomic) IBOutlet UITableView *tableView;  
@end  
@implementation ViewController  
/**  
 通过Scoket可以实现所有的网络功能:包括:GET、POST、PUT、DELETE  
   
 最主要的应用场景是:自定义的协议,编写自由的网络应用!  
   
 ==========================================================  
 Socket 的难点:  
   
 1. 因为所有的输入输出都是在一个代理方法中调用,随着自定义协议的复杂度的提高,  
    程序编写难度势必要大幅度提升。  
   
 2. 多线程的处理!  
    示例代码中,输入流合输出流都添加到了主运行循环,如果应用过于复杂,将影响主线程程序的性能  
    因此,需要使用另外一个运行循环,专门管理输入输出流。  
   
    代理方法的工作是对数据的输入输出流进行“解析”,解析工作同样不需要影响到主线程的工作。  
   
 多线程方面的处理,是Socket的一大难点!  
   
 可以使用第三方框架!会在XMPP项目中隆重登场!  
   
 ============================================================  
   
 程序员写程序最主要的目的是用来阅读的,捎带着把程序的功能给实现了。  
   
 SSL 安全漏洞,goto fail。  
   
 */  
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
      
    _dataList = [NSMutableArray array];  
}  
- (BOOL)textFieldShouldReturn:(UITextField *)textField  
{  
    // 确认真的输入了文字,再发送消息给服务器  
    if (textField.text.length > 0) {  
        // 发送登录消息  
        NSString *msg = [NSString stringWithFormat:@"msg:%@", textField.text];  
        // 在网络上发送的是二进制数据  
        NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];  
          
        // 发送数据,直接往输入流写数据  
        [_outputStream write:data.bytes maxLength:data.length];  
    }  
      
    return YES;  
}  
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section  
{  
    return _dataList.count;  
}  
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath  
{  
    static NSString *ID = @"Cell";  
      
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];  
      
    cell.textLabel.text = _dataList[indexPath.row];  
      
    return cell;  
}  
#pragma mark - 私有方法  
#pragma mark 连接到服务器  
- (void)connectToServer:(NSString *)hostName port:(NSInteger)port  
{  
    // 要进行Socket开发,以下代码都是固定的  
    // 设置网络  
    CFReadStreamRef readStream;  
    CFWriteStreamRef writeStream;  
      
    // CF框架是C语言的框架,在OC中的Socet方法,C语言部分的代码,总共就5行  
    // 此方法可以连接到服务器,并分配输入流和输出流的内存空间  
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)hostName, port, &readStream, &writeStream);  
      
    // 记录已经分配的输入流和输出流  
    _inputStream = (__bridge NSInputStream *)readStream;  
    _outputStream = (__bridge NSOutputStream *)writeStream;  
      
    // 设置代理,监听输入流和输出流中的变化  
    _inputStream.delegate = self;  
    _outputStream.delegate = self;  
      
    // Scoket是建立的长连接,需要将输入输出流添加到主运行循环  
    // 如果不将流加入主运行循环,delegate拒绝工作  
    [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];  
    [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];  
      
    // 打开输入流和输出流,准备开始文件读写操作  
    [_inputStream open];  
    [_outputStream open];  
}  
#pragma mark NSStream的代理方法  
/**  
 NSStreamEventNone = 0,                         // 无事件  
 NSStreamEventOpenCompleted = 1UL << 0,         // 建立连接完成  
 NSStreamEventHasBytesAvailable = 1UL << 1,     // 有可读的字节,接收到了数据,可以读了  
 NSStreamEventHasSpaceAvailable = 1UL << 2,     // 可以使用输出流的空间,此时可以发送数据给服务器  
 NSStreamEventErrorOccurred = 1UL << 3,         // 发生错误  
 NSStreamEventEndEncountered = 1UL << 4         // 流结束事件,在此事件中负责做销毁工作  
 */  
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode  
{  
    NSLog(@"%d", eventCode);  
      
    switch (eventCode) {  
        case NSStreamEventOpenCompleted:  
            NSLog(@"连接完成");  
            break;  
        case NSStreamEventHasBytesAvailable:  
            NSLog(@"有可读字节");  
            // 读从服务器接收到得数据,从输入流中读取  
            // 先开辟一段缓冲区以读取数据,用空间来换取程序的简单  
            uint8_t buffer[1024];  
              
            // read返回的是输入流缓冲区中实际存储的字节数  
            NSInteger len = [_inputStream read:buffer maxLength:sizeof(buffer)];  
              
            if (len > 0) { // 读到数据  
                // 将buffer中的数据,转换成字符串,输出  
                NSString *str = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];  
                  
                // 将接收到的内容添加到数组  
                [_dataList addObject:str];  
                  
                // 刷新表格  
                [_tableView reloadData];  
            }  
              
            break;  
        case NSStreamEventHasSpaceAvailable:  
            NSLog(@"可以写入数据");  
            break;  
        case NSStreamEventErrorOccurred:  
            NSLog(@"发生错误");  
            break;  
        case NSStreamEventEndEncountered:  
            NSLog(@"流结束");  
            // 做善后工作  
            // 关闭流的同时,将流从主运行循环中删除  
            [aStream close];  
            [aStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];  
        default:  
            break;  
    }  
}  
#pragma mark - Action  
#pragma mark 登录到聊天室  
- (IBAction)login  
{  
    NSString *hostName = _hostName.text;  
    NSInteger port = [_portText.text integerValue];  
      
    [self connectToServer:hostName port:port];  
      
    // 发送登录消息  
    NSString *msg = [NSString stringWithFormat:@"iam:%@", _nickNameText.text];  
    // 在网络上发送的是二进制数据  
    NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];  
      
    // 发送数据,直接往输入流写数据  
    [_outputStream write:data.bytes maxLength:data.length];  
}  

@end


                  注:原文出自http://www.cnblogs.com/mcj-coding/p/3579329.html?utm_source=tuicool

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值