一、前言

  • Socket

    • Socket 是对 TCP/IP 协议的封装,其中IP协议对应为网络层,TCP 协议对应为传输层,而我们常用的HTTP协议,是位于应用层,在七层模型中HTTP协议是基于 TCP/IP 的,我们想要使用 TCP/IP 协议,则要通过 Socket
  • Socket 编程用途(其他待补充)
    • 长连接
    • 端到端的即时通讯
  • Socket 和 Http(来源网络)
    • socket 一般用于比较即时的通信和实时性较高的情况,比如推送,聊天,保持心跳长连接等,http 一般用于实时性要求不那么高的情况,比如信息反馈,图片上传,获取新闻信息等。

二、类似《你猜我画》简易效果说明

  • 效果(分别是模拟器和手机截图)



  • 工作中碰到类似需求,但没找到类似的成熟的第三方框架,只有先看看原理性的东西了。其实也就基于 socket 即时传输图片数据、笔画数据,还有聊天文字,也可以拓展做其他的指令控制

  • 没有做注册登录,没有做用户管理,只是简单原理性的探讨

  • 基于 GCDAsyncSocket 框架进行,关于 GCDAsyncSocket 的介绍可自行了解

三、服务端部分代码

  • 直接用 mac 程序作为服务端

    • Server 类
/*!
@method  开启服务
@abstract 开启服务器 TCP 连接服务
*/
- (void)startServer { self.serverSocket = [[GCDAsyncSocket alloc]initWithDelegate:self
delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
[self.serverSocket acceptOnPort:5555
error:&error];
if (error) {
NSLog(@"服务开启失败");
} else {
NSLog(@"服务开启成功");
} }
#pragma mark - GCDAsyncSocketDelegate
/*!
@method  收到socket端连接回调
@abstract 服务器收到socket端连接回调
*/
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket {
[self.clientSocketArray addObject:newSocket];
[newSocket readDataWithTimeout:-1
tag:self.clientSocketArray.count];
}
/*!
@method  收到socket端数据的回调
@abstract 服务器收到socket端数据的回调
*/
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
// 直接进行转发数据
for (GCDAsyncSocket *clientSocket in self.clientSocketArray) {
if (sock != clientSocket) { [clientSocket writeData:data
withTimeout:-1
tag:0];
}
}
[sock readDataWithTimeout:-1
tag:0]; }
  • main 中
int main(int argc, const char * argv[]) {
@autoreleasepool { Server *chatServer = [[Server alloc]init];
[chatServer startServer];
// 开启主运行循环
[[NSRunLoop mainRunLoop] run];
}
return 0;
}

四、移动端部分代码

  • 基于 GCDAsyncSocket 的接受数据代理方法及发送数据方法
  • 图片数据的发送
// 回调 发送图片
__weak typeof(self) weakSelf = self;
bgImgView.block = ^(UIImage *img) { weakSelf.drawView.drawImg = img;
// image
NSData *imgData = UIImageJPEGRepresentation(weakSelf.drawView.drawImg, 0.2);
NSMutableData *dat = [NSMutableData data];
[dat appendData:imgData];
// 拼接二进制数据流的结束符
NSData *endData = [@"$" dataUsingEncoding:NSUTF8StringEncoding];
[dat appendData:endData];
// 发送数据
[weakSelf.clientSocket writeData:dat
withTimeout:-1
tag:111111]; };
  • 图片二进制数据的传输是基于流的,一段一段的,避免断包缺包等问题,需要拼接结束符,图片数据结束
  • 图片数据的接受接受
        // 拼接数据 转成图片

       [self.socketReadData appendData:data];

       NSData *endData = [data subdataWithRange:NSMakeRange(data.length -1, 1)];

       NSString *end= [[NSString alloc] initWithData:endData
encoding:NSUTF8StringEncoding]; if ([end isEqualToString:@"$"]) { UIImage *tmpImg = [UIImage imageWithData:self.socketReadData]; self.drawView.drawImg = tmpImg; [self.drawView setNeedsDisplay]; [self.clientSocket readDataWithTimeout:-1
tag:111111];
// 拼完图片 恢复默认
self.socketReadData = nil; }
  • 画布笔画数据的传输

    • 因为传输的是二进制数据,所以采取将贝塞尔曲线转换成 CGPoint 坐标数组,再加上线宽和线的颜色,最后组成一个字典,转换为二进制进行传输
    • 考虑到坐标点在不同屏幕上需要适配,因此需要把当前手机端的屏幕高宽一起传输
/*!
@method  发送路径
@abstract 通过socket 发送路径信息
*/
- (void)sendPath {
// path 坐标点及 转换
NSArray *points = [(UIBezierPath *)self.dataModel.path points];
NSMutableArray *tmp = [NSMutableArray array];
for (id value in points) {
CGPoint point = [value CGPointValue];
NSDictionary *dic = @{@"x" : @(point.x), @"y": @(point.y)};
[tmp addObject:dic];
} // 颜色类别
NSInteger colorNum = 0; if (CGColorEqualToColor(self.drawView.color.CGColor, [UIColor redColor].CGColor)) {
colorNum = 1;
}
else if (CGColorEqualToColor(self.drawView.color.CGColor, [UIColor blueColor].CGColor) ){ colorNum = 2;
} else if (CGColorEqualToColor(self.drawView.color.CGColor, [UIColor greenColor].CGColor) ) {
colorNum = 3;
} // 传递数据格式
NSDictionary *pathDataDict = @{
@"path" : tmp,
@"width" : @(self.drawView.width),
@"color" : @(colorNum),
@"screenW": @([UIScreen mainScreen].bounds.size.width),
@"screenH": @([UIScreen mainScreen].bounds.size.height)
}; NSData *pathData = [NSJSONSerialization
dataWithJSONObject:pathDataDict
options:NSJSONWritingPrettyPrinted
error:nil]; [self.clientSocket writeData:pathData
withTimeout:-1
tag:111111];
}
  • 笔画数据的接受

    • 需要转换坐标,解析自定义传输的数据格式
       // 1、接受坐标点
NSInteger w = [tmpDict[@"screenW"] integerValue];
NSInteger h = [tmpDict[@"screenH"] integerValue];
CGFloat scaleW = [UIScreen mainScreen].bounds.size.width / w;
CGFloat scaleH = [UIScreen mainScreen].bounds.size.height / h;
// 处理点
NSArray *pointDict = tmpDict[@"path"];
DIYBezierPath *path = [[DIYBezierPath alloc]init];
for (NSDictionary *tmpDict in pointDict) {
CGPoint point = CGPointMake([tmpDict[@"x"] floatValue] * scaleW, [tmpDict[@"y"] floatValue] * scaleH);
NSInteger index = [pointDict indexOfObject:tmpDict];
if (index == 0) {
[path moveToPoint:point];
} else {
[path addLineToPoint:point];
} }
switch ([tmpDict[@"color"] integerValue]) {
case 0:
self.drawView.color = [UIColor blackColor];
break;
case 1:
self.drawView.color = [UIColor redColor];
break;
case 2:
self.drawView.color = [UIColor blueColor];
break;
case 3:
self.drawView.color = [UIColor greenColor];
break; default:
break;
}
self.drawView.width = [tmpDict[@"width"] floatValue];
self.drawView.currentPath = path;
self.drawView.currentPath.pathColor = self.drawView.color;
self.drawView.currentPath.lineWidth = self.drawView.width;
[self.drawView.pathArray addObject:path];
[self.drawView setNeedsDisplay];

五、小demo地址

https://github.com/HOWIE-CH/-You-guess-I-painted-_socket.git

六、问题

  • 定义了图片文件二进制数据、笔画路径二进制数据、聊天字符串二进制数据,三种格式的二进制数据,在 GCDAsyncSocket 接受数据的代理方法,需要判断接受的二进制文件的类型再进行解析,如果有更好的方式可留言。
  • 只是简单的功能的尝试,有时存在画的一条线过长就传输不过去的情况,存在图片偶尔传输不完整的情况
  • 不清楚是否有相关成熟的框架,如果有,请留言。
  • 最近试过服务端是 NodeJs 用 socket.io 的话,iOS 用 GCDAsyncSocket,感觉这样是通讯不了的。像这样要实现 Android、iOS 跨平台 socket 传输数据,那 socket 选择什么框架呢,服务端选择什么 socket 框架? 之前即时通讯都是 XMPP ,现在貌似是 webSocket socket.io 了。

基于 GCDAsyncSocket,简单实现类似《你猜我画》的 socket 数据传输的更多相关文章

  1. 基于 socket.io, 简单实现多平台类似你猜我画 socket 数据传输

    一.前言 socket.io 实现了实时双向的基于事件的通讯机制,是基于 webSocket 的封装,但它不仅仅包括 webSocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通 ...

  2. 基于Bootstrap简单实用的tags标签插件

    http://www.htmleaf.com/jQuery/ jQuery之家 自由分享jQuery.html5和css3的插件库 基于Bootstrap简单实用的tags标签插件

  3. 基于最简单的FFmpeg包封过程:视频和音频分配器启动(demuxer-simple)

    ===================================================== 基于最简单的FFmpeg封装工艺的系列文章上市: 最简单的基于FFmpeg的封装格式处理:视 ...

  4. 基于最简单的FFmpeg采样读取内存读写:存储转

    ===================================================== 基于最简单的FFmpeg样品系列读写内存列表: 最简单的基于FFmpeg的内存读写的样例:内 ...

  5. 基于最简单的FFmpeg的AVDevice抽样(屏幕录制)

    =====================================================基于最简单的FFmpeg的AVDevice样品文章: 最简单的基于FFmpeg的AVDevic ...

  6. 基于最简单的FFmpeg采样读取内存读写:内存玩家

    ===================================================== 基于最简单的FFmpeg样品系列读写内存列表: 最简单的基于FFmpeg的内存读写的样例:内 ...

  7. 基于Jquery 简单实用的弹出提示框

    基于Jquery 简单实用的弹出提示框 引言: 原生的 alert 样子看起来很粗暴,网上也有一大堆相关的插件,但是基本上都是大而全,仅仅几句话可以实现的东西,可能要引入好几十k的文件,所以话了点时间 ...

  8. 基于PHP——简单的WSDL的创建(WSDL篇)

    1.建立WSDL文件      建立WSDL的工具很多,eclipse.zendstudio.vs都可以,我个人建议自己写,熟悉结构,另外自动工具对xml schame类型支持在类型中可能会报错. 下 ...

  9. Python 基于pykafka简单实现KAFKA消费者

    基于pykafka简单实现KAFKA消费者   By: 授客 QQ:1033553122         1.测试环境 python 3.4 zookeeper-3.4.13.tar.gz 下载地址1 ...

随机推荐

  1. vs2012中的小技巧

    解除起始页: 网站(或者叫项目)-属性-启动选项-使用当前页 发布项目: 有些文件在发布的时候,不能发布到指定文件夹中,所以要手动修改该文件的属性 修改两处: 复制到输出目录:始终复制 生成操作:内容

  2. Windows Server 2012 安装sqlserver2008 小记

    1.拷贝大文件被阻止   解决方案:把大文件压缩成小文件... 据说关闭防火墙会好点,没试验过. 2.安装第一步,提示没有安装.net framework 3.5 sp1 ,使用服务器管理器,添加角色 ...

  3. S3C2440看门狗解析

    个PCLK周期的复位信号 也就是说,在某些环境下,看门狗可以当做定时器使用,当他中断的时候并不发生复位,只发生中断,我看看图 看门狗的中断和复位信号是可以依靠wtcon来切断的(看门狗的时钟是无法切断 ...

  4. API WAVE 专栏

    关于音频输入.输出设备的使用 源:API WAVE 专栏

  5. java数据结构整理(二)

    一.List接口,有序的Collection接口,能够精确地控制每个元素插入的位置,允许有相同的元素 1.链表,LinkedList实现了List接口,允许null元素,提供了get().remove ...

  6. 2016——3——16 kmp 7题

    1.传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2725 题目大意:找一个串在另一个串中出现的次数 题解:kmp(纯裸题) #inclu ...

  7. UVa 143 - Orchard Trees

    题目大意:果园里的树排列成矩阵,它们的x和y坐标均是1~99的整数.输入若干三角形,依次统计每一个三角形内部和边界上共有多少棵树. 三角形P0P1P2有向面积为A:2A = x0y1 + x2y0 + ...

  8. JAVA文件的两种读取方法和三种写入方法

    在使用java对文件进行读写操作时,有多种方法可以使用,但不同的方法有不同的性能. 此文对常用的读写方法进行了整理,以备不时之需. 1.文件的读取 主要介绍两种常用的读取方法.按行读取和按字符块读取. ...

  9. iOS 英语

    allocation:分配 agrregate :聚合,聚集,总计.在iOS中是一种文件类型. atomically:原子级的 archiver:归档.例如,oc专门有归档类NSKeyedArchiv ...

  10. Webx3学习笔记(2)——基本流程

    Webx3项目是运行在jetty/tomcat这种Web应用容器中的,Web应用的模式都是请求-响应的.一个请求通过浏览器发出,封装为HTTP报文到达服务端,被容器接受到,封装为HttpRequest ...