【iOS】Socket通信

Socket是网络传输层的一种技术,跟http有本质的区别,http是应用层的一个网络协议。
Socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 Socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

http和Socket的区别:

  • Socket是长连接。http是短连接
  • Socket是双向通信,http是单向的,只能客户端向服务器发送数据
  • Socket的数据完全由自己组织,http必须按照http协议来发送

下载CocoaAsyncSocket

点击前往github下载地址

GCDAsyncSocket 网络请求使用

我们使用Socket时,可以选择tcp或者udp,所以下面以tcp为例

ViewController.h

#import <UIKit/UIKit.h>

enum
{
    SOCKET_CONNECT_USER, //用户断开连接
    SOCKET_CONNECT_SERVER, //服务器连接失败
};

@interface ViewController : UIViewController

@end

ViewController.m

#import "ViewController.h"
/*导入头文件*/
#import "GCDAsyncSocket.h"

#define BaseHost @"wangys.com"       //host地址
#define BasePort 80                  //端口
#define BaseHeartStr @"wys"          //心跳数据
#define BaseSocktConnectStr @"request_data" //请求链接数据

/* 记住要设置代理 */
@interface ViewController ()<GCDAsyncSocketDelegate>
@property(nonatomic,strong)GCDAsyncSocket * mysocket;
@property(nonatomic,strong)NSTimer * myTimer;
@property (weak, nonatomic) IBOutlet UIButton * sendDataBtn;
@property (weak, nonatomic) IBOutlet UITextField * DataStrText;
@property (weak, nonatomic) IBOutlet UITextField * receiveText;
@end

@implementation ViewController

-(void)viewDidLoad
{
    [super viewDidLoad];
}

#pragma mark - creat连接
//创建连接
-(void)createSocket
{
    if (self.mysocket != nil) return;
    
    self.mysocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    [self tcpConnectWithData:BaseSocktConnectStr];
}

-(void)tcpConnectWithData:(NSString *)userData
{
    [self.mysocket setUserData:userData];
    
    NSError * error = nil;
    
    if (![self.mysocket connectToHost:BaseHost onPort:BasePort withTimeout:2.0f error:&error])
    {
        NSLog(@"error:%@",error);
    }
}

#pragma mark - 发送心跳包
//定时发送心跳包
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    self.myTimer = [NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(sendHeartData) userInfo:nil repeats:YES];
    
    [self.myTimer fire]; //先执行一次
}

-(void)sendHeartData
{
    NSString * heartStr = BaseHeartStr;
    
    NSData * hData = [heartStr dataUsingEncoding:NSUTF8StringEncoding];
    
    [self.mysocket writeData:hData withTimeout:10.0f tag:0];
}

#pragma mark - 向服务器发送数据
//发送数据
-(IBAction)sendData:(UIButton *)sender
{
    NSString * dataStr = self.DataStrText.text;
    
    NSData * dataForTesxt = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
    
    [self.mysocket writeData:dataForTesxt withTimeout:10.0f tag:1];
}

//向服务器发送完数据之后回调
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    if (tag == 1)
    {
        NSLog(@"发送成功");
    }
}

#pragma mark - 服务器向客户端发送数据
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    [self.mysocket readDataWithTimeout:10.0f tag:tag];
    
    [self receiveData:data];
}

-(void)receiveData:(NSData *)data
{
    //显示
    self.receiveText.text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
}

#pragma mark - 断网处理
//如果不是手动切断,是其他原因,所以要重新连接,断开连接的时候会调用这个函数
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    if ([sock.userData isEqualToString:[NSString stringWithFormat:@"%d",SOCKET_CONNECT_SERVER]])
    {
        //服务器掉线 重新连接
        [self tcpConnectWithData:BaseSocktConnectStr];
    }
    else
    {
        return;
    }
}

//用户手动断开网络(这个方法我没写调用)
-(void)disconnectSocket
{
    self.mysocket.userData = SOCKET_CONNECT_USER;
    
    [self.myTimer timeInterval];
    
    [self.mysocket disconnect];
}

@end

Cocoa Streams使用详解

Cocoa Streams实际上是Objective-C对CFNetwork的简单封装,主要包含了三个类:NSStream, NSInputStream, and NSOutputStream。
它使用名为 NSStreamDelegate 的协议来实现 CFNetwork 中的回调函数的作用,同样,runloop 也与 NSStream 结合的很好。

#import "ViewController.h"

#define BaseHost @"wangys.com"
#define BasePort 12345

@interface ViewController ()<NSStreamDelegate>
{
    NSInputStream  * inputStream;
    NSOutputStream * outputStream;
}
@end

@implementation ViewController

-(void)viewDidLoad
{
    [super viewDidLoad];
}

//建立连接
-(void)connectToHost:(UIButton *)button
{
    NSString * host = BaseHost;
    int port = BasePort;
    
    // 1.定义C语言输入输出流
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)(host), port, &readStream, &writeStream);
    
    // 2.把C语言的输入输出流转化成OC对象
    inputStream = (__bridge NSInputStream *)(readStream);
    outputStream = (__bridge NSOutputStream *)(writeStream);
    
    // 3.设置代理
    inputStream.delegate = self;
    outputStream.delegate = self;
    
    // 4.输入输出流添加到runloop循环中
    [inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    
    // 5.打开输入输出流
    [inputStream open];
    [outputStream open];
}

//登录
-(void)loginClick:(UIButton *)button
{
    /**
     * 如果要登录,发送的数据格式为 "iam:wys";
     * 如果要发送聊天消息,数据格式为 "msg:send a message";
     */
    
    //登录指令
    NSString * loginString = @"iam:wys";
    
    NSData * data = [loginString dataUsingEncoding:NSUTF8StringEncoding];
    
    [outputStream write:data.bytes maxLength:data.length];
}

//处理流事件
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    NSLog(@"current thread : %@",[NSThread currentThread]);
    
    /**
     * NSStreamEventOpenCompleted = 1UL << 0      //输入输出流打开完成
     * NSStreamEventHasBytesAvailable = 1UL << 1  //有字节可读
     * NSStreamEventHasSpaceAvailable = 1UL << 2  //可以发放字节
     * NSStreamEventErrorOccurred = 1UL << 3      //连接出现错误
     * NSStreamEventEndEncountered = 1UL << 4     //连接结束
     */
    
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            break; 

        case NSStreamEventHasBytesAvailable:
            [self havaBytesToRead];
            break;

        case NSStreamEventHasSpaceAvailable:
            break;  

        case NSStreamEventErrorOccurred:
            break;
            
        case NSStreamEventEndEncountered:
            [self endConnect];
            break;
            
        default:
            break;
    }
}

//有字节可读
-(void)havaBytesToRead
{
    // 1.建立一个缓冲区 可以放1024字节
    uint8_t buf[1024];
    
    // 2.返回实际装的字节数
    NSInteger length = [inputStream read:buf maxLength:sizeof(buf)];
    
    // 3.把字节数组转化成字符串
    NSData * data = [NSData dataWithBytes:buf length:length];
    NSString * readStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    
    [self showMessage:readStr];
}

//关闭连接
-(void)endConnect
{
    // 关闭输出输入流
    [inputStream close];
    [outputStream close];
    
    //从主运行循环中移除
    [inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

//显示信息
-(void)showMessage:(NSString *)string
{
    NSLog(@"showMessage:%@",string);
}
@end

2016-12-23更新

TCPUDP的区别

基于连接与无连接   
对系统资源的要求(TCP较多,UDP少)   
UDP程序结构较简单   
流模式与数据报模式   
TCP保证数据正确性,UDP可能丢包   
TCP保证数据顺序,UDP不保证   
UDP Server不需要调用listen和accept   
UDP收发数据用sendto/recvfrom函数   
TCP:地址信息在connect/accept时确定   
UDP:在sendto/recvfrom函数中每次均 需指定地址信息   
UDP:shutdown函数无效

参考文献
IOS开发之SOCKET长连接的使用
Socket简介
iOS中socket使用
GCDAsyncSocket 网络请求
iOS学习之Socket使用简明教程- AsyncSocket

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 一、概念 首先,理清一些概念 TCP/IP和UDP,HTTP协议,Socket 1.TCP/IP和UDP,是网络中...
    _AJH阅读 4,174评论 0 18
  • 一、网络各个协议:TCP/IP、SOCKET、HTTP等 网络七层由下往上分别为物理层、数据链路层、网络层、传输层...
    杯水救车薪阅读 2,266评论 0 17
  • 对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想...
    yuantao123434阅读 5,457评论 1 97
  • 用socket从服务器上获取数据,有以下三种方法:第一种是使用封装好的第三方GCDAsyncSocket,下载地址...
    CMD独白阅读 1,148评论 3 5
  • 人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟。 ____...
    璐帆阅读 163评论 0 0