因為項目的要求是全局的socket, 哪里都有可能使用到socket去發消息, 所以我把socket寫在了單利里面
項目用的是 pod 'CocoaAsyncSocket' 三方庫, 是異步的, 如果沒有cocopods 那就去guthub下載一個
特別需要注意一點, 如果服務器一下連着發了好幾條數據, 消息會阻塞. 明確來說是大部分的文章在發出一條數據之后只調用了一次[sock readDataWithTimeout:-1 tag:0]; 這個方法去手動接收. 類似於發一條才能收一條這種概念,所以我在每收到一條數據處理完后再次調用一次這個方法
1.建立單例類 (單利還不會的,那真的要去好好查了, 這里的單例可能說的不太詳細)
.h文件
#import <Foundation/Foundation.h>
//導入頭文件
#import "GCDAsyncSocket.h"
//遵循代理
@interface Singleton : NSObject<GCDAsyncSocketDelegate>
//全局socket
@property(nonatomic,strong) GCDAsyncSocket *socket;
//單例創建方法
//此類方法, 不管調用多少次. 都只會使用一個, 單利請謹慎使用
+(Singleton *)shareSingleton;
//socket連接
- (void)connectToServerWithHost:(NSString *)host AndPort:(int)port;
//socket發送消息
- (void)sendMassageWithData:(NSString *)data;
@end
.m文件
static Singleton * shareS = nil;
//單例實現方法
+ (Singleton *)shareSingleton{
if (shareS == nil) {
shareS = [[Singleton alloc]init];
}
return shareS;
}
//實現.h連接方法
- (void)connectToServerWithHost:(NSString *)host AndPort:(int)port{
//這個方法在下面 , host 是后台給的服務器地址, port是端口
[self StartLiveBtnWithHost:host AndPort:port];
}
//實現發送消息方法
- (void)sendMassageWithData:(NSString *)str{
//這個方法是CocoaAsyncSocket 的方法, str是其他地方調用的時候傳來, 注意: 如果后台對socket消息有不同的要求,那么要溝通好, 比如消息頭,消息體之類的
NSData * data = [str dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:data withTimeout:1 tag:200];
}
#pragma mark 建立Socket連接
- (void)StartLiveBtnWithHost:(NSString *)host AndPort:(int )port{
NSLog(@"建立長連接");
//getProperIPWithAddress 是針對ipv6后, 做的處理 , 別急, 在下面
NSString * ipaddr = [self getProperIPWithAddress:host port:port];
//創建一個socket對象
GCDAsyncSocket * socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
//連接
NSError *error = nil;
[socket connectToHost:ipaddr onPort:port error:&error];
self.socket = socket;
if (error) {
NSLog(@"%@",error);
}
}
//針對ipv6網絡環境下適配,ipv4環境直接使用原來的地址
- (NSString *)getProperIPWithAddress:(NSString *)ipAddr port:(UInt32)port
{
NSError *addresseError = nil;
NSArray *addresseArray = [GCDAsyncSocket lookupHost:ipAddr
port:port
error:&addresseError];
if (addresseError) {
NSLog(@"");
}
NSString *ipv6Addr = [[NSString alloc]init];
for (NSData *addrData in addresseArray) {
if ([GCDAsyncSocket isIPv6Address:addrData]) {
ipv6Addr = [GCDAsyncSocket hostFromAddress:addrData];
}
}
if (ipv6Addr.length == 0) {
ipv6Addr = ipAddr;
}
return ipv6Addr;
}
#pragma mark 連接成功
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{
NSLog(@"%s",__func__);
//只要走了這個代理方法, 就說明連接已經成功
NSLog(@"連接成功");
}
#pragma mark 斷開連接
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
//這里有兩種情況,網絡不暢時間太長連接失敗, 或者用戶退出正常斷開, 我這里做了斷開提示用戶,並且相應的斷線重連
if (err) {
NSLog(@"連接失敗");
[self.socket disconnect];
//彈出提示框;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"網絡不暢斷開連接,請檢查重新連接" preferredStyle: UIAlertControllerStyleAlert];
UIAlertAction * action1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}];
UIAlertAction * action2 = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//這里就是重新調一下socket連接的方法, 以及一些其他操作,根據自己的需求來
[HttpRequest ReconnectOnloss];
}];
[alert addAction:action1];
[alert addAction:action2];
//彈出提示框;
[[self appRootViewController] presentViewController:alert animated:true completion:nil];
}
}else{
NSLog(@"正常斷開");
[self.timer invalidate];
[self.socket disconnect];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"網絡不暢斷開連接,請檢查重新連接" preferredStyle: UIAlertControllerStyleAlert];
UIAlertAction * action1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}];
UIAlertAction * action2 = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//這里就是重新調一下socket連接的方法, 以及一些其他操作,根據自己的需求來
[HttpRequest ReconnectOnloss];
}];
[alert addAction:action1];
[alert addAction:action2];
//彈出提示框;
[[self appRootViewController] presentViewController:alert animated:true completion:nil];
}
}
#pragma mark 數據發送成功
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
NSLog(@"%s",__func__);
//發送完數據手動讀取,-1不設置超時
[sock readDataWithTimeout:-1 tag:0];
}
#pragma mark 讀取數據
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
//這里的data,就是實時收到的后台發來的消息, 如果服務器用了各種方式的加密, 還需要跟后台人員及時溝通
NSLog(@"-------------data:%@",data);
NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"--------------%@",receiverStr);
//在這里,或者在你的消息處理類里面調用, 這樣就不用發, 也可以實時一直收到數據
[sock readDataWithTimeout:-1 tag:0];
}
--------------------------------------------------------------------------------
//順便加上調用發消息
//如果在單例里面發
[self sendMassageWithData:[NSString stringWithFormat:@"8014,%@",dic[@"mRoleID"]]];
//如果在其他類發
[[Singleton shareSingleton] sendMassageWithData:[NSString stringWithFormat:@"8014,%@",dic[@"mRoleID"]]];
//如果有哪里不對的地方請多多包涵,共同研究, 可能有些括號不全之類的.