iOS App实现基于 Wi-Fi 的 Socket 通信(物联网)

1,250 阅读3分钟

在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加密库使用

若有说错的地方,恳请大家指正。相互学习,一起进步~~