Socket就是为网络服务提供的一种机制。
通信的两端都是Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket之间通过IO传输。
Socket服务器有两种一种是类似于QQ的服务器,另一种就是网站服务器。所谓服务器就是提供资源访问的。网站服务器是短连接。聊天室是长连接。
所谓的短连接比如访问网址时便会返回网站数据,返回数据后就会关闭连接。
但是长连接一旦建立连接就会一直监听数据的发送与接收,不会关闭连接。
所谓的IO就是输入输出流。输出流:用来写数据(发送数据)。 输入流:用于读数据(接收数据)。
在工程的网络连接中会有很多C语言与OC类型数据进行转换的操作,转换的技巧如下:保存,点击错误提示的红色圆点,会出现修正提示,点击第一项bridge桥接即可自动进行类型转换。
Socket聊天室的大致原理过程如下:
因为内容繁多,只在代码内做注释,不再剖析方法的使用。
新建工程,具体如下:
第一步:打开聊天室服务器文件
第二步:查看服务器文件的内容(端口号,以及数据发送格式)。
第三步:进行界面搭建
3.1
3.2
3.3
3.4
3.5
3.6
3.7
viewController视图控制器的代码如下:
//
// ViewController.m
// 聊天室
//
// Created by apple on 15/11/4.
// Copyright (c) 2015年 LiuXun. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<NSStreamDelegate>
{
NSInputStream *_inputStream; // 对应输入流
NSOutputStream *_outputStream; // 对应输出流
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 收发数据
// 做一个聊天
// 1. 用户登录
// 2. 登录成功后即可收发数据
}
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
NSLog(@"%@", aStream);
/*
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(@"连接结束");
break;
default :
break;
}
}
- (IBAction)connectToHost:(id)sender {
// 1. 第一步建立连接
/* 点击参数——>在编辑界面右侧就有此参数的描述
第一个参数CFAllocatorRef alloc:用于为输入输出流分配空间,如果传为NULL就会使用默认的大小分配
第二个参数CFStringRef host:所要连接到主机的IP地址。即要连接到哪台主机。
第三个参数port:所要连接到主机的具体端口号。
*/
NSString *host = @"127.0.0.1"; // 因为是C语言函数所以要进行桥接转换为C语言字符串
int port = 12345;
// 定义输入输出流
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(host), port, &readStream, &writeStream);
// 将C语言的输入输出流转化为OC对象
// 如果要想知道连接成功还是失败,可以通过OC中的代理进行实现。所以需要将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];
}
- (IBAction)loginBtnClick:(id)sender {
// 登录
// 发送用户名和密码
// 在这里做的时候,只发用户名,密码就不用发送
// 如果要登录,发送的数据格式为 "iam:zhangsan"
// 如果要发送聊天消息,数据格式为"msg:did you have dinner"
// uint8_t typedef —> unsigned char uint8_t;
// (const uint8_t *) 意味着要传入一个字节数组
// maxLength: 表示发送字节数组中的前多少个字节
// 登录的指令
NSString *loginStr = @"iam:zhangsanXXXXXXX";
// 把Str转化为NSData
NSData *data = [loginStr dataUsingEncoding:NSUTF8StringEncoding];
[_outputStream write:data.bytes maxLength:data.length];
}
#pragma mark - 读取服服务器返回的数据
-(void)readData
{
// 建立缓冲区,可以存放1024个字节
uint8_t buf[1024];
// 返回实际装的字节
NSInteger len = [_inputStream read:buf maxLength:sizeof(buf)];
// 把字节数组转化为字符串
NSData *data = [NSData dataWithBytes:buf length:len];
// 从服务器接收到的数据
NSString *reciveStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", reciveStr);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
运行结果如下: