本篇文章简单介绍一下iOS的socket即时通讯底层的实现。
<1>socket简单的本地服务器的搭建,大家可以参考博客http://www.jianshu.com/p/39a68332e19f 。
<2>iOS socket编程的底层是通过调用C语言的函数,并通过OC和C语言的桥接,通过输入输出流进行数据读写,进行及时通讯。下面是一个简单的socket网络编程的demo。
//
// ViewController.m
// SocketTest
//
// Created by fe on 16/9/4.
// Copyright © 2016年 fe. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<NSStreamDelegate,UITextFieldDelegate,UITableViewDelegate,UITableViewDataSource>
@property(nonatomic,strong) NSInputStream *inputStream;
@property(nonatomic,strong) NSOutputStream *outputStream;
//拿到输入框文本,以便修改位置
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textFieldCounterbottom;
//用于存放聊天内容的数组
@property (nonatomic,strong) NSMutableArray *msgArry;
@property (weak, nonatomic) IBOutlet UITableView *msgTableView;
@end
@implementation ViewController
- (NSMutableArray *)msgArry
{
if (!_msgArry) {
_msgArry = [[NSMutableArray alloc] init];
}
return _msgArry;
}
- (void)viewDidLoad {
[super viewDidLoad];
//监听键盘
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(theKeyBoardDidChanged:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
#pragma mark - NSStreamDelegate
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
// NSStreamEventOpenCompleted = 1UL << 0,//输入输出流打开完成
// NSStreamEventHasBytesAvailable = 1UL << 1,//有字节可读
// NSStreamEventHasSpaceAvailable = 1UL << 2,//可以发送字节
// NSStreamEventErrorOccurred = 1UL << 3,//链接出现了错误
// NSStreamEventEndEncountered = 1UL << 4//结束
switch (eventCode) {
case NSStreamEventOpenCompleted:
NSLog(@"输入输出流打开完成");
break;
case NSStreamEventHasBytesAvailable:
NSLog(@"有字节可读");
[self readData];
break;
case NSStreamEventHasSpaceAvailable:
NSLog(@"可以发送字节");
break;
case NSStreamEventErrorOccurred:
NSLog(@"链接出现了错误");
break;
case NSStreamEventEndEncountered:
NSLog(@"结束");
//关闭输入输出流
[self.inputStream close];
[self.outputStream close];
//从主循环移除
[self.inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
break;
default:
break;
}
}
- (IBAction)connectToInternet:(id)sender {
//1:建立连接
NSString *localHost = @"127.0.0.1";//本机地址
int port = 12345;//服务器端口
//定义c语言输入输出流
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(localHost), port, &readStream, &writeStream);
//把c语言的输入输出流转化为oc对象
self.inputStream = (__bridge NSInputStream *)readStream;
self.outputStream = (__bridge NSOutputStream *)(writeStream);
//设置输入输出流的代理
self.inputStream.delegate = self;
self.outputStream.delegate = self;
//把输入输出流添加到主运行循环
[self.inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
//打开输入输出流
[self.inputStream open];
[self.outputStream open];
}
- (IBAction)login:(id)sender {
//2:收发数据
//2.1:登陆 发送用户名和密码 这里我们只简单的发送一个用户名进行登陆
//如果登陆发送的数据格式为 “iam:zhengyanfneg”
//如果发送消息发送的数据格式为 “msg:How are you”
//登陆指令
NSString *loginStr = @"iam:zhengyanfeng";
//将字符串转化为data
NSData *loginData = [loginStr dataUsingEncoding:NSUTF8StringEncoding];
[self.outputStream write:loginData.bytes maxLength:loginData.length];
}
#pragma mark - 读取服务器返回的数据
- (void)readData
{
//建立数据缓冲区可以放1024个字节(字节数组)
uint8_t buf[1024];
//length 返回的实际字节长度
NSInteger length =[self.inputStream read:buf maxLength:sizeof(buf)];
//把字节转化成字符串
NSData *receiveData =[NSData dataWithBytes:buf length:length];
NSString *receiveStr = [[NSString alloc] initWithData:receiveData encoding:NSUTF8StringEncoding];
[self reloadDataWithString:receiveStr];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
//发送聊天数据
NSString *mesStr = [NSString stringWithFormat:@"msg:%@",textField.text];
[self reloadDataWithString:mesStr];
NSData *strData = [mesStr dataUsingEncoding:NSUTF8StringEncoding];
[self.outputStream write:strData.bytes maxLength:strData.length];
textField.text = nil;
return YES;
}
//监听键盘位置变化的方法
- (void)theKeyBoardDidChanged:(NSNotification *)noti{
NSLog(@"%@",noti.userInfo);
CGFloat windowH = [UIScreen mainScreen].bounds.size.height;
CGRect keyboardFrame = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardY = keyboardFrame.origin.y;
self.textFieldCounterbottom.constant = windowH - keyboardY;
}
#pragma mark - UITableViewDelegate UITableViewDataSource
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.msgArry.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *const reuseID = @"socketCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID];
cell.textLabel.text = self.msgArry[indexPath.row];
return cell;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self.view endEditing:YES];
}
//向聊天数组添加数据并刷新表格
- (void)reloadDataWithString:(NSString *)msgStr
{
[self.msgArry addObject:msgStr];
[self.msgTableView reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(self.msgArry.count -1 ) inSection:0];
[self.msgTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
@end