☞ 客户端向劳动器端发送网络要前。    ☞ 客户端向劳动器端发送网络要前。

一、概念

  • Socket 字面意思又如“套接字”

  • 网络达到的鲜只次(如,客户端以及劳务器端)通过一个双向的通信连接实现多少的置换,这个连续的如出一辙端称为一个Socket。

  • 应用程序一般是事先经过Socket来树一个通信连接,再向网发出请求或响应网络要。

  图片 1

  说明:

    ☞ 客户端向服务器端发送网络要前,必须要先行在底部建立一个通信连接(通信管道),才能够发送网络要。

客户端向劳动器端发送http请求,服务器返回数据,这个过程尽管是一个数据交换的历程。

客户端与劳动器端进行数据交换,需要事先建一个双向的通信连接(即同漫漫线、一个通路)

    ☞ 客户端和劳务端
两端都有一个Socket,通过Socket建立一个连接(双向通信管道),有矣管道就得拓展数据传。

    ☞ Socket 就是通信管道的星星独端口,可以知晓吧管道的入口/出口。

一、概念

  • Socket 字面意思又如“套接字”

  • 网直达之少单程序(如,客户端与服务器端)通过一个双向的通信连接实现数量的交换,这个连续的一律端称为一个Socket。

  • 应用程序一般是先行经过Socket来起一个通信连接,再朝着网络发出请求或响应网络要。

  图片 2

  说明:

    ☞ 客户端向劳动器端发送网络要前,必须使事先在底部建立一个通信连接(通信管道),才会发送网络要。

客户端向服务器端发送http请求,服务器返回数据,这个历程就是是一个数据交换的历程。

客户端与劳务器端进行数据交换,需要事先成立一个双向的通信连接(即一律漫长线、一个坦途)

    ☞ 客户端和服务端
两端都起一个Socket,通过Socket建立一个连(双向通信管道),有了管道就得展开数量传。

    ☞ Socket 就是通信管道的鲜只端口,可以知道吧管道的进口/出口。

老二、网络通信的因素

  网络上之求虽是通过Socket来确立连接然后相互通信

  1.
IP地址(网络达到主机设备的绝无仅有标识)——>寻找服务器主机

  2. 端口号(定位程序) ——> 寻找程序

    • 用以标示进程的逻辑地址,不同进程的标志
    • 有效端口:0~65535,其中0~1024是因为网采用要封存端口,开发被建议以1024以上之端口

  3.
传协议(就是用哪的点子进行互动)

  • 报导的平整
  • 周边协议:TCP、UDP

老二、网络通信的元素

  网络直达之请虽是透过Socket来确立连接然后互相通信

  1.
IP地址(网络及主机设备的绝无仅有标识)——>寻找服务器主机

  2. 端口号(定位程序) ——> 寻找程序

    • 用以标示进程的逻辑地址,不同进程的标记
    • 有效端口:0~65535,其中0~1024出于系统应用还是封存端口,开发中建议采用1024之上之端口

  3.
传协议(就是之所以什么的章程开展互)

  • 简报的平整
  • 大面积协议:TCP、UDP

老三、传输协议 TCP/UDP

  TCP和UDP:数据传的有限种植方式,即把多少从同端传至任何一样端的蝇头栽艺术

  1.
TCP(传输控制协议)
—>要建立连接(如:发送HTTP请求,客户端向服务端发送网络要)

☞ 建立连接,形成传输数据的大路

☞ 在一连着进行非常数目传(数据大小非给限制)

☞ 通过三糟糕握手完成连接,是可靠协议,安全送达

        说明:在确立通信连接(打通管道)之前来三次于握手,目的是为了多少的安全性与可靠性(让数安全可靠的导到对方)。

        举例:打电话 (理解三软握手)

率先浅握手:拿起电话,进行拨号。这个拨号的进程叫第一赖握手。【开始准备连接】

第二不好握手:拨通了,对方”喂”了平名(响应了一如既往声),我听见了,称为第二糟糕握手。【说明我一连而 没问题】

其三不成握手:我听到了对方”喂”了同一名(响应了一样声),我吗习惯性的”喂”了一致望,对方听到了。【说明你连我 没问题】

一旦及时三单过程都无问题,就可确定通话连接起成。

    ☞ 必须树立连接,效率会稍微小。(每次要都要成立连接)

  2.
UDP(用户数据报协议)—>不树立连接 (如:广播用者,不断的发送数据包)

    ☞ 将 数据 及 源 和 目的 封装成数据包中,不需树立连接

    ☞ 每个数据报的深浅限制以64KB之内

    ☞ 因为无需连续,因此是不可靠协议

      举例:看教师广播讲课,网络卡主了,再观看的是流行的视频内容,不能够跟着看,可能失去了片情节。

    ☞ 不需要树立连接,速度快 (省掉了三不行握手操作)

其三、传输协议 TCP/UDP

  TCP和UDP:数据传的一定量栽艺术,即把数据从平端传至任何一样端的个别种方法

  1.
TCP(传输控制协议)
—>要树连接(如:发送HTTP请求,客户端向服务端发送网络要)

☞ 建立连接,形成传输数据的大路

☞ 于连年着展开大数据传(数据大小不吃限制)

☞ 通过三赖握手完成连接,是保险协议,安全送达

        说明:在起通信连接(打通管道)之前有三不善握手,目的是为多少的安全性以及可靠性(让数据安全可靠的传导到对方)。

        举例:打电话 (理解三次等握手)

首先次于握手:拿起电话,进行拨号。这个拨号的经过叫第一差握手。【开始备连接】

老二浅握手:拨通了,对方”喂”了同等名(响应了平等声),我闻了,称为第二赖握手。【说明自己一连而 没问题】

其三不良握手:我听见了对方”喂”了同名(响应了同声),我哉习惯性的”喂”了同一望,对方听到了。【说明你连我 没问题】

万一立刻三独过程还未曾问题,就可确定通话连接起成。

    ☞ 必须树立连接,效率会聊小。(每次要都设白手起家连接)

  2.
UDP(用户数量报协议)—>不成立连接 (如:广播用此,不断的发送数据包)

    ☞ 将 数据 及 源 和 目的 封装成数据包中,不需树立连接

    ☞ 每个数据报的大大小小限制于64KB之内

    ☞ 因为不论需连续,因此是不可靠协议

      举例:看师资广播讲课,网络卡主了,再见到的凡新型的视频内容,不克随着看,可能夺了有些内容。

    ☞ 不需要建立连接,速度快 (省掉了三次握手操作)

季、Socket 通信流程图

图片 3

☞ bind():绑定端口 (80、3306)

☞ listen():监听端口(服务器监听客户端起没有发出连续到者端口来)

☞ accept():如果发生连续至这端口,就接这连续。(通信管道挖,接下去就是足以传输数据了)

☞ write():发请求/写请求/发数据

☞ read():读请求/读数据

  • HTTP底层就是Socket通信,通过Socket建立连接(通信管道),实现多少传,连接的计(数据传的措施)是TCP。
  • HTTP是一个TCP的导协议(方式),它是一个保险、安全之商事。

季、Socket 通信流程图

图片 4

☞ bind():绑定端口 (80、3306)

☞ listen():监听端口(服务器监听客户端有没有发连续到之端口来)

☞ accept():如果生连续至这端口,就接这连续。(通信管道挖,接下就是可传输数据了)

☞ write():发请求/写请求/发数据

☞ read():读请求/读数据

  • HTTP底层就是Socket通信,通过Socket建立连接(通信管道),实现数量传,连接的不二法门(数据传的方式)是TCP。
  • HTTP是一个TCP的传导协议(方式),它是一个保险、安全之说道。

五、体验 Socket

  实现Socket服务端监听:

1)使用C语言实现。

2)使用 CocoaAsyncSocket 第三方框架(OC),内部是针对性C的包装。

    telnet命令:是接二连三服务器上的某个端口对应之服务。

    telnet命令:telnet
host port 

      如:telnet www.baidu.com 80  (IP地址及域名一样,都能找到主机。)

五、体验 Socket

  实现Socket服务端监听:

1)使用C语言实现。

2)使用 CocoaAsyncSocket 第三正在框架(OC),内部是对准C的卷入。

    telnet命令:是连续服务器上之某部端口对应的服务。

    telnet命令:telnet
host port 

      如:telnet www.baidu.com 80  (IP地址及域名一样,都能够找到主机。)

1. 【案例】写单10086劳务,体验客户端与劳动端的Socket通信


自己写一个服务端,用极代替客户端来演示

☞ 掌握:通过Socket对象在服务器里怎么去接收数据和归数据。

图片 5

图片 6

图片 7图片 8

/// ----- MyServiceListener.h -----
@interface MyServiceListener : NSObject
//开启服务
- (void)start;
@end

/// ----- MyServiceListener.m -----
#import "MyServiceListener.h"
#import "GCDAsyncSocket.h"
/**
 *  服务的监听者(服务端监听客户端连接)
 */
@interface MyServiceListener()<GCDAsyncSocketDelegate>
/** 保存服务端的Socket对象 */
@property (nonatomic, strong) GCDAsyncSocket *serviceSocket;
/** 保存客户端的所有Socket对象 */
@property (nonatomic, strong) NSMutableArray *clientSocketArr;

@end

@implementation MyServiceListener
- (GCDAsyncSocket *)serviceSocket {
    if (!_serviceSocket) {
        //1.创建一个Socket对象
                //serviceSocket 服务端的Socket只监听 有没有客户端请求连接
               //队列:代理的方法在哪个队列里调用 (子线程的队列)
        _serviceSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    return _serviceSocket;
}

- (NSMutableArray *)clientSocketArr {
    if(!_clientSocketArr) {
        _clientSocketArr = [NSMutableArray array];
    }
    return _clientSocketArr;
}

- (void)start {
    //开启10086服务:5288
    //2.绑定端口 + 开启监听
    NSError *error = nil;
    //框架里的这个方法做了两件事情:绑定端口和开启监听
    [self.serviceSocket acceptOnPort:5288 error:&error];
    if (!error) {
        NSLog(@"10086服务开启成功!");
    } else {
        //失败的原因是端口被其它程序占用
        NSLog(@"10086服务开启失败:%@", error);
    }    
}

#pragma mark -- 实现代理的方法 如果有客户端的Socket连接到服务器,就会调用这个方法。
- (void)socket:(GCDAsyncSocket *)serviceSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket {
    static NSInteger index = 1;
    NSLog(@"客户端【%ld】已连接到服务器!", index++);
    //1.保存客户端的Socket(客户端的Socket被释放了,连接就会关闭)
    [self.clientSockets addObject:clientSocket];

    //提供服务(客户端一连接到服务器,就打印下面的内容)
    NSMutableString *serviceStr = [[NSMutableString alloc]init];
    [serviceStr appendString:@"========欢迎来到10086在线服务========\n"];
    [serviceStr appendString:@"请输入下面的数字选择服务...\n"];
    [serviceStr appendString:@" [0] 在线充值\n"];
    [serviceStr appendString:@" [1] 在线投诉\n"];
    [serviceStr appendString:@" [2] 优惠信息\n"];
    [serviceStr appendString:@" [3] special services\n"];
    [serviceStr appendString:@" [4] 退出\n"];
    [serviceStr appendString:@"=====================================\n"];
    // 服务端给客户端发送数据
    [clientSocket writeData:[serviceStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

    //2.监听客户端有没有数据上传 (参数1:超时时间,-1代表不超时)
    /**
     *  timeout: 超时时间,-1 代表不超时
     *  tag:标识作用,现在不用就写0
     */
    [clientSocket readDataWithTimeout:-1 tag:0];
}

#pragma mark -- 服务器端 读取 客户端请求(发送)的数据。在服务端接收客户端数据,这个方法会被调用
- (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag {
    //1.获取客户端发送的数据
    NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSInteger index = [self.clientSocketArr indexOfObject:clientSocket];
    NSLog(@"接收到客户端【%ld】发送的数据:%@", index + 1, str);
    //把字符串转成数字
    NSInteger num = [str integerValue];
    NSString *responseStr = nil;
    //服务器对应的处理的结果
    switch (num) {
        case 0:
            responseStr = @"在线充值服务暂停中...\n";
            break;
        case 1:
            responseStr = @"在线投诉服务暂停中...\n";
            break;
        case 2:
            responseStr = @"优惠信息没有\n";
            break;
        case 3:
            responseStr = @"没有特殊服务\n";
            break;
        case 4:
            responseStr = @"恭喜你退出成功!\n";
            break;
        default:
            break;
    }

    //2.服务端处理请求,返回数据(data)给客户端
    [clientSocket writeData:[responseStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    //写完数据后 判断
    if (num == 4) {
        //移除客户端,就会关闭连接
        [self.clientSockets removeObject:clientSocket];
    }

    //由于框架内部的实现,每次读完数据后,都要调用一次监听数据的方法(保证能接收到客户端第二次上传的数据)
    [clientSocket readDataWithTimeout:-1 tag:0];

}
@end

/// ----- ViewController.m -----
#import "ViewController.h"
#import "MyServiceListener.h"
@interface ViewController ()

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //1.创建一个服务监听对象
    MyServiceListener *listener = [[MyServiceListener alloc]init];
    //2.开始监听
    [listener start];
    //3.开启主运行循环,让服务不能停(服务器一般要永久开启)
    [[NSRunLoop mainRunLoop] run];

}
@end


体验Socket通信-服务端简单实现代码:

Demo下载地址:https://github.com/borenfocus/Socket10086ServerDemo 

 

1. 【案例】写个10086劳动,体验客户端和劳动端的Socket通信


自己写一个服务端,用极端代替客户端来演示

☞ 掌握:通过Socket对象在服务器里怎么去接收数据和归数据。

图片 9

图片 10

图片 11图片 12

/// ----- MyServiceListener.h -----
@interface MyServiceListener : NSObject
//开启服务
- (void)start;
@end

/// ----- MyServiceListener.m -----
#import "MyServiceListener.h"
#import "GCDAsyncSocket.h"
/**
 *  服务的监听者(服务端监听客户端连接)
 */
@interface MyServiceListener()<GCDAsyncSocketDelegate>
/** 保存服务端的Socket对象 */
@property (nonatomic, strong) GCDAsyncSocket *serviceSocket;
/** 保存客户端的所有Socket对象 */
@property (nonatomic, strong) NSMutableArray *clientSocketArr;

@end

@implementation MyServiceListener
- (GCDAsyncSocket *)serviceSocket {
    if (!_serviceSocket) {
        //1.创建一个Socket对象
                //serviceSocket 服务端的Socket只监听 有没有客户端请求连接
               //队列:代理的方法在哪个队列里调用 (子线程的队列)
        _serviceSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    return _serviceSocket;
}

- (NSMutableArray *)clientSocketArr {
    if(!_clientSocketArr) {
        _clientSocketArr = [NSMutableArray array];
    }
    return _clientSocketArr;
}

- (void)start {
    //开启10086服务:5288
    //2.绑定端口 + 开启监听
    NSError *error = nil;
    //框架里的这个方法做了两件事情:绑定端口和开启监听
    [self.serviceSocket acceptOnPort:5288 error:&error];
    if (!error) {
        NSLog(@"10086服务开启成功!");
    } else {
        //失败的原因是端口被其它程序占用
        NSLog(@"10086服务开启失败:%@", error);
    }    
}

#pragma mark -- 实现代理的方法 如果有客户端的Socket连接到服务器,就会调用这个方法。
- (void)socket:(GCDAsyncSocket *)serviceSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket {
    static NSInteger index = 1;
    NSLog(@"客户端【%ld】已连接到服务器!", index++);
    //1.保存客户端的Socket(客户端的Socket被释放了,连接就会关闭)
    [self.clientSockets addObject:clientSocket];

    //提供服务(客户端一连接到服务器,就打印下面的内容)
    NSMutableString *serviceStr = [[NSMutableString alloc]init];
    [serviceStr appendString:@"========欢迎来到10086在线服务========\n"];
    [serviceStr appendString:@"请输入下面的数字选择服务...\n"];
    [serviceStr appendString:@" [0] 在线充值\n"];
    [serviceStr appendString:@" [1] 在线投诉\n"];
    [serviceStr appendString:@" [2] 优惠信息\n"];
    [serviceStr appendString:@" [3] special services\n"];
    [serviceStr appendString:@" [4] 退出\n"];
    [serviceStr appendString:@"=====================================\n"];
    // 服务端给客户端发送数据
    [clientSocket writeData:[serviceStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

    //2.监听客户端有没有数据上传 (参数1:超时时间,-1代表不超时)
    /**
     *  timeout: 超时时间,-1 代表不超时
     *  tag:标识作用,现在不用就写0
     */
    [clientSocket readDataWithTimeout:-1 tag:0];
}

#pragma mark -- 服务器端 读取 客户端请求(发送)的数据。在服务端接收客户端数据,这个方法会被调用
- (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag {
    //1.获取客户端发送的数据
    NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSInteger index = [self.clientSocketArr indexOfObject:clientSocket];
    NSLog(@"接收到客户端【%ld】发送的数据:%@", index + 1, str);
    //把字符串转成数字
    NSInteger num = [str integerValue];
    NSString *responseStr = nil;
    //服务器对应的处理的结果
    switch (num) {
        case 0:
            responseStr = @"在线充值服务暂停中...\n";
            break;
        case 1:
            responseStr = @"在线投诉服务暂停中...\n";
            break;
        case 2:
            responseStr = @"优惠信息没有\n";
            break;
        case 3:
            responseStr = @"没有特殊服务\n";
            break;
        case 4:
            responseStr = @"恭喜你退出成功!\n";
            break;
        default:
            break;
    }

    //2.服务端处理请求,返回数据(data)给客户端
    [clientSocket writeData:[responseStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    //写完数据后 判断
    if (num == 4) {
        //移除客户端,就会关闭连接
        [self.clientSockets removeObject:clientSocket];
    }

    //由于框架内部的实现,每次读完数据后,都要调用一次监听数据的方法(保证能接收到客户端第二次上传的数据)
    [clientSocket readDataWithTimeout:-1 tag:0];

}
@end

/// ----- ViewController.m -----
#import "ViewController.h"
#import "MyServiceListener.h"
@interface ViewController ()

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //1.创建一个服务监听对象
    MyServiceListener *listener = [[MyServiceListener alloc]init];
    //2.开始监听
    [listener start];
    //3.开启主运行循环,让服务不能停(服务器一般要永久开启)
    [[NSRunLoop mainRunLoop] run];

}
@end


体验Socket通信-服务端简单实现代码:

Demo下载地址:https://github.com/borenfocus/Socket10086ServerDemo 

 

2. 【案例扩展】写个换车信息服务(群聊服务端)

  • 基本上独客户端连接到服务器。
  • 当一个客户端发送信息让服务器时,服务器转发给其它都连续的客户端。
  • 相当给一个群聊的雏形。

  图片 13

图片 14图片 15

/// MyService.h
#import <Foundation/Foundation.h>

@interface MyService : NSObject
/** 开启服务 */
- (void)startService;

@end

/// MyService.m
#import "MyService.h"
#import "GCDAsyncSocket.h"

@interface MyService ()<GCDAsyncSocketDelegate>
/** 保存服务端的Socket对象 */
@property (nonatomic, strong) GCDAsyncSocket *serviceSocket;
/** 保存客户端的所有Socket对象 */
@property (nonatomic, strong) NSMutableArray *clientSocketArr;

@end

@implementation MyService

//开启10086服务:5288
- (void)startService {
    NSError *error = nil;
    // 绑定端口 + 开启监听
    [self.serviceSocket acceptOnPort:5288 error:&error];
    if (!error) {
        NSLog(@"服务开启成功!");
    } else {
        NSLog(@"服务开启失败!");
    }
}

#pragma mark -- 实现代理的方法 如果有客户端的Socket连接到服务器,就会调用这个方法。
- (void)socket:(GCDAsyncSocket *)serviceSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket {
    // 客户端的端口号是系统分配的,服务端的端口号是我们自己分配的
    NSLog(@"客户端【Host:%@, Port:%d】已连接到服务器!", clientSocket.connectedHost, clientSocket.connectedPort);
    //1.保存客户端的Socket(客户端的Socket被释放了,连接就会关闭)
    [self.clientSocketArr addObject:clientSocket];

    //2.监听客户端有没有数据上传 (参数1:超时时间,-1代表不超时;参数2:标识作用,现在不用就写0)
    [clientSocket readDataWithTimeout:-1 tag:0];
}

#pragma mark -- 服务器端 读取 客户端请求(发送)的数据。在服务端接收客户端数据,这个方法会被调用
- (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag {
    //1.获取客户端发送的数据
    NSString *messageStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到客户端【Host:%@, Port:%d】发送的数据:%@",  clientSocket.connectedHost, clientSocket.connectedPort, messageStr);
    // 遍历客户端数组
    for (GCDAsyncSocket *socket in self.clientSocketArr) {
        if (socket != clientSocket) { // 不转发给自己
            //2.服务端把收到的消息转发给其它客户端
            [socket writeData:data withTimeout:-1 tag:0];
        }
    }
    //由于框架内部的实现,每次读完数据后,都要调用一次监听数据的方法(保证能接收到客户端第二次上传的数据)
    [clientSocket readDataWithTimeout:-1 tag:0];
}

- (GCDAsyncSocket *)serviceSocket {
    if (!_serviceSocket) {
        // 1.创建一个Socket对象
        // serviceSocket 服务端的Socket只监听 有没有客户端请求连接
        // 队列:代理的方法在哪个队列里调用 (子线程的队列)
        _serviceSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    return _serviceSocket;
}

- (NSMutableArray *)clientSocketArr {
    if (!_clientSocketArr) {
        _clientSocketArr = [[NSMutableArray alloc]init];
    }
    return _clientSocketArr;
}

@end


/// main.m
#import <Foundation/Foundation.h>
#import "MyService.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //1.创建一个服务监听对象
        MyService *service = [[MyService alloc]init];
        //2.开始监听
        [service startService];
        //3.开启主运行循环,让服务不能停(服务器一般要永久开启)
        [[NSRunLoop mainRunLoop] run];
    }
    return 0;
}


体验Socket通信-群聊服务端实现代码:

 Demo下载地址:https://github.com/borenfocus/SocketGroupServerDemo

2. 【案例扩展】写个换车信息服务(群聊服务端)

  • 基本上单客户端连接到服务器。
  • 当一个客户端发送信息于服务器时,服务器转发给任何都连续的客户端。
  • 相当给一个群聊的雏形。

  图片 16

图片 17图片 18

/// MyService.h
#import <Foundation/Foundation.h>

@interface MyService : NSObject
/** 开启服务 */
- (void)startService;

@end

/// MyService.m
#import "MyService.h"
#import "GCDAsyncSocket.h"

@interface MyService ()<GCDAsyncSocketDelegate>
/** 保存服务端的Socket对象 */
@property (nonatomic, strong) GCDAsyncSocket *serviceSocket;
/** 保存客户端的所有Socket对象 */
@property (nonatomic, strong) NSMutableArray *clientSocketArr;

@end

@implementation MyService

//开启10086服务:5288
- (void)startService {
    NSError *error = nil;
    // 绑定端口 + 开启监听
    [self.serviceSocket acceptOnPort:5288 error:&error];
    if (!error) {
        NSLog(@"服务开启成功!");
    } else {
        NSLog(@"服务开启失败!");
    }
}

#pragma mark -- 实现代理的方法 如果有客户端的Socket连接到服务器,就会调用这个方法。
- (void)socket:(GCDAsyncSocket *)serviceSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket {
    // 客户端的端口号是系统分配的,服务端的端口号是我们自己分配的
    NSLog(@"客户端【Host:%@, Port:%d】已连接到服务器!", clientSocket.connectedHost, clientSocket.connectedPort);
    //1.保存客户端的Socket(客户端的Socket被释放了,连接就会关闭)
    [self.clientSocketArr addObject:clientSocket];

    //2.监听客户端有没有数据上传 (参数1:超时时间,-1代表不超时;参数2:标识作用,现在不用就写0)
    [clientSocket readDataWithTimeout:-1 tag:0];
}

#pragma mark -- 服务器端 读取 客户端请求(发送)的数据。在服务端接收客户端数据,这个方法会被调用
- (void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag {
    //1.获取客户端发送的数据
    NSString *messageStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到客户端【Host:%@, Port:%d】发送的数据:%@",  clientSocket.connectedHost, clientSocket.connectedPort, messageStr);
    // 遍历客户端数组
    for (GCDAsyncSocket *socket in self.clientSocketArr) {
        if (socket != clientSocket) { // 不转发给自己
            //2.服务端把收到的消息转发给其它客户端
            [socket writeData:data withTimeout:-1 tag:0];
        }
    }
    //由于框架内部的实现,每次读完数据后,都要调用一次监听数据的方法(保证能接收到客户端第二次上传的数据)
    [clientSocket readDataWithTimeout:-1 tag:0];
}

- (GCDAsyncSocket *)serviceSocket {
    if (!_serviceSocket) {
        // 1.创建一个Socket对象
        // serviceSocket 服务端的Socket只监听 有没有客户端请求连接
        // 队列:代理的方法在哪个队列里调用 (子线程的队列)
        _serviceSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    return _serviceSocket;
}

- (NSMutableArray *)clientSocketArr {
    if (!_clientSocketArr) {
        _clientSocketArr = [[NSMutableArray alloc]init];
    }
    return _clientSocketArr;
}

@end


/// main.m
#import <Foundation/Foundation.h>
#import "MyService.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //1.创建一个服务监听对象
        MyService *service = [[MyService alloc]init];
        //2.开始监听
        [service startService];
        //3.开启主运行循环,让服务不能停(服务器一般要永久开启)
        [[NSRunLoop mainRunLoop] run];
    }
    return 0;
}


体验Socket通信-群聊服务端实现代码:

 Demo下载地址:https://github.com/borenfocus/SocketGroupServerDemo

3. 【案例】体验Socket通信-群聊客户端实现

  图片 19

  图片 20

图片 21图片 22

///  ViewController.m
#import "ViewController.h"
#import "GCDAsyncSocket.h"

@interface ViewController ()<UITableViewDataSource, GCDAsyncSocketDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UITextField *textField;
@property (nonatomic, strong) GCDAsyncSocket *clientSocket;

@property (nonatomic, strong) NSMutableArray *dataArr;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 实现聊天室
    // 1. 连接到服务器
    NSError *error = nil;
    [self.clientSocket connectToHost:@"192.168.1.95" onPort:5288 error:&error];
    if (error) {
        NSLog(@"error:%@", error);
    }
}

#pragma mark - GCDAsyncSocketDelegate
- (void)socket:(GCDAsyncSocket *)clientSock didConnectToHost:(NSString *)host port:(uint16_t)port {
    NSLog(@"与服务器连接成功!");
    // 监听读取数据(在读数据的时候,要监听有没有数据可读,目的是保证数据读取到)
    [clientSock readDataWithTimeout:-1 tag:0];
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    NSLog(@"与服务器断开连接:%@", err);
}

// 读取数据(接收消息)
- (void)socket:(GCDAsyncSocket *)clientSock didReadData:(NSData *)data withTag:(long)tag {
    NSString *messageStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到消息:%@", messageStr);
    messageStr = [NSString stringWithFormat:@"【匿名】:%@", messageStr];
    [self.dataArr addObject:messageStr];
    // 刷新UI要在主线程
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.tableView reloadData];
    });

    // 监听读取数据(读完数据后,继续监听有没有数据可读,目的是保证下一次数据可以读取到)
    [clientSock readDataWithTimeout:-1 tag:0];
}

#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataArr.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    cell.textLabel.text = self.dataArr[indexPath.row];
    return cell;
}

- (IBAction)clickSenderBtn:(UIButton *)sender {
    NSLog(@"发送消息");
    [self.view endEditing:YES];
    NSString *senderStr = self.textField.text;
    if (senderStr.length == 0) {
        return;
    }
    // 发送数据
    [self.clientSocket writeData:[senderStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

    senderStr = [NSString stringWithFormat:@"【我】:%@", senderStr];
    [self.dataArr addObject:senderStr];
    [self.tableView reloadData];
}

- (GCDAsyncSocket *)clientSocket {
    if (!_clientSocket) {
        _clientSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    return _clientSocket;
}

- (NSMutableArray *)dataArr {
    if (!_dataArr) {
        _dataArr = [[NSMutableArray alloc]init];
    }
    return _dataArr;
}

@end


体验Socket通信-群聊客户端实现:

Demo下载地址:https://github.com/borenfocus/SocketGroupClientDemo  

3. 【案例】体验Socket通信-群聊客户端实现

  图片 23

  图片 24

图片 25图片 26

///  ViewController.m
#import "ViewController.h"
#import "GCDAsyncSocket.h"

@interface ViewController ()<UITableViewDataSource, GCDAsyncSocketDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (weak, nonatomic) IBOutlet UITextField *textField;
@property (nonatomic, strong) GCDAsyncSocket *clientSocket;

@property (nonatomic, strong) NSMutableArray *dataArr;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 实现聊天室
    // 1. 连接到服务器
    NSError *error = nil;
    [self.clientSocket connectToHost:@"192.168.1.95" onPort:5288 error:&error];
    if (error) {
        NSLog(@"error:%@", error);
    }
}

#pragma mark - GCDAsyncSocketDelegate
- (void)socket:(GCDAsyncSocket *)clientSock didConnectToHost:(NSString *)host port:(uint16_t)port {
    NSLog(@"与服务器连接成功!");
    // 监听读取数据(在读数据的时候,要监听有没有数据可读,目的是保证数据读取到)
    [clientSock readDataWithTimeout:-1 tag:0];
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    NSLog(@"与服务器断开连接:%@", err);
}

// 读取数据(接收消息)
- (void)socket:(GCDAsyncSocket *)clientSock didReadData:(NSData *)data withTag:(long)tag {
    NSString *messageStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"接收到消息:%@", messageStr);
    messageStr = [NSString stringWithFormat:@"【匿名】:%@", messageStr];
    [self.dataArr addObject:messageStr];
    // 刷新UI要在主线程
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.tableView reloadData];
    });

    // 监听读取数据(读完数据后,继续监听有没有数据可读,目的是保证下一次数据可以读取到)
    [clientSock readDataWithTimeout:-1 tag:0];
}

#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataArr.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    cell.textLabel.text = self.dataArr[indexPath.row];
    return cell;
}

- (IBAction)clickSenderBtn:(UIButton *)sender {
    NSLog(@"发送消息");
    [self.view endEditing:YES];
    NSString *senderStr = self.textField.text;
    if (senderStr.length == 0) {
        return;
    }
    // 发送数据
    [self.clientSocket writeData:[senderStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];

    senderStr = [NSString stringWithFormat:@"【我】:%@", senderStr];
    [self.dataArr addObject:senderStr];
    [self.tableView reloadData];
}

- (GCDAsyncSocket *)clientSocket {
    if (!_clientSocket) {
        _clientSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    return _clientSocket;
}

- (NSMutableArray *)dataArr {
    if (!_dataArr) {
        _dataArr = [[NSMutableArray alloc]init];
    }
    return _dataArr;
}

@end


体验Socket通信-群聊客户端实现:

Demo下载地址:https://github.com/borenfocus/SocketGroupClientDemo  

六、长连接和短连接

  长连接和短连接:是接连的一个封存状态(保存时),长连接就是丰富日子连,短连接就是不够日总是。

  • http网络要是紧缺连接。
  • 长连接用在马上通信(实时聊天,要随时随地的发送信息,考虑到性,用长连)

六、长连接和短连接

  长连接和短连接:是连的一个保留状态(保存时),长连接就是丰富日子连,短连接就是差日总是。

  • http网络要是不够连接。
  • 长连接用在及时通信(实时聊天,要随时随地的发送信息,考虑到性,用添加连)

七、Socket 层上的情商

  Socket层及的商议:指的数额传的格式。

  1. HTTP共谋:定义在网及数据传的一模一样栽格式。

    传输格式:假设:这是一旦,实际http的格式不是这般的。

    http1.1,content-type:multipart/form-data,content-length:188,body:username=zhangsan&password=123456

  2.
XMPP协议:是同一缓慢即时通讯协议 (别人定义好的商议,我们经常拿来为此)

    是依据可扩大标记语言(XML)的说道,它用于即时消息(IM)以及在线现场探测。

    传输格式:

      <from>zhangsan<from>

      <to>lisi<to>

      <body>一起吃晚上</body>

  3.
由定义即时通讯协议,json格式。

    {

      ”from”:
“zhangsan”,

      ”to”:
“lisi”,

      ”body”:
“中午同用餐”,

    }

  你做呀操作,必须要来一个原则性的格式,这样服务器才晓得您要举行呀。

  

  举例:写一封闭信于北京市知音(区别 TCP/UDP 与 HTTP/XMMP)

  • 多少传的不二法门:TCP/UDP —》相当给 EMS/顺丰/申通/中通   

  • 数量传的格式:HTTP/XMMP —》相当给 信的情节格式 (可以是汉语/英文/…等)

 

七、Socket 层上之协商

  Socket层及之磋商:指的数目传的格式。

  1. HTTP商事:定义在网达到数传的等同种格式。

    传输格式:假设:这是如果,实际http的格式不是这样的。

    http1.1,content-type:multipart/form-data,content-length:188,body:username=zhangsan&password=123456

  2.
XMPP协议:是如出一辙放缓即时通讯协议 (别人定义好之情商,我们常以来所以)

    是冲可扩大标记语言(XML)的协议,它用来即时消息(IM)以及在线现场探测。

    传输格式:

      <from>zhangsan<from>

      <to>lisi<to>

      <body>一起吃晚上</body>

  3.
打定义即时通讯协议,json格式。

    {

      ”from”:
“zhangsan”,

      ”to”:
“lisi”,

      ”body”:
“中午联手进餐”,

    }

  你开啊操作,必须使出一个稳定的格式,这样服务器才亮你要是召开什么。

  

  举例:写一封信给北京市知音(区别 TCP/UDP 与 HTTP/XMMP)

  • 数据传的主意:TCP/UDP —》相当给 EMS/顺丰/申通/中通   

  • 数码传的格式:HTTP/XMMP —》相当给 信的情格式 (可以是汉语/英文/…等)

 

相关文章