iOS - AsyncSocket 的使用
生活随笔
收集整理的這篇文章主要介紹了
iOS - AsyncSocket 的使用
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1、AsyncSocket
基于 CFSocket、GCD 進行的封裝(OC)。
支持 TCP 和 UDP。
完整的回調函數(用于處理各種回調事件,連接成功,斷開連接,收到數據等)。
需要注意的問題:
- 1、Socekt 連接成功回調方法中主動調用:[self.socket readDataWithTimeout:-1 tag:0];,相當于主動添加一個讀取請求,不然不會執行讀取信息回調方法。
- 2、讀取信息回調方法中,讀取完信息后,主動調用一下 [self.socket readDataWithTimeout:-1 tag:0];,讀取完信息后,重新向隊列中添加一個讀取請求,不然當收到信息后不會執行讀取回調方法。
2、基本使用
2.1 Client 客戶端
TCP 客戶端
#import "GCDAsyncSocket.h"@interface ViewController () <GCDAsyncSocketDelegate>@property (weak, nonatomic) IBOutlet UITextField *addrTF;@property (weak, nonatomic) IBOutlet UITextField *portTF;@property (weak, nonatomic) IBOutlet UITextField *msgTF;@property (weak, nonatomic) IBOutlet UITextView *logTV;@property (nonatomic, strong) GCDAsyncSocket *clientSockfd;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];// 創建 Socket,在主隊列中處理,所有的回執都在主隊列中執行。self.clientSockfd = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];}- (IBAction)connectBtnClick:(id)sender {NSError *error = nil;// Socket 連接if (![self.clientSockfd isConnected]) {[self.clientSockfd connectToHost:_addrTF.text onPort:_portTF.text.intValue error:&error];}if (error != nil) {[self showLogMessage:@"連接失敗..."];}}#pragma mark - GCDAsyncSocketDelegate 協議方法// 連接成功,協議方法- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {// 讀取數據,必須添加,相當于主動添加一個讀取請求,不然不會執行讀取信息回調方法[self.clientSockfd readDataWithTimeout:-1 tag:0];[self showLogMessage:[NSString stringWithFormat:@"連接服務器地址:%@, 端口:%d 成功", host, port]];[self showLogMessage:[NSString stringWithFormat:@"本地地址:%@, 端口:%d", sock.localHost, sock.localPort]];}// 已經斷開鏈接,協議方法- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {[self showLogMessage:@"socket 斷開連接..."];}// 讀取到數據,協議方法- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {// 注意:要想長連接,必須還要在 DidReceiveData 的 delegate 中再寫一次 [_udpSocket receiveOnce:&error]// 讀取數據,讀取完信息后,重新向隊列中添加一個讀取請求,不然當收到信息后不會執行讀取回調方法。[self.clientSockfd readDataWithTimeout:-1 tag:0];NSString *strMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];[self showLogMessage:[NSString stringWithFormat:@"recv:%@", strMsg]];}#pragma mark - 發送數據- (IBAction)sendBtnClick:(id)sender {// Socket 發送數據[self.clientSockfd writeData:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];[self showLogMessage:[NSString stringWithFormat:@"send:%@", _msgTF.text]];}// 顯示信息- (void)showLogMessage:(NSString *)msg {_logTV.text = [_logTV.text stringByAppendingFormat:@"%@\n", msg];}// 鍵盤回收- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self.view endEditing:YES];}@end
2.2 Server 服務端
TCP 服務端
#import <arpa/inet.h>#import <ifaddrs.h>#import "GCDAsyncSocket.h"@interface ViewController () <GCDAsyncSocketDelegate>@property (weak, nonatomic) IBOutlet UITextField *addrTF;@property (weak, nonatomic) IBOutlet UITextField *portTF;@property (weak, nonatomic) IBOutlet UITextField *msgTF;@property (weak, nonatomic) IBOutlet UITextView *logTV;@property (nonatomic, strong) GCDAsyncSocket *serverSocketfd;@property (nonatomic, strong) GCDAsyncSocket *clientSocketfd;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];NSString *ip_addr = [self getIPAddress];_addrTF.text = ip_addr;// 創建 Socket,在主隊列中處理,所有的回執都在主隊列中執行。self.serverSocketfd = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];}- (IBAction)listenBtnClick:(id)sender {NSError *error = nil;// Socket 監聽[self.serverSocketfd acceptOnPort:_portTF.text.intValue error:&error];if (error != nil) {NSLog(@"監聽出錯:%@", error);} else{[self showLogMessage:@"正在監聽..."];}}#pragma mark - GCDAsyncSocketDelegate 協議方法// 接收到連接請求,協議方法- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket {// 讀取數據,必須添加,相當于主動添加一個讀取請求,不然不會執行讀取信息回調方法[newSocket readDataWithTimeout:-1 tag:0];[self showLogMessage:@"收到客戶端連接...."];[self showLogMessage:[NSString stringWithFormat:@"客戶端地址:%@, 端口:%d", newSocket.connectedHost, newSocket.connectedPort]];self.clientSocketfd = newSocket;}// 已經斷開鏈接,協議方法- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {[self showLogMessage:@"socket 斷開連接..."];}// 讀取到數據,協議方法- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {// 讀取數據,讀取完信息后,重新向隊列中添加一個讀取請求,不然當收到信息后不會執行讀取回調方法。[sock readDataWithTimeout:-1 tag:0];NSString *strMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];[self showLogMessage:[NSString stringWithFormat:@"recv:%@",strMsg]];}#pragma mark - 發送數據- (IBAction)sendBtnClick:(id)sender {// Socket 發送數據[self.clientSocketfd writeData:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];[self showLogMessage:[NSString stringWithFormat:@"send:%@", _msgTF.text]];}#pragma mark - 獲取本地 IP 地址- (NSString *)getIPAddress {NSString *address = @"error";struct ifaddrs *interfaces = NULL;struct ifaddrs *temp_addr = NULL;int success = 0;// retrieve the current interfaces - returns 0 on successsuccess = getifaddrs(&interfaces);if (success == 0) {// Loop through linked list of interfacestemp_addr = interfaces;while (temp_addr != NULL) {if (temp_addr->ifa_addr->sa_family == AF_INET) {// Check if interface is en0 which is the wifi connection on the iPhoneif ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {// Get NSString from C Stringaddress = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];}}temp_addr = temp_addr->ifa_next;}}// Free memoryfreeifaddrs(interfaces);return address;}// 顯示信息- (void)showLogMessage:(NSString *)msg {_logTV.text = [_logTV.text stringByAppendingFormat:@"%@\n", msg];}// 鍵盤回收- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self.view endEditing:YES];}@end
2.3 UDP 通信
UDP 通信
#import <arpa/inet.h>#import <ifaddrs.h>#import "GCDAsyncUdpSocket.h"@interface ViewController () <GCDAsyncUdpSocketDelegate>@property (weak, nonatomic) IBOutlet UITextField *desAddrTF;@property (weak, nonatomic) IBOutlet UITextField *desPortTF;@property (weak, nonatomic) IBOutlet UITextField *locAddrTF;@property (weak, nonatomic) IBOutlet UITextField *locPortTF;@property (weak, nonatomic) IBOutlet UITextField *msgTF;@property (weak, nonatomic) IBOutlet UITextView *logTV;@property (nonatomic, strong) GCDAsyncUdpSocket *udpSocketfd;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];NSString *ip_addr = [self getIPAddress];_locAddrTF.text = ip_addr;// 創建 Socket,在主隊列中處理,所有的回執都在主隊列中執行self.udpSocketfd = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];}- (IBAction)connectBtnClick:(id)sender {NSError *error = nil;// 綁定端口[self.udpSocketfd bindToPort:_locPortTF.text.intValue error:&error];if (error != nil) {NSLog(@"綁定端口出錯:%@", error);return;} else{[self showLogMessage:[NSString stringWithFormat:@"綁定端口 %d 成功...", _locPortTF.text.intValue]];}// 開始接收數據[self.udpSocketfd beginReceiving:&error];if (error != nil) {NSLog(@"開始接收數據出錯:%@", error);} else{[self showLogMessage:@"開始接收數據..."];}}#pragma mark - GCDAsyncUdpSocketDelegate 協議方法// 接收到的數據,協議方法- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)datafromAddress:(NSData *)addresswithFilterContext:(id)filterContext {NSString *strMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];[self showLogMessage:[NSString stringWithFormat:@"recv:%@", strMsg]];}#pragma mark - 發送數據- (IBAction)sendBtnClick:(id)sender {// Socket 發送數據[self.udpSocketfd sendData:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding]toHost:_desAddrTF.textport:_desPortTF.text.intValuewithTimeout:30 tag:0];[self showLogMessage:[NSString stringWithFormat:@"send:%@", _msgTF.text]];}#pragma mark - 獲取本地 IP 地址- (NSString *)getIPAddress {NSString *address = @"error";struct ifaddrs *interfaces = NULL;struct ifaddrs *temp_addr = NULL;int success = 0;// retrieve the current interfaces - returns 0 on successsuccess = getifaddrs(&interfaces);if (success == 0) {// Loop through linked list of interfacestemp_addr = interfaces;while (temp_addr != NULL) {if (temp_addr->ifa_addr->sa_family == AF_INET) {// Check if interface is en0 which is the wifi connection on the iPhoneif ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {// Get NSString from C Stringaddress = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];}}temp_addr = temp_addr->ifa_next;}}// Free memoryfreeifaddrs(interfaces);return address;}// 顯示信息- (void)showLogMessage:(NSString *)msg {_logTV.text = [_logTV.text stringByAppendingFormat:@"%@\n", msg];}// 鍵盤回收- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self.view endEditing:YES];}@end
2.4 Socket 長連接
PublicTool.h
@interface PublicTool : NSObject// 字典轉換成 JSON 字符串+ (NSString *)JSONStringWithDic:(NSDictionary *)dic;// JSON 字符串轉換成字典+ (NSDictionary *)dictionaryWithJSON:(NSString *)json;@endPublicTool.m
@implementation PublicTool// 字典轉成成 JSON 字符串+ (NSString *)JSONStringWithDic:(NSDictionary *)dic {NSError *error = nil;NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dicoptions:0error:&error];if (jsonData == nil) {NSLog(@"fail to get JSON from dictionary: %@, error: %@", self, error);return nil;}NSString *jsonString = [[NSString alloc] initWithData:jsonDataencoding:NSUTF8StringEncoding];return jsonString;}// JSON 字符串轉換成字典+ (NSDictionary *)dictionaryWithJSON:(NSString *)json {NSError *error = nil;NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding];NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonDataoptions:NSJSONReadingMutableContainers |NSJSONReadingAllowFragmentserror:&error];if (jsonDict == nil) {NSLog(@"fail to get dictioanry from JSON: %@, error: %@", json, error);return nil;}return jsonDict;}@endSocketSingleTon.h
@interface SocketSingleTon : NSObject@property (nonatomic, copy) NSString *hostAddr;@property (nonatomic, copy) NSString *port;@property (nonatomic, copy) void(^msgLog)(NSString *);+ (instancetype)shareInstance;// 連接到服務器- (void)connectToServer;// 斷開連接- (void)cutOffSocket;// 發送數據到服務器- (void)sendDataToServer:(NSData*)data;@endSocketSingleTon.m
#import "GCDAsyncSocket.h"#import <netdb.h>#import <arpa/inet.h>#import "PublicTool.h"typedef NS_ENUM(NSInteger, SocketOffline) {SocketOfflineByServer,SocketOfflineByUser};@interface SocketSingleTon () <GCDAsyncSocketDelegate>@property (nonatomic, strong) GCDAsyncSocket *socket;@property (nonatomic, strong) NSTimer *beatTimer;@end@implementation SocketSingleTon+ (instancetype)shareInstance {static SocketSingleTon *shareInstance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{shareInstance = [[self alloc] init];});return shareInstance;}// 連接到服務器- (void)connectToServer {NSError *error = nil;if (self.socket != nil) {if ([self.socket isConnected]) {// 斷開后再連接self.socket.userData = @(SocketOfflineByUser);[self cutOffSocket];[self.socket connectToHost:self.hostAddr onPort:self.port.intValue error:&error];} else {[self.socket connectToHost:self.hostAddr onPort:self.port.intValue error:&error];}} else {self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];[self.socket connectToHost:self.hostAddr onPort:self.port.intValue error:&error];}if (error != nil) {NSLog(@"socket 連接失敗:%@", error);} else {NSLog(@"socket 連接成功");}}// 斷開連接- (void)cutOffSocket {self.socket.userData = @(SocketOfflineByUser);[self.socket disconnect];}#pragma mark - GCDAsyncSocketDelegate 協議方法// 連接成功,協議方法- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {[sock readDataWithTimeout:-1 tag:0];NSString *logStr = [NSString stringWithFormat:@"連接主機:%@:%d 成功\n", host, port];NSLog(@"%@", logStr);if (self.msgLog) {self.msgLog(logStr);}// 創建定時器,定時發送 beat 包self.beatTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(longConnectToServer) userInfo:nil repeats:YES];}// 連接斷開,協議方法- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {self.socket = nil;[self.beatTimer invalidate];self.beatTimer = nil;if ([sock.userData isEqual: @(SocketOfflineByUser)]) {NSLog(@"the socket have been cutted off by user");if (self.msgLog) {self.msgLog(@"the socket have been cutted off by user");}} else if (sock.userData == SocketOfflineByServer) {NSLog(@"the socket have been cutted off by server");if (self.msgLog) {self.msgLog(@"the socket have been cutted off by server");}// reconnect[self connectToServer];} else {NSLog(@"%@", err.localizedDescription);if (self.msgLog) {self.msgLog([NSString stringWithFormat:@"%@", err.localizedDescription]);}[self connectToServer];}}// 收到消息,協議方法- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {[sock readDataWithTimeout:-1 tag:0];char buf[1024];[data getBytes:buf length:1024];NSString *receivedData = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];NSLog(@"receivedData:%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);if (receivedData.length > 0) {NSDictionary *dataDic = [PublicTool dictionaryWithJSON:receivedData];if ([dataDic[@"msgType"] isEqualToString:@"beta"]) {NSString *strMsg = [NSString stringWithFormat:@"收到心跳確認的數據:%@\n", receivedData];if (self.msgLog) {self.msgLog(strMsg);}} else if ([dataDic[@"msgType"] isEqualToString:@"normal"]) {NSString *strMsg = [NSString stringWithFormat:@"收到正常的數據:%@\n", receivedData];if (self.msgLog) {self.msgLog(strMsg);}} else if ([dataDic[@"msgType"] isEqualToString:@"exit"]) {NSString *strMsg = [NSString stringWithFormat:@"收到關閉的數據:%@\n", receivedData];if (self.msgLog) {self.msgLog(strMsg);}[self cutOffSocket];}}}// 發送數據- (void)longConnectToServer {[self sendDataToServer:[@"hello" dataUsingEncoding:NSUTF8StringEncoding]];}// 發送數據到服務器- (void)sendDataToServer:(NSData*)data {NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];NSMutableDictionary *dicParams = [NSMutableDictionary dictionary];if ([dataStr isEqualToString:@"hello"]) {[dicParams setValue:dataStr forKey:@"msg"];[dicParams setValue:@"beta" forKey:@"msgType"];} else {[dicParams setValue:dataStr forKey:@"msg"];[dicParams setValue:@"normal" forKey:@"msgType"];}NSData *sendData = [[PublicTool JSONStringWithDic:dicParams] dataUsingEncoding:NSUTF8StringEncoding];NSString *strMsg = [NSString stringWithFormat:@"發送數據: %@\n", [PublicTool JSONStringWithDic:dicParams]];if (self.msgLog) {self.msgLog(strMsg);}[self.socket writeData:sendData withTimeout:30 tag:0];}@endViewController.m
#import "SocketSingleTon.h"#import "PublicTool.h"@interface ViewController ()@property (weak, nonatomic) IBOutlet UITextField *addressTF;@property (weak, nonatomic) IBOutlet UITextField *portTF;@property (weak, nonatomic) IBOutlet UITextField *msgTF;@property (weak, nonatomic) IBOutlet UITextView *logTV;@end@implementation ViewController- (IBAction)connectToServer:(id)sender {// 創建 SocketSocketSingleTon *socketInstance = [SocketSingleTon shareInstance];socketInstance.hostAddr = _addressTF.text;socketInstance.port = _portTF.text;__weak __typeof (self)weakSelf = self;socketInstance.msgLog = ^(NSString *log){dispatch_async(dispatch_get_main_queue(), ^{weakSelf.logTV.text = [weakSelf.logTV.text stringByAppendingString:log];});};// 連接到服務器[socketInstance connectToServer];}- (IBAction)cutOffConnect:(id)sender {SocketSingleTon *socketInstance = [SocketSingleTon shareInstance];// 斷開連接[socketInstance cutOffSocket];}- (IBAction)sendDataToServer:(id)sender {SocketSingleTon *socketInstance = [SocketSingleTon shareInstance];// 發送數據到服務器[socketInstance sendDataToServer:[_msgTF.text dataUsingEncoding:NSUTF8StringEncoding]];}- (IBAction)sendBetaDataToServer:(id)sender {SocketSingleTon *socketInstance = [SocketSingleTon shareInstance];NSMutableDictionary *dicParams = [NSMutableDictionary dictionary];[dicParams setValue:@"beta" forKey:@"msgType"];[dicParams setValue:@"hello" forKey:@"msg"];NSString *strMsg = [PublicTool JSONStringWithDic:dicParams];// 發送心跳數據到服務器[socketInstance sendDataToServer:[strMsg dataUsingEncoding:NSUTF8StringEncoding]];}- (IBAction)clearLog:(id)sender {_logTV.text = nil;}// 鍵盤回收- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self.view endEditing:YES];}@end
轉載于:https://www.cnblogs.com/QianChia/p/6392265.html
總結
以上是生活随笔為你收集整理的iOS - AsyncSocket 的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 写一个脚本,判断下如果是阴历7月7日,在
- 下一篇: 掌握jQuery插件开发,这篇文章就够了