iOS socket编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS socket编程相关的知识,希望对你有一定的参考价值。

//
//  ViewController.m
//  socket
//
//  Created by emerys on 16/3/2.
//  Copyright © 2016年 Emerys. All rights reserved.
//

#import "ViewController.h"

#warning 0000前面四位为消息长度,后接消息,最后以#结束

@interface ViewController ()<NSStreamDelegate,UITableViewDataSource>
{
    // 输入流
    NSInputStream *inputStream;
    //  输出流
    NSOutputStream *outputStream;
}
// 用以存储聊天记录,键值为 client_date/service_date
@property (nonatomic,strong) NSMutableArray *history;

@property (nonatomic,copy) NSString *msg;

@property (weak, nonatomic) IBOutlet UITextField *inputTextField;
@property (weak, nonatomic) IBOutlet UIButton *sendMsgButton;
@property (weak, nonatomic) IBOutlet UITableView *historyTableView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self loadHistory];
    
    self.historyTableView.dataSource = self;
    
    [self connect];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/**
 * @brief 懒加载字典
 */
-(NSMutableArray *)history{
    if (!_history) {
        _history = [NSMutableArray array];
    }
    return _history;
}
/**
 * @brief 开启链接
 */
-(void)connect{
    // 1.建立连接
    NSString *host = @"119.29.65.70";
    int port = 9868;

    // 定义C语言输入输出流
    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];
    
     // 打开输入输出流
    [inputStream open];
    [outputStream open];
}
/**
 * @brief 开启输入/出流监听
 */
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{

    switch (eventCode) {
        case NSStreamEventNone:{
            
        }
            break;
        case NSStreamEventOpenCompleted:{
            // 打开
//            [self showAlertMessage:@"连接服务器成功"];
            NSLog(@"ok");
        }
            break;
        case NSStreamEventHasBytesAvailable:{
            // 有数据可读
            [self readData];
        }
            break;
            

        case NSStreamEventHasSpaceAvailable:{
            // 可发
            if (self.msg) {
                [self writeData];
            }
            
        }
            break;
            

        case NSStreamEventErrorOccurred:{
            // 链接错误,那么重新链接
            [self connect];
        }
            break;
            

        case NSStreamEventEndEncountered:{
            // 结束链接,关闭流,从主运行循环移开
            [inputStream close];
            [outputStream close];
            
            [inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
            [outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        }
            break;
            
        default:
            break;
    }
    
}
/**
 * @brief 读取数据
 */
-(void)readData{
    // 设置1024字节的缓冲区
    uint8_t buf[1024];
    
    NSInteger length = [inputStream read:buf maxLength:1024];
    
    if (length > 0) {
        NSData *data = [NSData dataWithBytes:buf length:length];
#warning 在此编码方式不同可能会引起str为空,添加进数组时程序崩溃
        // gb2312
        NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingGB_18030_2000);
        
        NSString *str = [[NSString alloc] initWithData:data encoding:encoding];
        
//        NSLog(@"%@",str);
        [self.history addObject:str];
        [self.historyTableView reloadData];
        [self saveData];
    }
    
}
/**
 * @brief 发送数据
 */
-(void)writeData{
    NSString *msg = self.msg;
    NSString *head;
    NSUInteger msgLength = msg.length;
    // 超出最大限制,或没有数据, 则不发
    if (!msgLength && msgLength >= 10000) {
        [self showAlertMessage:@"您发送的数据长度超出限度,请重新编辑"];
    }else{
        if (msgLength < 10) {
            head = [NSString stringWithFormat:@"000%li",msgLength];
        }else if(msgLength >= 10 && msgLength < 100){
            head = [NSString stringWithFormat:@"00%li",msgLength];
        }else if (msgLength >= 100 && msgLength < 1000){
            head = [NSString stringWithFormat:@"0%li",msgLength];
        }else if (msgLength >= 1000 && msgLength < 10000){
            head = [NSString stringWithFormat:@"%li",msgLength];
        }
        NSString *str = [NSString stringWithFormat:@"%@%@#",head,msg];
        
        NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
        
        [outputStream write:data.bytes maxLength:data.length];
        self.msg = nil;
        [self.history addObject:msg];
        [self.historyTableView reloadData];
        [self saveData];
    }

}
/**
 * @brief 显示一些提示信息
 * @param msg 表示将要显示的信息
 */
-(void)showAlertMessage:(NSString *)msg{
    __block UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:msg preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [alert dismissViewControllerAnimated:YES completion:nil];
        alert = nil;
    }];
    [alert addAction:action];
    [self presentViewController:alert animated:YES completion:nil];
}
/**
 * @brief 发送数据按钮回调
 */
- (IBAction)senMessage:(id)sender {
    NSString *msg = self.inputTextField.text;
//    NSString *head;
    NSUInteger msgLength = msg.length;
    // 超出最大限制或没有数据, 则不发
    if (!msgLength && msgLength >= 10000) {
        [self showAlertMessage:@"您发送的数据长度超出限度,请重新编辑"];
    }else{
        self.msg = msg;
        [self stream:outputStream handleEvent:NSStreamEventHasSpaceAvailable];
    }

}


#pragma mark 数据源
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return [self.history count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    NSString *str = self.history[indexPath.row];
    cell.textLabel.text = str;
    return cell;
}
/**
 * @brief 检测文件是否存在
 */
-(BOOL)checkFile{
//    NSString *dPath = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) firstObject];
    NSString *home = NSHomeDirectory();
    NSString *path = [home stringByAppendingPathComponent:@"documents/history.plist"];

    NSFileManager *manager = [NSFileManager defaultManager];
    return [manager fileExistsAtPath:path];
}

/**
 * @brief 保存数据到本地
 */
-(void)saveData{
  
    if (self.history.count) {

        NSString *home = NSHomeDirectory();
        NSString *path = [home stringByAppendingPathComponent:@"documents/history.plist"];
        if([self.history writeToFile:path atomically:YES]){
            NSLog(@"save ok");
        }else{
            NSLog(@"save error");
        }

    }
    
   
}
/**
 * @brief 读取历史数据
 */
-(void)loadHistory{
    NSString *home = NSHomeDirectory();
    NSString *path = [home stringByAppendingPathComponent:@"documents/history.plist"];
    
    NSArray *array = [NSArray arrayWithContentsOfFile:path];
    if (array.count) {
        [self.history addObjectsFromArray:array];
        [self.historyTableView reloadData];
    }

}



@end

 

以上是关于iOS socket编程的主要内容,如果未能解决你的问题,请参考以下文章

Python 之 Socket编程(TCP/UDP)

java之socket编程(BIO)

初次试验最简单的socket编程

iOS网络编程笔记——Socket编程

iOS socket编程

iOS开发— Socket编程