IOS框架使用:GCDAsySocket

原创:知识进阶型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 一、使用Socket建立即时通讯
    • 1、创建socket建立连接
    • 2、发送消息
    • 3、接收数据
    • 4、对接收信息和发送信息进行格式处理
    • 5、即时通讯Demo运行效果
  • 二、使用GCDAsySocket建立即时通讯
    • 1、导入框架和创建属性
    • 2、点击按钮触发的事件
    • 3、GCDAsyncSocketDelegate
    • 4、演示运行效果
  • 三、粘包拆包
    • 1、点击按钮触发的事件
    • 2、发送数据格式化
    • 3、发送心跳
    • 4、重连机制
    • 5、演示运行效果
  • 四、UDP画板功能应用
    • 1、点击按钮触发事件
    • 2、接受数据的回调
    • 3、进行绘图将点位信息发送给服务器
  • Demo
  • 参考文献

一、使用Socket建立即时通讯

Socket是双工+开关(A与B可以相互发送信息)可以主动发送请求,支持即使通讯。

1、创建socket建立连接

a、创建socket
  • domain:协议域。决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合,而AF_UNIX决定了要用一个绝对路径名作为地址。
  • type:指定Socket类型。流式SocketSOCK_STREAM)是一种面向连接的Socket,针对于面向连接的TCP服务应用。数据报式SocketSOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDP服务应用。
  • protocol:指定协议。常用协议有IPPROTO_TCPIPPROTO_UDP,分别对应TCP传输协议、UDP传输协议。typeprotocol不可以随意组合,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当第三个参数为0时,会自动选择第二个参数类型对应的默认协议。
  • 返回值:如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET
int socketID = socket(AF_INET, SOCK_STREAM, 0);
self.clinenId = socketID;
if (socketID == -1)
{
    NSLog(@"创建socket失败");
    return;
}

b、创建套接字地址
  • htons:将一个无符号短整型的主机数值转换为网络字节顺序 (big-endian大尾顺序、little-endian小尾顺序)
  • inet_addr:是一个计算机函数,功能是将一个点分十进制的IP转换成一个长整数型数
#define SocketPort htons(8040)
#define SocketIP   inet_addr("127.0.0.1")
struct sockaddr_in socketAddr;
socketAddr.sin_family = AF_INET;// AF_INET(地址族)PF_INET(协议族)
socketAddr.sin_port   = SocketPort;// 端口
struct in_addr socketIn_addr;
socketIn_addr.s_addr  = SocketIP;// ip
socketAddr.sin_addr   = socketIn_addr;

c、建立连接
  • 参数一:套接字描述符
  • 参数二:指向数据结构sockaddr的指针,其中包括目的端口和IP地址
  • 参数三:参数二sockaddr的长度,可以通过sizeof(struct sockaddr)获得
  • 返回值:成功则返回0,失败返回非0,错误码GetLastError()
int result = connect(socketID, (const struct sockaddr *)&socketAddr, sizeof(socketAddr));
if (result != 0)
{
    NSLog(@"链接失败");
    return;
}
NSLog(@"链接成功");

d、开启服务器通过8040端口进行链接
// 端口
#define SocketPort htons(8040)
// IP地址
#define SocketIP   inet_addr("127.0.0.1")
// 开启服务器
2021-02-22 21:16:16.400957+0800 001---Socket初体验[53153:1499929] 创建socket 成功
2021-02-22 21:16:16.401220+0800 001---Socket初体验[53153:1499929] 绑定socket成功
2021-02-22 21:16:16.401554+0800 001---Socket初体验[53153:1499929] 监听成功
2021-02-22 21:16:20.000913+0800 001---Socket初体验[53153:1500736] 客户端 in,socket:6
// 未开启服务器链接失败
2021-02-22 21:12:46.299454+0800 001---Socket初体验[52927:1488177] 链接失败
// 开启服务器后链接成功
2021-02-22 21:16:20.000716+0800 001---Socket初体验[52927:1488177] 链接成功

e、在子线程异步接收消息
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self recvMessage];
});

2、发送消息

  • s:一个用于标识已连接套接口的描述字。
  • buf:包含待发送数据的缓冲区。
  • len:缓冲区中数据的长度。
  • flags:调用执行方式。
  • 返回值:如果成功则返回发送的字节数,失败则返回SOCKET_ERROR
- (void)sendMsgAction
{
    if (self.sendMsgContent_tf.text.length == 0)
    {
        return;
    }
    
    // 计算发送的消息长度
    const char *msg = self.sendMsgContent_tf.text.UTF8String;
    ssize_t sendLength = send(self.clinenId, msg, strlen(msg), 0);
    NSLog(@"发送 %ld 字节",sendLength);
    
    // 展示发送的消息
    [self showMsg:self.sendMsgContent_tf.text msgType:0];
    
    // 消息发送完成后清空文本框
    self.sendMsgContent_tf.text = @"";
}

3、接收数据

  • 参数一:客户端socket
  • 参数二:接收内容缓冲区地址
  • 参数三:接收内容缓存区长度
  • 参数四:接收方式,0表示阻塞,必须等待服务器返回数据
  • 返回值:如果成功,则返回读入的字节数,失败则返回SOCKET_ERROR
- (void)recvMessage
{
    // 1:通过循环的方式模拟长连接(如果不循环监听,那么发送一次消息后即断开链接)
    while (1)
    {
        uint8_t buffer[1024];
        ssize_t recvLength = recv(self.clinenId, buffer, sizeof(buffer), 0);
        
        if (recvLength == 0)
        {
            NSLog(@"接收到了0个字节");
            continue;
        }
        
        // buffer -> data -> string
        NSData *data = [NSData dataWithBytes:buffer length:recvLength];
        NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"当前线程为:%@,接收到的信息为:%@",[NSThread currentThread],string);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            // 作为接收到的消息进行展示
            [self showMessage:string MessageType:1];
            // 接收到消息后清空文本框
            self.sendMessageContentTextField.text = @"";
        });
    }
}

4、对接收信息和发送信息进行格式处理

- (void)showMessage:(NSString *)Message MessageType:(int)MessageType
{
    ...
}
a、将消息发送到时间添加到聊天框的文本中
NSMutableAttributedString *dateAttributedString = [[NSMutableAttributedString alloc] initWithString:showTimeStr];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = NSTextAlignmentCenter;// 段落对齐方式
[dateAttributedString addAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13],NSForegroundColorAttributeName:[UIColor blackColor],NSParagraphStyleAttributeName:paragraphStyle} range:NSMakeRange(0, showTimeStr.length)];

[self.totalAttributeString appendAttributedString:dateAttributedString];
[self.totalAttributeString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@"\n"]];
b、我发送的消息
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.headIndent = 20.f;
NSMutableAttributedString *attributedString;
if (MessageType == 0)// 我发送的消息
{
    attributedString = [[NSMutableAttributedString alloc] initWithString:Message];
    paragraphStyle.alignment = NSTextAlignmentRight;
    [attributedString addAttributes:@{
                                      NSFontAttributeName:[UIFont systemFontOfSize:15],
                                      NSForegroundColorAttributeName:[UIColor whiteColor],
                                      NSBackgroundColorAttributeName:[UIColor blueColor],
                                      NSParagraphStyleAttributeName:paragraphStyle
                                      }
                              range:NSMakeRange(0, Message.length)];
}
c、对方发送的消息
else// 对方发送的消息
{
    Message = [Message substringToIndex:Message.length - 1];
    attributedString = [[NSMutableAttributedString alloc] initWithString:Message];

    [attributedString addAttributes:@{
                                      NSFontAttributeName:[UIFont systemFontOfSize:15],
                                      NSForegroundColorAttributeName:[UIColor blackColor],
                                      NSBackgroundColorAttributeName:[UIColor whiteColor],
                                      NSParagraphStyleAttributeName:paragraphStyle
                                      }
                              range:NSMakeRange(0, Message.length)];
}
d、将发送的消息添加到聊天框的文本中
[self.totalAttributeString appendAttributedString:attributedString];
[self.totalAttributeString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@"\n"]];
e、将聊天框的文本放到聊天框中
self.allMessageContentTextView.attributedText = self.totalAttributeString;

5、即时通讯Demo运行效果

2021-02-25 14:05:47.431511+0800 SocketDemo[61539:2661249] 链接成功
2021-02-25 14:06:10.202964+0800 SocketDemo[61539:2679731] 当前线程为:<NSThread: 0x600003cd6980>{number = 8, name = (null)},接收到的信息为:Hello Boy
2021-02-25 14:06:19.910566+0800 SocketDemo[61539:2661249] 发送 10 字节
2021-02-25 14:06:19.912298+0800 SocketDemo[61539:2661249] 系统当前时间:2021-02-25 06:06:19 +0000,记录日期:2021-02-25 06:06:10 +0000,时间差:9.910700
2021-02-25 14:06:36.369588+0800 SocketDemo[61539:2679731] 当前线程为:<NSThread: 0x600003cd6980>{number = 8, name = (null)},接收到的信息为:I want to know you
2021-02-25 14:06:36.369950+0800 SocketDemo[61539:2661249] 系统当前时间:2021-02-25 06:06:36 +0000,记录日期:2021-02-25 06:06:19 +0000,时间差:17.369708
2021-02-25 14:06:55.629877+0800 SocketDemo[61539:2661249] 发送 17 字节
2021-02-25 14:06:55.630306+0800 SocketDemo[61539:2661249] 系统当前时间:2021-02-25 06:06:55 +0000,记录日期:2021-02-25 06:06:36 +0000,时间差:19.630005

二、使用GCDAsySocket建立即时通讯

1、导入框架和创建属性

#import <GCDAsyncSocket.h>

@interface ViewController ()<GCDAsyncSocketDelegate>

@property (weak, nonatomic) IBOutlet UITextField *contentTF;
@property (nonatomic, strong) GCDAsyncSocket *socket;

@end

2、点击按钮触发的事件

a、连接socket
- (IBAction)didClickConnectSocket:(id)sender
{
    // 创建socket
    if (self.socket == nil)
    {
        self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    
    // 连接socket
    if (!self.socket.isConnected)
    {
        NSError *error;
        [self.socket connectToHost:@"127.0.0.1" onPort:8090 withTimeout:-1 error:&error];
        if (error) NSLog(@"错误信息:%@",error);
    }
}
b、发送消息
- (IBAction)didClickSendAction:(id)sender
{
    NSData *data = [self.contentTF.text dataUsingEncoding:NSUTF8StringEncoding];
    [self.socket writeData:data withTimeout:-1 tag:10086];
}
c、关闭socket
- (IBAction)didClickCloseAction:(id)sender
{
    [self.socket disconnect];
    self.socket = nil;
}
d、重连socket
- (IBAction)didClickReconnectAction:(id)sender
{
    // 创建socket
    if (self.socket == nil)
    {
        self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
        
    // 连接socket
    if (!self.socket.isConnected)
    {
        NSError *error;
        [self.socket connectToHost:@"127.0.0.1" onPort:8090 withTimeout:-1 error:&error];
        if (error) NSLog(@"%@",error);
    }
}

3、GCDAsyncSocketDelegate

a、已经连接到服务器
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(nonnull NSString *)host port:(uint16_t)port
{
    NSLog(@"连接成功,主机:%@,端口:%d",host,port);
    [self.socket readDataWithTimeout:-1 tag:10086];// -1 表示长链接,保持链接状态
}
b、断开连接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    NSLog(@"断开socket连接,错误原因:%@",err);
}
c、已经接收到服务器返回来的数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSLog(@"接收到tag = %ld,长度 = %ld 的数据",tag,data.length);
    [self.socket readDataWithTimeout:-1 tag:10086];
}
d、成功向服务器发送消息
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"%ld 成功向服务器发送消息",tag);
}

4、演示运行效果

a、通过终端开启服务器建立链接
xiejiapei@xiejiapeis-iMac ~ % nc -lk 8090
2021-02-25 15:35:24.627974+0800 GCDAsySocketDemo[63622:2787577] 连接成功,主机:127.0.0.1,端口:8090
b、向服务端发送消息
2021-02-25 15:38:04.996879+0800 GCDAsySocketDemo[63622:2788493] 10086 成功向服务器发送消息
c、服务器向客户端发送消息
2021-02-25 15:45:03.072456+0800 GCDAsySocketDemo[63622:2796716] 接收到tag = 10086,长度 = 19 的数据

三、粘包拆包

1、点击按钮触发的事件

a、发送文本
- (IBAction)didClickSendTextAction:(UIButton *)sender
{
    NSData *data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
    
    unsigned int command = kcVideoDataType;
    [self sendData:data dataType:command];
}
b、发送图片
- (IBAction)didClickSendImageAction:(UIButton *)sender
{
    UIImage *image = [UIImage imageNamed:@"luckcoffee"];
    NSData  *imageData  = UIImagePNGRepresentation(image);

    unsigned int command = kcImageDataType;
    [self sendData:imageData dataType:command];
}
c、发送视频
- (IBAction)didClickSendVideoAction:(UIButton *)sender
{
    NSData  *videoData  = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"girl.mp4" ofType:nil]];
    
    unsigned int command = kcVideoDataType;
    [self sendData:videoData dataType:command];
}

2、发送数据格式化

a、拼接发送数据
- (void)sendData:(NSData *)data dataType:(unsigned int)dataType
{
    NSMutableData *mData = [NSMutableData data];
    
    // 拼接数据总长度
    unsigned int dataLength = 4+4+(int)data.length;
    NSData *lengthData = [NSData dataWithBytes:&dataLength length:4];
    [mData appendData:lengthData];
    
    // 拼接数据类型
    NSData *typeData = [NSData dataWithBytes:&dataType length:4];
    [mData appendData:typeData];
    
    // 最后拼接实际数据
    [mData appendData:data];
    
    NSLog(@"发送数据的总字节大小: %ld",mData.length);
    
    // 发送数据
    [self.socket writeData:mData withTimeout:-1 tag:10086];
}
b、解析服务器返回的数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSLog(@"接收到tag = %ld,长度 = %ld 的数据",tag,data.length);
    
    // 获取总的数据包大小
    NSData *totalSizeData = [data subdataWithRange:NSMakeRange(0, 4)];
    unsigned int totalSize = 0;
    [totalSizeData getBytes:&totalSize length:4];
    NSLog(@"响应总数据的大小 %u",totalSize);
    
    // 获取指令类型
    NSData *commandIdData = [data subdataWithRange:NSMakeRange(4, 4)];
    unsigned int commandId = 0;
    [commandIdData getBytes:&commandId length:4];
    
    // 获取数据上传结果
    NSData *resultData = [data subdataWithRange:NSMakeRange(8, 4)];
    unsigned int result = 0;
    [resultData getBytes:&result length:4];
    
    NSMutableString *str = [NSMutableString string];
    if (commandId == kcImageDataType)
    {
        [str appendString:@"图片 "];
    }
    
    if(result == 1)
    {
        [str appendString:@"上传成功"];
    }
    else
    {
        [str appendString:@"上传失败"];
    }
    NSLog(@"已经接收服务器返回来的数据:%@",str);
    
    [self.socket readDataWithTimeout:-1 tag:10086];
}

3、发送心跳

发送心跳保证客户端与服务器在同一个链接上,避免数据丢包。因为SocketTCP的长连接不同,只管发送消息不管服务端是否有能力接受消息,比如青楼今天客满了那么即使再来了客人也没有姑娘陪你。

a、设置心跳机制
- (void)setupHeartBeat
{
    dispatch_async(dispatch_get_main_queue(), ^{
        
        [self destoryHeartBeat];
        
        __weak typeof(self) weakSelf = self;
        self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:15 repeats:YES block:^(NSTimer * _Nonnull timer) {
            __weak typeof(self) strongSelf = weakSelf;

            NSData *heartData = [@"heartBeat" dataUsingEncoding:NSUTF8StringEncoding];
            [strongSelf.socket writeData:heartData withTimeout:-1 tag:10086];
            NSLog(@"heartBeat");
        }];
    });
}
b、销毁心跳机制
- (void)destoryHeartBeat
{
    dispatch_main_async_safe(^{
        if (self.heartTimer && [self.heartTimer respondsToSelector:@selector(isValid)] && [self.heartTimer isValid])
        {
            [self.heartTimer invalidate];
            self.heartTimer = nil;
        }
    });
}

4、重连机制

a、重连Socket
- (void)reconnectSocket
{
    // 1、关闭socket
    [self disconnectSocket];
    
    // 2.1 超时判断
    if (self.reconnectTime > 64)
    {
        NSLog(@"网络超时,不再重连");
        return;
    }
    
    // 2.2 延时等待重连
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.reconnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self connectSocketOrCreate];
    });
    
    // 3、超时时长处理
    if (self.reconnectTime == 0)
    {
        self.reconnectTime = 2;
    }
    else
    {
        // 2^5 = 64(重连次数)
        self.reconnectTime *= 2;
    }
}
b、关闭socket
- (void)disconnectSocket
{
    if (self.socket)
    {
        [self.socket disconnect];
        self.socket.delegate = nil;
        self.socket = nil;
        [self destoryHeartBeat];
    }
}
c、断开连接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    NSLog(@"断开socket连接,错误原因:%@",err);
    
    // 进行重连
    [self reconnectSocket];
}

5、演示运行效果

a、客户端与服务器建立链接
开启服务器
2021-02-26 10:00:03.218274+0800 003---粘包拆包[73735:3331386] 服务器socket开启成功
2021-02-26 10:01:20.478809+0800 003---粘包拆包[73735:3331573] 当前客户端的IP:127.0.0.1 端口号57491
2021-02-26 10:01:20.478968+0800 003---粘包拆包[73735:3331573] 当前有1个客户端连接
客户端请求链接
2021-02-26 10:01:20.478908+0800 粘包拆包[74007:3345722] 连接成功,主机:127.0.0.1,端口:8060

b、客户端向服务器发送文本信息
客户端发送文本信息
2021-02-26 10:12:37.490970+0800 粘包拆包[74161:3357535] 发送数据的总字节大小: 13
2021-02-26 10:12:37.491273+0800 粘包拆包[74161:3357683] 10086 成功向服务器发送消息
服务器接收到客户端发来的文本
2021-02-26 10:12:37.492464+0800 粘包拆包服务器[74145:3357277] 服务器接收到客户端发来的文本:hello

c、客户端向服务器发送图片信息
客户端发送图片信息
2021-02-26 10:14:39.701774+0800 粘包拆包[74161:3357535] 发送数据的总字节大小: 2019792
2021-02-26 10:14:39.803917+0800 粘包拆包[74161:3359425] 10086 成功向服务器发送消息
服务器接收到客户端发来的图片信息
2021-02-26 10:14:39.702292+0800 003---粘包拆包[74145:3357277] 接收到tag = 10010 : 244980 长度的数据
2021-02-26 10:14:39.702425+0800 003---粘包拆包[74145:3357277] 接收总数据的大小 2019792
2021-02-26 10:14:39.702735+0800 003---粘包拆包[74145:3357277] 此次接收的数据包大小 244980
...
2021-02-26 10:14:39.806013+0800 003---粘包拆包[74145:3359429] 此次接收的数据包大小 237316
2021-02-26 10:14:39.806093+0800 003---粘包拆包[74145:3359429] 数据已经接收完成
2021-02-26 10:16:10.002145+0800 003---粘包拆包[74145:3357024] 保存图片成功

d、客户端向服务器发送视频信息
客户端发送视频信息
2021-02-26 10:14:39.803917+0800 粘包拆包[74161:3359425] 10086 成功向服务器发送消息
2021-02-26 10:17:31.754655+0800 粘包拆包[74161:3357535] 发送数据的总字节大小: 3887273
服务器接收到客户端发来的视频信息
2021-02-26 10:19:03.438611+0800 003---粘包拆包[74145:3361181] 接收到tag = 10010 : 195984 长度的数据
2021-02-26 10:19:03.438726+0800 003---粘包拆包[74145:3361181] 接收总数据的大小 3887273
2021-02-26 10:19:03.438872+0800 003---粘包拆包[74145:3361181] 此次接收的数据包大小 195984
...
2021-02-26 10:19:03.447098+0800 003---粘包拆包[74145:3361180] 此次接收的数据包大小 944937
2021-02-26 10:19:03.447222+0800 003---粘包拆包[74145:3361180] 数据已经接收完成
2021-02-26 10:19:03.457837+0800 003---粘包拆包[74145:3361180] 写入视频状态: 1 路径: /Users/xiejiapei/Library/Developer/CoreSimulator/Devices/5BC32A40-EDB6-4954-A93D-DE1741EFFB53/data/Containers/Data/Application/5CD7D2AA-2241-4E41-884A-388E089CE077/Documents/netVideo.mp4

e、服务器向客户端发送图片信息
服务器发送图片信息
2021-02-26 10:42:27.557118+0800 003---粘包拆包[74404:3375045] 发送数据的总字节大小:8379226
2021-02-26 10:42:27.701872+0800 003---粘包拆包[74404:3387849] 10010 发送数据成功
客户端接受到的信息

不知道为什么好像不行诶~尝试了几下都没找到问题原因,有人修复了可以给我说一声我改一下哈,谢谢。

2021-02-26 10:45:43.956786+0800 粘包拆包[74613:3392355] 接收到tag = 10086,长度 = 666078 的数据
2021-02-26 10:45:43.956872+0800 粘包拆包[74613:3392355] 响应总数据的大小 1429423039
2021-02-26 10:45:43.956937+0800 粘包拆包[74613:3392355] 已经接收服务器返回来的数据:上传失败

f、超时重连
杀死服务器后客户端输出的信息
2021-02-26 10:27:45.107291+0800 粘包拆包[74161:3374632] 断开socket连接,错误原因:Error Domain=GCDAsyncSocketErrorDomain Code=7 "Socket closed by remote peer" UserInfo={NSLocalizedDescription=Socket closed by remote peer}
2021-02-26 10:27:45.107865+0800 粘包拆包[74161:3374632] 网络超时,不再重连

四、UDP画板功能应用

无论是在客户端还是服务器,只要一方在进行绘图,另外一方也会立即同步进行绘图。

1、点击按钮触发事件

a、创建socket连接服务器
- (IBAction)didClickCreatSocketAction:(UIBarButtonItem *)sender
{
    // 1 创建socket
    if (!self.udpSocket)
    {
        self.udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    NSLog(@"创建socket 成功");
    
    // 2: 绑定socket
    NSError * error = nil;
    [self.udpSocket bindToPort:8060 error:&error];
    if (error)
    {
        // 监听错误打印错误信息
        NSLog(@"error:%@",error);
    }
    else
    {
        // 3: 监听成功则开始接收信息
        [self.udpSocket beginReceiving:&error];
    }
}
b、断开socket连接
- (IBAction)didClickDisconnectAction:(id)sender
{
    [self.drawView.pathArray removeAllObjects];
    [self.drawView.currentPath removeAllPoints];
    [self.drawView setNeedsDisplay];
}

2、接受数据的回调

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
    ...
}
a、获取到点位信息并将这些点连接起来
NSDictionary *tempDic = [NSJSONSerialization JSONObjectWithData:data
                                                        options:kNilOptions
                                                          error:nil];
NSArray *pointArray = [tempDic objectForKey:@"points"];
UIBezierPath *tempPath = [UIBezierPath bezierPath];
for (int i = 0; i < pointArray.count; I++)
{
    NSDictionary *pointDict = pointArray[I];
    CGPoint point = CGPointMake([pointDict[@"x"] floatValue] , [pointDict[@"y"] floatValue]);
    if (i == 0)
    {
        // 起始点
        [tempPath moveToPoint:point];
    }
    else
    {
        [tempPath addLineToPoint:point];
    }
}
b、获取到线条宽度和颜色,将其绘制到连接好的点位上
dispatch_async(dispatch_get_main_queue(), ^{
    
    UIBezierPath *path = tempPath;
    NSString *lineColor = [tempDic objectForKey:@"lineColor"];
    CGFloat lineWidth = [[tempDic objectForKey:@"lineWidth"] floatValue];
    
    [self.drawView dealwithData:path lineColor:lineColor lineWidth:lineWidth];
    [self.drawView setNeedsDisplay];
});

3、进行绘图将点位信息发送给服务器

- (void)initWithDrawView
{
    __weak typeof(self) weakSelf = self;
    self.drawView.onePathEndBlock = ^(NSMutableDictionary *dict) {
        ...
    };
}
a、获取到绘制的图形的点位信息、线条宽度、线条颜色
UIBezierPath *path = dict[@"drawPath"];
NSString *lineColor = dict[@"lineColor"];
CGFloat lineWidth = [dict[@"lineWidth"] floatValue];

NSArray *points = [path points];
NSMutableArray *temp = [NSMutableArray arrayWithCapacity:1];

for (id value in points)
{
    CGPoint point = [value CGPointValue];
    NSDictionary *dict = @{@"x":@(point.x),@"y":@(point.y)};
    [temp addObject:dict];
}
b、将点位信息、线条宽度、线条颜色包裹成字典
NSDictionary *passDic = @{@"points"    : temp,
                          @"lineWidth" : @(lineWidth),
                          @"lineColor" : lineColor
                          };
c、将字典转化为数据类型再发送给服务器
NSData *passData = [NSJSONSerialization dataWithJSONObject:passDic
                                                   options:NSJSONWritingPrettyPrinted
                                                     error:nil];

[weakSelf.udpSocket sendData:passData toHost:@"127.0.0.1" port:8070 withTimeout:-1 tag:10088];

Demo

Demo在我的Github上,欢迎下载。
UseFrameworkDemo

参考文献

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

推荐阅读更多精彩内容