close
当前位置: 物联网在线 > 技术文库 > ios >

iOS 使用 socket 即时通信(非第三方库)

其实写这个socket一开始我是拒绝的。

iOS 使用 socket 即时通信(非第三方库)

因为大家学C 语言和linux基础时肯定都有接触,客户端和服务端的通信也都了解过,加上现在很多开放的第三方库都不需要我们来操作底层的通信。

但是来了!!!

但是!还是想写。底层的东西最好了解下。

iOS 使用 socket 即时通信(非第三方库)

好了 正经了!!!!

效果

iOS 使用 socket 即时通信(非第三方库)

效果.gif

由于5M的上传限制GIF可能看不清 我再截两张图吧

iOS 使用 socket 即时通信(非第三方库)

服务器

iOS 使用 socket 即时通信(非第三方库)

客户端A

iOS 使用 socket 即时通信(非第三方库)

客户端B

模型图

iOS 使用 socket 即时通信(非第三方库)

做了个逗比模型图:arrow_down:

iOS 使用 socket 即时通信(非第三方库)

模型

分析

由上图可以了解到服务器和客户端需要做哪些工作

服务器

抽象一点分为:

1.创建线程等待接收客户端的连接

2.接收并解析客户端发来的消息

3.给客户端发送消息
具体一点:

1.创建socket. 绑定端口.开始监听.

2.创建线程.等待接收客户端连接.

3.接收客户端发来的消息

4.解析消息内容

a.设置用户名

b.发送消息给指定客户端

客户端

抽象一点分为:

1.连接服务器

2.给服务器发送消息

3.接收服务器消息

4.解析消息内容
具体一点:

1.创建socket.绑定端口.连接服务器

2.发送消息

a.设置用户名

b.给指定用户发消息:按服务器格式拼接字符串

3.接收消息

a.普通消息

b.用户列表:保存至用户列表

UI方面

服务器:其实不用什么UI放个控件展示下日志就是了

客户端:比较简单,一个俗套聊天室的界面,直接storyboard里拖拖控件设置约束了

DEMO而已别太当真

iOS 使用 socket 即时通信(非第三方库)

代码部分 服务器

要使用scoket需要引用这三个头文件

#include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h>

只有一个model,用来绑定用户名和socket

@interface ClientModel : NSObject @property(nonatomic,assign)int clientSocket; @property(nonatomic,copy)NSString *clientName; @end

只有一个文件全给你

#import "ViewController.h" #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #import "ClientModel.h" static int const kMaxConnectCount = 5; @interface ViewController() @property (weak) IBOutlet NSTextField *textField; //@property (nonatomic,assign)int client_socket; //客户端socket @property (unsafe_unretained) IBOutlet NSTextView *textView; @property (nonatomic,strong)NSMutableArray *clientArray; @property (nonatomic,strong)NSMutableArray *clientNameArray; @end @implementation ViewController - (NSMutableArray *)clientArray { if (!_clientArray) { _clientArray = [NSMutableArray array]; } return _clientArray; } - (NSMutableArray *)clientNameArray { if (!_clientNameArray) { _clientNameArray = [NSMutableArray array]; } return _clientNameArray; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. //创建socket int server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { NSLog(@"创建失败"); [self showLogsWithString:@"socket创建失败"]; }else{ //绑定地址和端口 struct sockaddr_in server_addr; server_addr.sin_len = sizeof(struct sockaddr_in); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(1234); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server_addr.sin_zero), 8); int bind_result = bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (bind_result == -1) { NSLog(@"绑定端口失败"); [self showLogsWithString:@"绑定端口失败"]; }else{ if (listen(server_socket, kMaxConnectCount)==-1) { NSLog(@"监听失败"); [self showLogsWithString:@"监听失败"]; }else{ for (int i = 0; i < kMaxConnectCount; i++) { //接受客户端的链接 [self acceptClientWithServerSocket:server_socket]; } } } } } - (void)setRepresentedObject:(id)representedObject { [super setRepresentedObject:representedObject]; // Update the view, if already loaded. } //创建线程接受客户端 -(void)acceptClientWithServerSocket:(int)server_socket{ struct sockaddr_in client_address; socklen_t address_len; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ //创建新的socket while (1) { int client_socket = accept(server_socket, (struct sockaddr*)&client_address,&address_len ); if (client_socket == -1) { [self showLogsWithString:@"接受客户端链接失败"]; NSLog(@"接受客户端链接失败"); }else{ NSString *acceptInfo = [NSString stringWithFormat:@"客户端 in,socket:%d",client_socket]; [self showLogsWithString:acceptInfo]; //接受客户端数据 [self recvFromClinetWithSocket:client_socket]; } } }); } //接受客户端数据 - (void)recvFromClinetWithSocket:(int)client_socket{ while (1) { //接受客户端传来的数据 char buf[1024] = {0}; long iReturn = recv(client_socket, buf, 1024, 0); if (iReturn>0) { NSLog(@"客户端来消息了"); NSString *str = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding]; [self showLogsWithString:[NSString stringWithFormat:@"客户端来消息了:%@",str]]; [self checkRecvStr:str andClientSocket:client_socket]; }else if (iReturn == -1){ NSLog(@"读取消息失败"); [self showLogsWithString:@"读取消息失败"]; break; }else if (iReturn == 0){ NSLog(@"客户端走了"); [self showLogsWithString:[NSString stringWithFormat:@"客户端 out socket:%d",client_socket]]; NSMutableArray *array = [NSMutableArray arrayWithArray:self.clientArray]; for (ClientModel *model in array) { if (model.clientSocket == client_socket) { [self.clientNameArray removeObject:model.clientName]; [self.clientArray removeObject:model]; } } close(client_socket); break; } } } //检查接受到的字符串 - (void)checkRecvStr:(NSString*)str andClientSocket:(int)socket{ if ([str hasPrefix:@"name:"]) { NSString *name = [str substringFromIndex:5]; ClientModel *model = [[ClientModel alloc] init]; model.clientSocket = socket; model.clientName = name; if (self.clientArray.count > 0) { int flag = 999; //用户名不能相同 int i = 0; for (ClientModel *client in self.clientArray) { //改名 if (client.clientSocket == socket) { NSString *oldName = self.clientNameArray[i]; self.clientNameArray[i] = name; self.clientArray[i] = model; for (ClientModel *oldclient in self.clientArray) { [self sendMsg:[NSString stringWithFormat:@"%@ 改名 %@",oldName,name] toClient:oldclient.clientSocket]; [self showLogsWithString:[NSString stringWithFormat:@"%@ 改名 %@",oldName,name]]; NSString *list = [self.clientNameArray componentsJoinedByString:@","]; //向客户端推送当前在线列表 [self sendMsg:[NSString stringWithFormat:@"list:%@",list] toClient:oldclient.clientSocket]; } flag = 2; }else{ if ([client.clientName isEqualToString:model.clientName]) { //用户名已存在 flag = 1; break; } } i++; } if (flag != 1 & flag != 2) { [self.clientArray addObject:model]; [self.clientNameArray addObject:model.clientName]; //向客户端推送当前在线列表 for (ClientModel *client in self.clientArray) { [self sendMsg:[NSString stringWithFormat:@"%@,上线了",name] toClient:client.clientSocket]; NSString *list = [self.clientNameArray componentsJoinedByString:@","]; //向客户端推送当前在线列表 [self sendMsg:[NSString stringWithFormat:@"list:%@",list] toClient:client.clientSocket]; } //给当前客户端发送一条欢迎信息 NSString *msg = [NSString stringWithFormat:@"Welcome %@ !",name]; [self sendMsg:msg toClient:socket]; [self showLogsWithString:msg]; }else if (flag == 1){ [self sendMsg:@"注册用户名失败,用户名已经存在,请重新设置用户名" toClient:socket]; [self showLogsWithString:[NSString stringWithFormat:@"socket %d 注册用户名失败,设置的用户名已经存在",socket]]; for (ClientModel *model in self.clientArray) { [name isEqualToString:model.clientName]; } } }else{ [self.clientArray addObject:model]; [self.clientNameArray addObject:model.clientName]; //向客户端推送当前在线列表 //给当前客户端发送一条欢迎信息 NSString *msg = [NSString stringWithFormat:@"Welcome %@ !",name]; [self sendMsg:msg toClient:socket]; [self showLogsWithString:msg]; NSString *list = [self.clientNameArray componentsJoinedByString:@","]; //向客户端推送当前在线列表 [self sendMsg:[NSString stringWithFormat:@"list:%@",list] toClient:socket]; } } //给某人发消息 else if ([str hasPrefix:@"to:"]){ NSRange nameRange = [str rangeOfString:@"*"]; NSString *name = [str substringWithRange:NSMakeRange(3, nameRange.location-3)]; NSString *content = [str substringFromIndex:nameRange.location+1]; NSString *fromClientName; //找出发送者 for (ClientModel *model in self.clientArray) { if (socket == model.clientSocket) { fromClientName = model.clientName; break; } } //给目标发送信息 for (ClientModel *model in self.clientArray) { if ([name isEqualToString:model.clientName]) { NSString *msg = [NSString stringWithFormat:@"%@ to you\n%@",fromClientName,content]; [self sendMsg:msg toClient:model.clientSocket]; [self showLogsWithString:[NSString stringWithFormat:@"%@ 发送给 %@ 内容是:%@",fromClientName,name,content]]; break; } } } } //给客户端发送信息 - (void)sendMsg:(NSString*)msg toClient:(int)socket{ char *buf[1024] = {0}; const char *p1 = (char*)buf; p1 = [msg cStringUsingEncoding:NSUTF8StringEncoding]; send(socket, p1, 1024, 0); } //在界面上显示日志 - (void)showLogsWithString:(NSString*)str { dispatch_async(dispatch_get_main_queue(), ^{ NSString *newStr = [NSString stringWithFormat:@"\n%@",str]; self.textView.string = [self.textView.string stringByAppendingString:newStr]; }); } @end 客户端
(责任编辑:ioter)

用户喜欢...

使用光学传感器解决传感挑战

能够适应不断变化的应用要求的测量系统是首选,因为不断增长的成本压力需要较长的系统寿命和功能灵活性。开发此类系统的最有效方法是使用像PXI这样的软件定义的模块化架构。可以混合和...


使用DLP技术创建高分辨率自适应大灯

传统上,典型的汽车前照灯光束仅照亮车辆前方的物体,以提高驾驶员在低光照和恶劣天气条件下的可视性。低光束在车辆前方短距离照亮道路,而远光灯具有更长的范围和更宽的角度。这种...


使用原型套件简化能量收集设计

在使用设计之前,根据其环境条件,能量收集电路能够提供多少功率可能并不明显。 这会影响能量收集源和能量存储所需的尺寸和质量。 为了使设计进一步复杂化,诸如能量存储泄漏,稳压器...


使用Xilinx系统生成器实现简单的DDS

在本文中,我们将讨论使用Xilinx System Generator实现简单的直接数字频率合成器(DDS)。 System Generator是一个功能强大的工具,它将Xilinx FPGA设计过程与MATLAB的Simulink集成,后者使用高级描述轻松实...


如何为RF应用选择,使用和维护同轴连接器

无线电频率(RF)电路在有线和无线通信中正在激增,包括用于物联网(IoT)的Wi-Fi和各种无线技术。这些高频信号需要在系统,电路组件和子组件之间进行分配,而损耗或寄生辐射损耗最小。...


为物联网程序存储器应用选择和使用正确的闪存技术

随着IoT节点变得越来越强大,处理需求可能会增加到需要完整的嵌入式操作系统(OS)(如Linux)才能正确管理所有运行任务的复杂性的水平。尽管将此代码存储在闪存microSDHC卡上很方便,但大...


使用RS-485和电流回路接口实现可靠的工业连接

尽管蓝牙,以太网和其他连接选项广泛普及并支持高数据速率,但工业应用设计人员仍然需要确保以最低的成本获得最可靠的连接。他们还必须在安装的连接选项基础范围内工作。 由于这些原...


使用Zigbee和线程快速构建网状连接的照明应用

对于家庭,建筑和工业自动化应用,连接的照明系统非常方便,可以帮助节省能源。基于802.15.4的网状网络提供低功耗操作,扩展范围和可用性的有效组合,但实施起来却很复杂。 为了减少这种...


使用全可编程SoC简化您的增强现实系统

研究表明,我们人类与世界的视觉互动,因为我们处理视觉图像比以其他形式呈现的信息快许多倍,例如书面文本。 增强现实(AR)与其虚拟现实(VR)表亲一样,使我们能够体验对周围环境的...


使用Amazon FreeRTOS将设计快速安全地连接到云端

传统的嵌入式系统开发人员通常在断开设备的裸机级别工作,但物联网需要系统连接到Internet。这要求嵌入式系统开发人员学习如何连接他们的设备并使用更高级别的操作系统,例如实时操作系...