在iOS开发中,除了蓝牙通信(BLE),Wi-Fi网络下的Socket 通信同样常用于设备互联,比如智能家居、安防系统、工业控制等应用场景。
本篇文章将以Objective-C为语言,完整讲解我在iOS开发工程中如何使用 GCDAsyncSocket 第三方库,在 iOS设备上通过Wi-Fi与局域网设备建立 TCP Socket 通信连接,并实现数据的收发。
使用的框架
导入第三方库 GCDAsyncSocket
我们使用 CocoaAsyncSocket 项目中的 GCDAsyncSocket 作为TCP Socket的封装库。
安装方式:
使用 CocoaPods 安装:
pod 'CocoaAsyncSocket'
或手动集成:
可以自行从GitHub下载,详细教程可以从GitHub官网查看说明。
通信原理简介
- 基于 TCP 协议的通信是可靠连接
- iOS 作为客户端,主动连接服务器(通常是局域网中的嵌入式设备)
- 通过 IP 和端口完成连接,发送/接收数据使用 NSData
创建 SocketManager 类(Objective-C)
我们将通信逻辑封装在 SocketManager 单例类中,方便统一管理。
SocketManager.h
#import <Foundation/Foundation.h>
#import "GCDAsyncSocket.h"
NS_ASSUME_NONNULL_BEGIN
@interface SocketManager : NSObject <GCDAsyncSocketDelegate>
@property (nonatomic, strong) GCDAsyncSocket *socket;
/// 单例
+ (instancetype)sharedManager;
/// 连接服务器
- (void)connectToHost:(NSString *)host port:(uint16_t)port;
/// 发送数据
- (void)sendData:(NSData *)data;
/// 断开连接
- (void)disconnect;
@end
NS_ASSUME_NONNULL_END
SocketManager.m
#import "SocketManager.h"
@implementation SocketManager
+ (instancetype)sharedManager {
static SocketManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[SocketManager alloc] init];
});
return manager;
}
- (instancetype)init {
self = [super init];
if (self) {
self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
return self;
}
#pragma mark - 连接服务器
- (void)connectToHost:(NSString *)host port:(uint16_t)port {
if ([self.socket isConnected]) {
[self.socket disconnect];
}
NSError *error = nil;
BOOL result = [self.socket connectToHost:host onPort:port error:&error];
if (!result || error) {
NSLog(@"连接失败:%@", error.localizedDescription);
} else {
NSLog(@"正在连接 %@:%d", host, port);
}
}
#pragma mark - 发送数据
- (void)sendData:(NSData *)data {
if ([self.socket isConnected]) {
[self.socket writeData:data withTimeout:-1 tag:0];
} else {
NSLog(@"Socket未连接,无法发送");
}
}
#pragma mark - 断开连接
- (void)disconnect {
[self.socket disconnect];
}
#pragma mark - GCDAsyncSocketDelegate
/// 连接成功回调
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
NSLog(@"连接成功:%@:%d", host, port);
[sock readDataWithTimeout:-1 tag:0]; // 准备接收数据
}
/// 接收到数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *receivedStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"收到数据:%@", receivedStr);
[sock readDataWithTimeout:-1 tag:0]; // 继续读取
}
/// 数据发送成功
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
NSLog(@"数据发送成功");
}
/// 断开连接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err {
NSLog(@"连接断开:%@", err ? err.localizedDescription : @"正常断开");
}
@end
示例调用代码
NSString *ip = @"192.168.1.100"; // 设备IP
uint16_t port = 8080; // 端口
[[SocketManager sharedManager] connectToHost:ip port:port];
// 构建发送命令
NSString *cmd = @"A1B2C3D4";
NSData *cmdData = [ParseDataTool transToDataWithString:cmd];
[[SocketManager sharedManager] sendData:cmdData];
我在工作中常见问题与建议
1. 连接不上设备?
- 检查设备是否和 iOS 在同一局域网
- 检查端口是否开放(用 PC 尝试连接测试)
- 如果是虚拟机或开发板,请关闭防火墙或开启Socket服务
2. 收不到数据?
- 检查服务器是否主动发送数据
- 请确保调用了
readDataWithTimeout:tag:读取回包
3. 断开频繁?
- TCP需要持续心跳或定时交互,避免NAT路由断流
- 可设置定时器每隔 30 秒发送一次PING包
实用拓展建议
- 建议使用我之前的
ParseDataTool工具类来处理十六进制命令、异或校验等底层数据 - 若需处理大数据或文件传输,请考虑分片 + 回包确认机制
- 若需加密通信,可结合TLS或AES加密库使用
若有说错的地方,恳请大家指正。相互学习,一起进步~~