ios客戶端socket通訊(二)

前言

寫上一篇文章之前完全是想總結(jié)一下自己對(duì)socket通訊流程的總結(jié),加深自己的印象,沒有想到會(huì)有很多人關(guān)注這一塊,再說自己是個(gè)socket的新手,寫出來的文章沒有多少人去看,所以上一篇文章寫得不是很詳細(xì),但是發(fā)現(xiàn)還是有很多人關(guān)注,所以我就趁今天是個(gè)周末,花個(gè)2,3小時(shí)來總結(jié)一下本周socket通訊的進(jìn)程,供想要了解的朋友參考。

正文

首先提一下網(wǎng)上CocoaAsyncSocket框架主要包括AsyncSocketGCDSocket,我使用的是后者,就是多線程Socket,主要區(qū)別就是前者基于NSRunLoop,后者是在多線程進(jìn)行,據(jù)我項(xiàng)目目前的情況來看,GCDSocket內(nèi)至少開辟了5個(gè)線程。
自己項(xiàng)目目前的進(jìn)度是將socket單獨(dú)封裝成一個(gè)單獨(dú)的類,也就是寫成一個(gè)單例類,這樣寫的好處顯而易見,這樣我們建立通訊連接,數(shù)據(jù)請(qǐng)求就方便了很多,因?yàn)槲覀儾豢赡苋ッ恳粋€(gè)需要數(shù)據(jù)的界面去創(chuàng)建socket進(jìn)行連接,想辦法把問題簡單化使我們程序員必須做的重要一部分。想必網(wǎng)上關(guān)于socket的文章大多數(shù)都是大家你抄我我抄你而來,寫一下socket創(chuàng)建,建立連接,實(shí)現(xiàn)代理方法,收發(fā)數(shù)據(jù),沒有更深一步的文章。其次大家有沒有這樣的一個(gè)疑惑,網(wǎng)上為什么沒有開源的關(guān)于socket通訊的集成好的第三方框架供我使用呢?我直接收發(fā)數(shù)據(jù)就好了,還要那么麻煩建立連接,一堆問題去處理。像普通的網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求,網(wǎng)上有封裝好的AFNetworking,為什么socket沒有!??!那我來告訴你基于socket的TCP的長連接往往數(shù)據(jù)傳輸協(xié)議是自定義的,所以這個(gè)不可能有現(xiàn)成的框架來用,必須根據(jù)自己的定義類型來收發(fā)數(shù)據(jù),否則就無法解析。舉個(gè)例子,我收數(shù)據(jù)需要這樣的格式[^1^2],那當(dāng)我收到數(shù)據(jù)data我必須得按這種格式校驗(yàn),否則我就無法收到。上面首先將socket的邏輯說清楚,下面我們上代碼,首先說明一點(diǎn),我這個(gè)單例封裝的我個(gè)人覺得比較完美,跟普通網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)那種格式一某一樣,最大的不同我是采用代理的方式回傳數(shù)據(jù),而不是block。

#import "GCDAsyncSocket.h"

@protocol GPSSocketServeDelegate <NSObject>

/*** 連接服務(wù)器成功以后回調(diào)  */
- (void)connectSeverSucess:(NSString *)sucess;

/***  登錄返回判斷 */
- (void)ClickIsSucess:(BOOL)isSucess StrParam2:(NSString *)strParam2;

/***  返回請(qǐng)求數(shù)據(jù) */
- (void)ClintReceCommData:(NSMutableArray *)data StrDataType:(NSString *)strDataType strParam2:(NSString *)strParam2;

@end


@class GPSSocketServeDelegate;
@interface GPSSocketServe : NSObject <GCDAsyncSocketDelegate>


@property (nonatomic,weak) id<GPSSocketServeDelegate>delegate;
@property (nonatomic,strong) GCDAsyncSocket *socket;

//在.h文件里面我給出了以下6個(gè)接口,建立連接,斷開連接,其他的就是請(qǐng)求數(shù)據(jù)接口的封裝

/***  獲取本類對(duì)象 */

+ (GPSSocketServe *)sharedSocketServe;

/***  socket連接 */

- (void)startConnectSocket;


/***  斷開socket開始連接 */

- (void)disConnectSocket;


/**
 *  登錄接口
 *
 *  @param username 用戶名
 *  @param password 用戶密碼
 */
- (void)userClick:(NSString *)username UserPassword:(NSString *)password;

/**
 *  用戶登錄調(diào)第一集部門表
 *
 *  @param p_strManagerCode 為用戶的最高部門code
 *  @param p_strWGLoginName 用戶名稱
 */
- (void)requestManagerDep:(NSString *)p_strManagerCode P_strWGLoginName:(NSString *)p_strWGLoginName;

/**
 *  用戶調(diào)第二級(jí)以及以后的部門表
 *
 *  @param p_strManagerCode    為當(dāng)前級(jí)的部門code
 *  @param p_strCurrentDepName 為當(dāng)前級(jí)的部門名稱
 */
- (void)requestSencondManagerDep:(NSString *)p_strCurrentDepCode P_strCurrentDepName:(NSString *)p_strCurrentDepName;

/**
 *  調(diào)查詢部門下的車輛列表
 *
 *  @param p_strManagerCode    為當(dāng)前級(jí)的部門code
 *  @param p_strCurrentDepName 為當(dāng)前級(jí)的部門名稱
 */
- (void)requestCarsOfDep:(NSString *)p_strCurrentDepCode P_strCurrentDepName:(NSString *)p_strCurrentDepName;

GPSSocketServe.m文件,接口的實(shí)現(xiàn)
//創(chuàng)建單例對(duì)象,重寫allocWithZone方法,保證這個(gè)對(duì)象在內(nèi)存中只有一份
+ (GPSSocketServe *)sharedSocketServe{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        socketServe = [[self alloc] init];
    });

    return socketServe;
}

+(id)allocWithZone:(NSZone *)zone
{
    @synchronized(self)
    {
        if (socketServe == nil)
        {
            socketServe = [super allocWithZone:zone];
            return socketServe;
        }
    }
    return nil;
}
- (void)startConnectSocket
{
 //這個(gè)里面主要就是創(chuàng)建socket對(duì)象,建立連接,代碼就不考了,可以參照上一篇


}

然后我說說我主要遇到的問題吧
1.原始的收到數(shù)據(jù)和我發(fā)送數(shù)據(jù)是在兩個(gè)不同的方法里面,我們?nèi)绾位貍鲾?shù)據(jù)?
2.當(dāng)我調(diào)用一級(jí)部門表的時(shí)候,發(fā)現(xiàn)收不到回傳數(shù)據(jù),然后一步一步的調(diào),發(fā)現(xiàn)我發(fā)送的數(shù)據(jù)少5個(gè)字符長度,這個(gè)哪里出了問題?
關(guān)于第一個(gè)問題,其實(shí)我第一個(gè)感覺就是使用block,這樣呢回傳數(shù)據(jù)感覺很方便,例如當(dāng)我發(fā)送登陸,就可以收到回調(diào)成功然后進(jìn)行跳轉(zhuǎn),當(dāng)時(shí)我已經(jīng)成功實(shí)現(xiàn)了block回傳數(shù)據(jù),大概寫一下實(shí)現(xiàn)的方法

//定義一個(gè)全局的block
//原因:發(fā)送請(qǐng)求和收到數(shù)據(jù)的不在一個(gè)方法里面
//定義一個(gè)block,申明一個(gè)屬性,這個(gè)block帶兩個(gè)參數(shù),一個(gè)是類型的字符串,另一個(gè)便是回傳數(shù)據(jù)
typedef void(^requestDataBlock)(NSString *string,NSMutableArray *data);
@property (nonatomic,copy) requestDataBlock dataBlock;
//發(fā)送數(shù)據(jù)的同一接口
- (void)ClintSendCommData:(short)intDataType strDataType:(NSString *)strDataType stSetType:(NSString *)stSetType strSetSN:(NSString *)strSetSN strSetSN1:(NSString *)strSetSN1 strAlmComType:(NSString *)strAlmComType strHisType:(NSString *)strHisType strPosType:(NSString *)strPosType strFadeType:(NSString *)strFadeType strRecogType:(NSString *)strRecogType strRecogType1:(NSString *)strRecogType1 StrParam1:(NSString *)strParam1 StrParam2:(NSString *)strParam2 StrParam3:(NSString *)strParam3 StrParam4:(NSString *)strParam4 StrParam5:(NSString *)strParam5 StrParam6:(NSString *)strParam6 StrParam7:(NSString *)strParam7 StrParam8:(NSString *)strParam8

//在這個(gè)方法后面添加上block `success:(^requestDataBlock)(NSString *string,NSMutableArray *data)`
//在這個(gè)方法里面self.dataBlock = requestDataBlock;
//再接收數(shù)據(jù)的方法里面回調(diào)block
self.dataBlock(參數(shù)一,返回?cái)?shù)據(jù));

這樣的話就實(shí)現(xiàn)了block回調(diào)數(shù)據(jù),比較容易,前提是你對(duì)block足夠的了解。
可是為什么我放棄了這個(gè)方法了,因?yàn)?.代碼的冗余率太多,因?yàn)槭嵌嗑€程,當(dāng)我更新UI我必須回到主線程,這個(gè)代碼得多大一塊;而且2.可擴(kuò)展性不好,這個(gè)登錄接口需要回傳一個(gè)參數(shù),調(diào)用需要回傳多個(gè)參數(shù),這樣共用性不好,所以我就想到了代理,不同的接口我調(diào)用不同的代理方法,以后再有新的類型回傳數(shù)據(jù),我大不了再寫一個(gè)回傳接口而已?;卣{(diào)函數(shù)請(qǐng)看上面。

//這個(gè)就是后臺(tái)提供給我的登錄接口,這寫也是用Java寫的,不過我已經(jīng)將他們改成oc的方法
ClintSendCommData(1105, "0002", "", "", "", "", "", "", "", "", "", p_strWGLoginName,p_strWGPassword, "", "", "", "", "", "");

大家一看這調(diào)用接口,需要傳的參數(shù)就兩個(gè),所以你們想到了什么?反正我想到的是再封裝一層

- (void)userClick:(NSString *)username UserPassword:(NSString *)password
{
    [self ClintSendCommData:1105 strDataType:@"0002" stSetType:@"" strSetSN:@"" strSetSN1:@"" strAlmComType:@"" strHisType:@"" strPosType:@"" strFadeType:@"" strRecogType:@"" strRecogType1:@"" StrParam1:username StrParam2:password StrParam3:@"" StrParam4:@"" StrParam5:@"" StrParam6:@"" StrParam7:@"" StrParam8:@""];
}

這樣我就調(diào)用這個(gè)方法就OK,其他的調(diào)用數(shù)據(jù)的方法類似。

第二個(gè)問題出在什么地方呢?其實(shí)還是跟上一篇文章編碼有關(guān),后臺(tái)服務(wù)器采用的是GB2312,我將它轉(zhuǎn)換為UTF-8了,在發(fā)送數(shù)據(jù)的時(shí)候

//自定義發(fā)送數(shù)據(jù)接口,底層其實(shí)是調(diào)用發(fā)送數(shù)據(jù)的方法,writedata:,我只不過封裝了一層
[self SendData:intDataType CharDatahead:(char *)[strData cStringUsingEncoding:enc] DataLen:intDataLen];

前面提到,我發(fā)送時(shí)候少了5個(gè)字符,這是跟我發(fā)送的里面包含了漢字,漢字的一般占用2個(gè)字符,而我們普通計(jì)算這個(gè)長度length當(dāng)做一個(gè)字符來算,所以intDataLen計(jì)算是不對(duì)的,登錄接口之所以對(duì),那是因?yàn)闆]有漢字,我首先在計(jì)算長度的NSString的方法里面沒有找到相應(yīng)的方法,不知道有沒有朋友找到,有找到的可以留言給我,非常感謝!那我說說我在網(wǎng)上找到的計(jì)算包含漢字的方法(其實(shí)這個(gè)方法是經(jīng)過我改造過的方法)

/**
 *  計(jì)算包含中文的字符的字符串長度
 */
-(int)lengthOfStringContainChinese:(NSString*)c{
    
    int strlength = 0;
    char* p = (char*)[c cStringUsingEncoding:NSUnicodeStringEncoding];
    
    for (int i=0 ; i<[c lengthOfBytesUsingEncoding:NSUnicodeStringEncoding] ;i++) {
        if (*p) {
            p++;
            strlength++;
        }
        else {
            p++;
        }
    }
    return strlength;
}

這樣計(jì)算就正確了。
以上是我在解決的主要問題,其實(shí)我在真機(jī)調(diào)試的時(shí)候,發(fā)現(xiàn)了一個(gè)比較嚴(yán)重的問題,在網(wǎng)絡(luò)請(qǐng)求的時(shí)候,網(wǎng)絡(luò)不好,會(huì)出現(xiàn)嚴(yán)重的內(nèi)存暴增,程序就會(huì)閃退,不過這個(gè)問題我已經(jīng)解決掉了,通過的是調(diào)試工具,這個(gè)下一期我會(huì)教大家調(diào)試的方法,首先科普一下,手機(jī)內(nèi)存一般達(dá)到30M的話就會(huì)自動(dòng)閃退,有遇到這個(gè)問題的朋友可以仔細(xì)研究研究。

簡易的程序運(yùn)行
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • iPhone的標(biāo)準(zhǔn)推薦是CFNetwork 庫編程,其封裝好的開源庫是 cocoa AsyncSocket庫,用它...
    Ethan_Struggle閱讀 2,361評(píng)論 2 12
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,628評(píng)論 30 472
  • iOS網(wǎng)絡(luò)架構(gòu)討論梳理整理中。。。 其實(shí)如果沒有APIManager這一層是沒法使用delegate的,畢竟多個(gè)單...
    yhtang閱讀 5,493評(píng)論 1 23
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,819評(píng)論 11 349
  • 春天來了,一對(duì)燕子夫婦從南方飛回來,在豪豪家屋檐下筑巢,當(dāng)天就下了五個(gè)蛋,燕媽媽每天窩在巢里孵小燕子呢。 豪豪早就...
    亦諾1閱讀 241評(píng)論 2 11

友情鏈接更多精彩內(nèi)容