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)

用户喜欢...

云计算使用不断增长

究竟何为云计算?最简单的解释是,它是一种基于互联网的服务,提供对共享计算资源(例如存储、服务器、网络、应用、服务)的按需访问,还可将数据传输到连网的设备。使用这些服务,用...


直接数字合成器 (DDS) 的基础知识及其选择和使用方式

随着无线设备数量的激增,设计人员正在使用越来越复杂多变的波形,来满足应用在数据速率、干扰抑制、成本、封装和低功耗方面的要求。这些波形需要有稳定的射频源:可以根据需要进行调...


使用无刷DC驱动器设计的六种奇怪方法

我已经知道了两个绝对真理。首先,无刷直流电机驱动器很酷。每个人都喜欢旋转电机的快感,无刷直流电机通过高速,高功率和高效率来提升令人惊叹的因素。其次,工程师喜欢有创意。有时...


使用LDPC编码处理5G NR DL-SCH和UL-SCH传输块

该示例突出显示了用于5G NR下行链路和上行链路共享传输信道(DL-SCH和UL-SCH)的低密度奇偶校验(LDPC)编码链。 共享通道参数 该示例使用DL-SCH来描述处理,其也适用于UL-SCH。 选择在下行链路共...


使用毫米波雷达套件快速开发精密目标检测设计

设计人员在设计运动传感器时面临着持续的竞争压力。传感器不仅要体积更小、更精确,并且还要在智能建筑、工厂自动化、交通运输和无人机等多种行业应用中具有更远的检测范围。虽然毫米...


Arduino 环境中的 FPGA:使用 Alorium 的 Snō 模块支持预配置和定制 IP

当固件在微控制器或微处理器上的运行速度过慢时,现场可编程门阵列 (FPGA) 可解决实时嵌入式设计的硬件问题。同时,FPGA 还具有外设灵活性。然而,要使用 FPGA,设计工程师就需要学习全新的...


使用SiC和GaN功率器件来满足EV设计要求

汽车制造商越来越多地开发电动汽车(EV),但他们的短距离驾驶范围仍然存在问题。虽然空气动力学设计,更轻的材料和更高效的电力使用确实有帮助,但这还不够。汽车电力电子设计人员需...


在高性能物联网端点中使用查找表时,外部闪存具有优势

随着物联网(IoT)网络变得更加复杂,物联网端点正在边缘执行更复杂的处理。这可能需要使用具有更快时钟速度,更多内存和更强大处理器内核的微控制器的系统来升级现有端点。 这还需要...


在 GPS 不足以提供精准的位置数据时使用 IMU

全球导航卫星系统 (GNSS) 非常有用,能够定位全球任意位置已正确启用此功能的系统,但仅仅使用 GNSS 接收器进行定位也存在一些问题。使用惯性测量装置 (IMU) 对 GNSS 进行补充,则可以克服这些...


使用暗电流补偿改进脉搏血氧饱和度测量结果

可穿戴式脉搏血氧计是一种无创医疗设备,可用于测量血氧饱和度和脉搏率。这些技术依靠的过程是,让 LED 光穿过透亮的身体部位(通常是手指),然后对其进行检测。 虽然这是一项广为人知...