iOS 框架学习-AsyncSocket

公開日: 2025-03-24 21:21 1263文字 7 min read

前端项目通过bridge获取客户端资源,客户端直接返回response对象常用代码模板1——基础算法常用代码模板2——数据结构统计iOS工程代码行数高数概念、公式、定理Objective-C 语法 3Objective-C 语法 2Objective-C 语法 1UIViewController 的生命周期UITableview调用reload方法时抖动问题UILabel中文带行间距的处理,限制行数,计算高度等UIButton扩大点击范围以及关于响应者链条的思考UIApplicationSwiftUI基本控件iPhone6 Plus上面神秘的缝隙iPhone 刘海机型UI适配(X、Xs、Xs Max、Xr)iOS:如何在UITableView调用reloadData刷新结束后再同步执行后续操作iOS 截取整个 scrollview 图片iOS 关于 UITextField 的字数限制Objective-C 中禁止调用指定的方法objc源码分析-runtime-classObjective-C Type EncodingsObjective-C:为什么分类中不能直接添加属性OC优缺点以及常见bugruntime——运行时简单使用当对象接收到不能处理的消息时调用的方法浅谈iOS中的weak为 UIControl 实现线程安全的 Block 事件扩展:原理与实践OC单例宏iOS常用数据类型转换OC中nil 、NULL、 Nil 、NSNull的区别Description方法和NSLog函数Block in Objective-CiOS自动化埋点的实现iOS平台编译Ogre游戏引擎库iOS:特殊符号大全iOS 网络小结iOS 沙盒与 BundleiOS 框架学习-AsyncSocketNSString的各种处理Swift Module 如何被全局引用CocoaPods组件化——OC/Swift动静态库混用COCOAPODS技巧-创建私有仓库关于NSNotificationCenter数据结构与算法解析习题2.23数据结构与算法解析习题2.19数据结构与算法解析习题2.16数据结构与算法解析习题2.14数据结构与算法解析习题2.13数据结构与算法解析习题2.12数据结构与算法解析习题2.11:二分查找数据结构与算法解析习题2.10:霍纳法则(Horner's rule)数据结构与算法解析习题2.7数据结构与算法解析习题1.3数据结构与算法解析习题1.2数据结构与算法解析习题1.1LeetCode 486 Predict the Winner(预测赢家)LeetCode 398 随机数索引LeetCode 106 Construct Binary Tree from Inorder and Postorder Traversal(由中序和后序遍历建立二叉树)LeetCode 70 爬楼梯(青蛙跳台阶)LeetCode 8 String to Integer (atoi)LeetCode 6 ZigZag Conversion(Z字转换)LeetCode 5 Longest Palindromic Substring(最长回文字串)iOS脚本打包 ipa(.app转.ipa)《什么是数学 》习题 第一章 补充《什么是数学 》习题 第一章 2 数系的无限性 数学归纳法《什么是数学 》习题 第一章 1 整数的计算Vue 的一些指令和缩写
この投稿は「日本語」では表示できません。元の投稿を表示しています。
最近需要写一个通过客户端向某一主板发送 UDP 消息的 Demo,之前只是简单了解过 AsyncSocket 这个框架,正好借此机会深入了解一下。 TCP/IP TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定

最近需要写一个通过客户端向某一主板发送 UDP 消息的 Demo,之前只是简单了解过 AsyncSocket 这个框架,正好借此机会深入了解一下。

TCP/IP

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准。

从字面意思来看 TCP/IP 是 TCP 和 IP 协议的合称,但实际上 TCP/IP 协议是指因特网整个 TCP/IP 协议族。不同于 OSI 模型的七个分层,TCP/IP 协议参考模型把所有的 TCP/IP 系列协议归类到四个抽象层中:

  • 应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等

  • 传输层:TCP,UDP

  • 网络层:IP,ICMP,OSPF,EIGRP,IGMP

  • 数据链路层:SLIP,CSLIP,PPP,MTU

每一抽象层建立在低一层提供的服务上,并且为高一层提供服务。

什么是 Socket

Socket 又称”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求。是在应用层和传输层之间的一个抽象层,它把 TCP/IP 层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

17001908225463.jpg
17001908225463.jpg

关于三次握手和其他相关知识的详细内容在此就不在赘述了,有机会我再补充,现在有兴趣的可以看一下:

维基百科:TCP/IP 协议族

百度百科:Socket

AsyncSocket

AsyncSocket 是在 iOS 上比较火的一个第三方库,对socket的封装比较好。

下载  AsyncSocket  库,或者用 pod 也可以,其中核心文件为

AsyncSocket.h, 
AsyncSocket.m, 
AsyncUdpSocket.h,
AsyncUdpSocket.m

必须保证这四个文件在工程之中。

UDP

一般来说,一个用户(对于 iOS 来说也就是我们的项目中)只能有一个正在连接的 socket,所以这个 socket 变量必须是全局的,这里可以考虑使用单例或是AppDelegate 进行数据共享,本文使用单例。

先分享一个单例宏,这样写单例的时候只需要在声明和实现中各写一行代码就完事了。

// .h文件的实现
#define SingletonH(methodName) + (instancetype)shared##methodName;

// .m文件的实现
#if __has_feature(objc_arc) // 是ARC
#define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
}

#else // 不是ARC

#define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
\
- (oneway void)release \
{ \
\
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return 1; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
}

#endif

把这个加在单例类的声明之中然后

@interface AKUdpSocketSingleton : NSObject

/// 获取单例
SingletonH(AKUdp)

@end

@implementation AKUdpSocketSingleton

SingletonM(AKUdp)

@end

这样就大功告成。

然后,在声明文件中写四个属性,遵循 GCDAsyncUdpSocketDelegate协议,声明一个连接方法和一个发送包的方法。

@interface AKUdpSocketSingleton : NSObject
<
GCDAsyncUdpSocketDelegate



@property (nonatomic, strong) GCDAsyncUdpSocket    *socket;       // socket
@property (nonatomic, copy  ) NSString             *socketHost;   // socket的Host
@property (nonatomic, assign) UInt16               socketPort;    // socket的prot
@property (nonatomic ,copy)   NSString             *message;      // 包内容

/// 获取单例
SingletonH(AKUdp)

/// socket连接
-(void)socketConnect;

/// 发送包

- (void)send;

@end

实现文件中实现 -(void)socketConnect;- (void)send;

-(void)socketConnect
{
    self.socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self
                                                delegateQueue:dispatch_get_main_queue()];
    //允许广播形式
    [self.socket enableBroadcast:YES error:nil];
	NSError *error = nil;

    //绑定端口
    if (![self.socket bindToPort:0 error:&error])
    {
        [self logError:FORMAT(@"Error binding: %@", error)];
        return;
    }

    if (![self.socket beginReceiving:&error])
    {
        [self logError:FORMAT(@"Error receiving: %@", error)];
        return;
    }

    [self logInfo:@"Ready"];
}

- (void)send
{
    if (self.socketHost.length == 0)
    {
        [self logError:@"Address required"];
        return;
    }
    if (self.socketPort <= 0 || self.socketPort > 65535)
    {
        [self logError:@"Valid port required"];
        return;
    }

    if (self.message == 0)
    {
        [self logError:@"Message required"];
        return;
    }

    NSData *data = [self.message dataUsingEncoding:NSUTF8StringEncoding];
    [self.socket sendData:data
                   toHost:self.socketHost
                     port:self.socketPort
              withTimeout:-1
                      tag:self.tag];

    [self logMessage:FORMAT(@"SENT (%i): %@", (int)self.tag, self.message)];

    self.tag++;
}

实现GCDAsyncUdpSocketDelegate 协议中的几个代理

#pragma mark - GCDAsyncUdpSocketDelegate

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
    // You could add checks here
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error
{
    // You could add checks here
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock
   didReceiveData:(NSData *)data
      fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if (msg)
    {
        [self logMessage:FORMAT(@"RECV: %@", msg)];
    }
    else
    {
        NSString *host = nil;
        uint16_t port = 0;
        [GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];

        [self logInfo:FORMAT(@"RECV: Unknown message from: %@:%hu", host, port)];
    }
}

在需要发 UDP 包的地方直接通过单例写下列代码就好了

    // 创建 socket 连接
    [[AKUdpSocketSingleton sharedAKUdp] socketConnect];

    // 设置 host
    [AKUdpSocketSingleton sharedAKUdp].socketHost = @"192.168.0.1";

    // 设置 port
    [AKUdpSocketSingleton sharedAKUdp].socketPort = 10086;

    // 设置包内容
    [AKUdpSocketSingleton sharedAKUdp].message = @"红红火火恍恍惚惚";

    // 发送包
    [[AKUdpSocketSingleton sharedAKUdp] send];
以上就是 UDP 简单使用方式,以后用到别的方法会继续补充。有问题随时留言哈。
© 2024 - 2026 cos @cosine
Powered by theme astro-koharu · Inspired by Shoka