標籤:127.0.0.1 unsigned list 表示 系統 無法 input isp get
寫在準備動手的時候:
Socket通訊在iOS中也是很常見,自己最近也一直在學習Telegram這個開源項目,Telegram就是在Socket的基礎上做的即時通訊,這個相信瞭解這個開源項目的也都知道,希望自己能慢慢的瞭解一下它的這個MtProtoKit開源協議,即時通訊這一塊的東西我以前寫過一篇《iOS 即時通訊 + 仿聊天架構 + 源碼》,從點擊量看的出來真的這一塊的東西我們的需求量還是很大,《iOS 即時通訊 + 仿聊天架構 + 源碼》這篇文章由於自己去年也是能力有限,現在我自己去看也會覺得很多地方不怎麼盡如人意,接下來Socket可以說這一個系列的自己準備認認真真的寫下去,不能急於求成,希望能認真紮實的把這系列的東西總結一下,至少讓自己覺得總結的全面一點。
Socket是什嗎?
我們從聊天的一些常用的協議開始慢慢的理解這個Socket,不想在這了直接拋出Socket的概念,在腦海中有這個問題就行!後面我們會慢慢的一步一步找到答案!在這裡先給出兩個連結:
《iOS即時通訊,從入門到“放棄”?》這篇文章細緻的說了iOS即時通訊這一塊作者自己理解和總結,能很好的協助我們理解即時通訊,只是篇幅有點長,要看完全得花點時間。
《關於iOS socket都在這裡了》這篇文章我看過之後也覺得很不錯,上面說的文章要是能幫我們理解即時通訊,那這篇文章就能幫我們消化一下Socket。
上面這兩篇文章我相信會對我們協助會很大,所以是強烈推薦!結合這兩篇內容,我們開始慢慢的總結Socket,在文章中所有的練習Demo以及詳細的一些整合過程Demo都會給大家。過了一年,再問一下自己到底該怎樣實現即時通訊?
(1)第三方IM服務
第三方能做即時通訊的是在是太多了,環信,融雲,網易雲信等等....說說我自己的理解:
1、他們的技術的確沒問題,是OK的。要是你公司整個Team Dev技術實力一般(普通公司的整個團隊技術其實實力真的一般,包括我自己待過的一些公司),還有以前你公司也沒有自己做過即時通訊並且還有一點就是想速成,那我就建議使用第三方!這一點說說我自己的親身經曆感覺會更真實一點,我待的第一家公司,項目有即時通訊的需求,我們是利用Socket來做,做到最後還是沒有做出一個讓人滿意的即時通訊,訊息的丟失、連結狀態的不穩定,剛開始的時候也根本還沒怎麼考慮過資訊安全這些問題,第二家有了第一次的前車之鑒再結合整個團隊的實力水平,選擇利用環信做,整體感覺比第一次好了許多,等到現在接觸到Telegram,才覺得一般的公司還是推薦使用第三方,那樣至少能保證你這個功能是沒問題的。
2、第三方的弊端,收費!定製化程度不是完全掌握在你的手裡,收費這一點,像網易這些在開始就明確了價格怎樣供你選擇,像環信這種,會在某一個適當的時機,當你使用者積累到一定量的時候開始收費。這個成本在開發的時候是必須需要考慮的。
3、沒辦法真正的提高你對即時通訊的理解以及水平的提升。
(2) 有能力自己做
有能力自己做真的肯定是最好,這個時候就需要你去好好認證的學習即時通訊所需要的方方面面,這個過程你的收穫肯定會很大,但困難也肯定會有,需要自己權衡,自己做,我們該怎樣開始?下面就重點總結一下我們自己開始做即時通訊的時候我們應該掌握的東西。
即時通訊自己動手 -- 選擇
這個時候你需要選擇我們使用的傳輸協議: TCP 還是 UDP
當然,你在選擇之前,肯定要知道什麼是TCP?什麼是UDP?(這裡就說一個題外話,在剛開始接觸iOS的時候,經常會看到有些人吐槽,我一個做iOS的,面試的時候常遇到有人問什麼是TCP什麼是UDP,我需要瞭解這些幹嘛?有用嗎?有必要嗎?不知道這樣吐槽的你工作幾年之後會不會覺得那時的你很青澀?這些我們需要掌握的必要性我就不在多提了,我也相信看這篇文章朋友也不會有這樣的想法!)
這個選擇問題,我給到一篇即時通訊網上比較好的文章的結論:文章連結《移動端IM/推送系統的協議選型:UDP還是TCP?》希望需要的朋友認真看看。下面是文章最後給的結論:
認真看看文章,傳輸協議我相信你也可能做一個正確的選擇了,那接下來你還得考慮聊天協議用哪個,所以這一塊的主題就是 -- 選擇
聊天協議這裡總結,比較一些它們之間的優缺點:
上面的總結能協助我們區分這些協議,清楚了這些,我們自己在做即時通訊的時候。最好的選擇還是利用Socket來實現,下面就總結一些自己學習之後對Socket的理解,要是有什麼不對的地方,大家可以指出來。
Socket理解
Socket(中文名:通訊端)是通訊的基石,是支援TCP/IP協議的網路通訊的基本操作單元,Socket本身並不是協議,Socket本質是編程介面(API),是對TCP/IP的封裝。通過Socket,我們能更好的使用TCP/IP 協議,而不是說只有通過Socket我們才能使用TCP/IP協議。要是沒有Socket我們就得直面傳輸層的TCP/IP協議,這個工作量就會更大,難度也會更大!
建立Socket串連至少需要一對通訊端,其中一個運行於用戶端稱為ClientSocket ,另一個運行於伺服器端稱為ServerSocket 。
下面是百度找的Socket使用TCP協議建立串連的一個流程圖:
其實這整個步驟就包括了它的串連,用戶端發送訊息以及讀取訊息,服務端接收訊息和給用戶端發送訊息以及到最後一個中斷連線的請求等等的過程,我們先看看整個串連的過程,也有人總結過大概是下面這樣的幾個步驟:(前面那些初始化過程就不提,這個自己注意就行)
1、伺服器監聽:伺服器端通訊端並不定位具體的用戶端通訊端,而是處於等待串連的狀態(也就是上面的阻塞直到用戶端串連),即時監控網路狀態,等待用戶端的串連請求。
2、用戶端請求:用戶端初始化Socket提出串連請求,要串連的目標是伺服器端的Socket。為此,用戶端的通訊端必須首先描述它要已連線的服務器的通訊端,指出伺服器端通訊端的地址和連接埠號碼,然後就向伺服器端通訊端提出串連請求。(這個過程在你用戶端初始化Socket在串連時候能在加深理解這一步)
3、串連確認:當伺服器端Socket監聽到或者說接收到用戶端Socket的串連請求時,就響應用戶端Socket的請求,建立一個新的線程,把伺服器端Socket的描述發給用戶端,一旦用戶端確認了此描述,雙方就正式建立串連。而伺服器端通訊端繼續處於監聽狀態,繼續接收其他用戶端通訊端的串連請求。
通過上面的過程,你的Socket就和服務端Socket建立了串連!
再補充一下:我們在傳輸資料時,可以使用(傳輸層)TCP/IP協議,但是那樣的話,如果沒有應用程式層,便無法識別資料內容,如果想要使傳輸的資料有意義,則必須使用到應用程式層協議,應用程式層協議有很多,比如我們常說的HTTP(Http是應用程式層的協議,它實際上也建立在TCP協議之上)。Web使用HTTP協議作應用程式層協議,以封裝HTTP文本資訊,然後使用TCP/IP做傳輸層協議將它發到網路上。
在Http和Socket之間,百度上面這個例子也是被很多的部落格所用:Http是轎車,提供了封裝或者顯示資料的具體形式;Socket是發動機,提供了網路通訊的能力。
上面的這些你要理解前面說過的TCP,UDP,網路層級這些東西,理解這個Socket就會容易很多!所以還是建議理解TCP,UDP這些協議,像TCP串連時候的三向交握,斷開時候的四次揮手。它和UDP之間的區別,還有Http的特點這些還是瞭解一下的好,這些我自己以前也是沒有總結過,等寫完這些再好好總結一些這些網路層次以及這些協議。
上面說了Socket的串連之後,隨之我們就再理解一下和它永遠都在一起的幾個問題: 心跳機制 PingPong機制 斷線重連
一: 心跳機制
心跳一般是指某端(絕大多數情況下是用戶端)每隔一定時間向服務端發送自訂指令,以判斷雙方是否存活,因其按照一定間隔發送,類似於心跳一樣,所以大家也就都叫它心跳機制。關於心跳的我們在這裡介紹完之後在後面的Demo中我會把源碼給大家,具體的可以結合源碼一起看看。
Socket選擇TCP傳輸協議建立了串連,這個TCP協議有一個KeepAlive機制,下面文章也是很明確的指出了為什麼不能用TCP協議的KeepAlive機制來做心跳的原因, 總結的觀點是: TCP KeepAlive 是用於檢測串連的死活,而不是用來檢測串連是否可用!而我們的心跳就是為了檢測串連死活的同時還要檢測串連是否可用!
再說說你用心跳保持長串連,那你的心跳方案怎樣設計呢?在參考文章的最下面文章給出了這樣兩個觀點,先看圖:
參考文章 《為什麼說基於TCP的移動端IM仍然需要心跳保活? 》
總結一下上面的兩個減少定時心跳的方法:
1、 考慮定時的時間間隔問題,這個問題其實有時候在面試的時候也會有人問,怎樣去確定心跳計時的時間間隔或者減少心跳等,上面給的第一個就是在計時的時間間隔上考慮。這個具體的時間設定多少,我們下面會仔細說說。
2、從最後收到訊息開始進行周期計時而不是設計固定的時間間隔,這個我自己的理解,收到訊息的時候你做標記計時,要是在某某時間內又有訊息收到,就不進行心跳,要是在某某時間內沒有在收到訊息就進行心跳。當然在你計時時間內你又收到了訊息,這個計時肯定也要重新整理,這個涉及到具體實踐細節。
服務端的和我們上面說的道理是一樣的!
衍生的問題: 這個時間間隔到底你該怎樣去設定? 在我們文章開頭給的推薦文章一種就有比較詳細的解釋這個問題:
具體的原因說到這個 -- NAT 逾時,你要有興趣可以點擊去百度具體瞭解一下什麼是NAT逾時,這裡我們就不在佔據篇幅寫這個,建議還是在我們最前面推薦的文章去瞭解一下這個NAT逾時,說說我們的結果:而國內的電訊廠商一般NAT逾時的時間為5分鐘,所以通常我們心跳設定的時間間隔為3-5分鐘。
二: PingPong機制
這個的出現是為了在我們設定的這個心跳間隔之內出現了串連問題,就像參考文章說的那樣我們在地鐵電梯這些場所當中的時候,那它具體是什嗎?
當服務端發出一個Ping
,用戶端沒有在約定的時間內返迴響應的ack
,則認為用戶端已經不線上,這時我們Server
端會主動斷開Scoket
串連,並且改由APNS
推送的方式發送訊息。
同樣當用戶端去發送一個訊息,因為我們遲遲無法收到服務端的響應ack包,則表明用戶端或者服務端已不線上,我們也會顯示訊息發送失敗,並且斷開
Scoket
串連。 說說自己的理解: 這個機制就是為了防止出現看著串連沒問題,但其實是發不出訊息的問題,你發送你條訊息經過了伺服器,OK伺服器會給你相應的回應,要是收不到回應,你就處理失敗,斷開Socket,下面是這個一個簡單的理解: 三: 斷開重連
我們自己主動去斷開的Scoket
串連(退出登入,App退出到後台等等)是不需要重連。其他的串連斷開,我們都需要進行斷線重連,一般解決方案是嘗試重連幾次,如果仍舊無法重連成功,那麼不再進行重連。這個簡單的瞭解一下,知道就行。
上面關於Socket的理論性的東西我們也就說的差不多了,下面的重點是通過代碼消化一下上面說的理論知識。
看看Socket的主要方法
接下來就看看在iOS中Socket的源碼裡面都有些什麼東西,可以先看一下,建立一個檔案,匯入 #import <sys/socket.h> 可以進去看看Socket裡面的東西。它最主要是下面一組介面,給我們提供了它的方法,我們在下面對它進行一個仔細的注釋分析:
按照我們前面說的整個的一個過程,我們歸納一下用戶端利用Socket發送訊息的整個過程:
1、 初始化Socket 使用方法 int socket (int , int ,int )
2、串連Socket 使用方法 int connect(int, const struct sockaddr *, socklen_t)
3、發送、接收資料 使用方法 ssize_t send(int, const void *, size_t, int)/ssize_t recv(int, void *, size_t, int)
ssize_t sendto(int, const void *, size_t,int, const struct sockaddr *, socklen_t)和
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr * __restrict, socklen_t * __restrict)
上面發送方法的是有區別的,在下面的注釋中我們添加說明。
4、關閉Socket 使用方法 close() 這個方法在後面我在說,在Socket源碼是看不到這個方法的。
下面是重要的一些Socket方法的方法的注釋:
/*為了保證閱讀的順序和源碼的順序對應,方便大家查看,就不調整方法順序,比如初始化的方法位置沒有調整。解釋的也是主要的,沒有全部都解釋 __BEGIN_DECLS 這個方法是在服務端用到,表示接受用戶端請求,並講用戶端的網路地址儲存在sockaddr類型指標__restrict,後面的__restrict是地址的長度intaccept(int, struct sockaddr * __restrict, socklen_t * __restrict) __DARWIN_ALIAS_C(accept); 將Socket與指定的主機地址與連接埠號碼綁定,綁定成功返回0.失敗返回-1,這個方法你可以在CocoaAsyncSocket源碼中看到intbind(int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS(bind); 用戶端Socket的串連方法,成功返回0,失敗返回-1,第一個參數是你初始化一個Socket擷取到的檔案描述符,初始化Socket返回的檔案描述符是int類型,這個你在下面可以看到。 第二個參數是一個指向要串連Socket的sockaddr結構體的指標, 第三個參數代表sockaddr結構體的位元組長度參考:https://baike.baidu.com/item/connect%28%29/10081861?fr=aladdinintconnect(int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS_C(connect); 擷取Socket的地址 參考:https://baike.baidu.com/item/getpeername%28%29intgetpeername(int, struct sockaddr * __restrict, socklen_t * __restrict)__DARWIN_ALIAS(getpeername); 擷取Socket的名稱 參考:https://baike.baidu.com/item/getsockname%28%29intgetsockname(int, struct sockaddr * __restrict, socklen_t * __restrict)__DARWIN_ALIAS(getsockname); https://baike.baidu.com/item/getsockopt%28%29intgetsockopt(int, int, int, void * __restrict, socklen_t * __restrict); 用於服務端監聽用戶端,傳的兩個參數一個是初始化Socket擷取到的檔案描述符, 第二個是等待串連隊列的最大長度如無錯誤發生,listen()返回0。否則的話,返回-1方法帶參數這樣 int listen( int sockfd, int backlog) ,這個方法在CocoaAsyncSocket源碼中也用做判斷參考:https://baike.baidu.com/item/listen%28%29intlisten(int, int) __DARWIN_ALIAS(listen); 接收訊息方法,詳細https://baike.baidu.com/item/recv%28%29ssize_trecv(int, void *, size_t, int) __DARWIN_ALIAS_C(recv); 從UDP Socket中讀取資料ssize_trecvfrom(int, void *, size_t, int, struct sockaddr * __restrict,socklen_t * __restrict) __DARWIN_ALIAS_C(recvfrom); ssize_trecvmsg(int, struct msghdr *, int) __DARWIN_ALIAS_C(recvmsg); 發送訊息方法 參考: https://baike.baidu.com/item/send%28%29#3ssize_tsend(int, const void *, size_t, int) __DARWIN_ALIAS_C(send); send,sendto以及sendmsg系統調用用於發送訊息到另一個通訊端。send函數在通訊端處於串連狀態時方可使用。而sendto和sendmsg在任何時候都可使用ssize_tsendmsg(int, const struct msghdr *, int) __DARWIN_ALIAS_C(sendmsg); sendto()適用於發送未建立串連的UDP資料包ssize_tsendto(int, const void *, size_t,int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS_C(sendto); intsetsockopt(int, int, int, const void *, socklen_t); shutdown()是指禁止在一個Socket上進行資料的接收與發送。intshutdown(int, int); intsockatmark(int) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); 重點理解一下Socket的初始化,完整的參數方法是這樣: int socket(int domain, int type, int protocol)domain 參數表示制定使用何種的地址類型 比如:PF_INET, AF_INET: Ipv4網路通訊協定PF_INET6, AF_INET6: Ipv6網路通訊協定 type 參數的作用是設定通訊的協議類型SOCK_STREAM: 提供連線導向的穩定資料轉送,即TCP協議。OOB: 在所有資料傳送前必須使用connect()來建立串連狀態。SOCK_DGRAM: 使用不連續不可靠的資料包串連。SOCK_SEQPACKET: 提供連續可靠的資料包串連。SOCK_RAW: 提供原始網路通訊協定存取。SOCK_RDM: 提供可靠的資料包串連。SOCK_PACKET: 與網路驅動程式直接通訊。參數protocol用來指定socket所使用的傳輸協議編號。這一參數通常不具體設定,一般設定為0即可。參考:https://baike.baidu.com/item/socket%28%29上面的解釋要是結合CocoaAsyncSocket的源碼再去理解,會理解的更透徹intsocket(int, int, int); intsocketpair(int, int, int, int *) __DARWIN_ALIAS(socketpair);#if !defined(_POSIX_C_SOURCE)intsendfile(int, int, off_t, off_t *, struct sf_hdtr *, int);#endif !_POSIX_C_SOURCE #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)void pfctlinput(int, struct sockaddr *);int connectx(int, const sa_endpoints_t *, sae_associd_t, unsigned int, const struct iovec *, unsigned int, size_t *, sae_connid_t *);int disconnectx(int, sae_associd_t, sae_connid_t); #endif(!_POSIX_C_SOURCE || _DARWIN_C_SOURCE)__END_DECLS*/
原生Socket 訊息發送接收Demo
這裡就沒有再做GIF格式的動圖,怕這個訊息發送接收做的圖太大!還是給出來,你可以在這裡看到CONNECTED串連成功,也可以看到收到的訊息Data,最後的你中斷連線的時候服務端的CLOSE我就不:這個Demo由於比較的小,我把主要的代碼給出來,Demo源碼會在這個系列文章和後面的demo一起傳上來:
這個是SocketManager這個單例類的.m源碼,上面全都有注釋,當然你急需要源碼。可以加我QQ找我,我發給你!這個Demo後面整理上傳!
#import "SocketManager.h"#import <sys/socket.h>#import <sys/types.h>#import <netinet/in.h>#import <arpa/inet.h>@interface SocketManager()@property (nonatomic,assign)int clientScoket; // Socket@property (nonatomic,assign)int connetSocketResult;// Socket串連結果@end@implementation SocketManager/** NOTE: 裡面涉及到一些Socket的建立的具體的方法你要是不理解可以暫時放下 等讀完CocoaAsyncSocket的源碼的具體注釋就可以理解*/+(instancetype)shareInstance{ static SocketManager * manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [[SocketManager alloc]init]; manager.connetSocketResult = -1; // 初始化串連狀態是斷開狀態 // 在建立單例的時候就去初始化Socket和開闢一條新的線程去接收訊息 [manager initSocket]; [manager ReceiveMessageThread]; }); return manager;}-(void)initSocket{ //每次串連前,先判斷是否在串連狀態 0 在串連狀態,直接Return if (_connetSocketResult == 0) { return; } _clientScoket = creatSocket(); //建立用戶端socket const char * server_ip="127.0.0.1"; //伺服器Ip short server_port = 6969; //伺服器連接埠 //等於0說明串連成功,-1則串連失敗 if (connectionToServer(_clientScoket,server_ip, server_port) == 0) { _connetSocketResult = 0; printf("Connect to server Success\n"); return ; }else{ _connetSocketResult = -1; printf("Connect to server error\n"); }}/** 建立Socket @return 返回Socket */static int creatSocket(){ int ClinetSocket = 0; // NOTE: Socket本質上就是int類型 ClinetSocket = socket(AF_INET, SOCK_STREAM, 0); // 返回建立的Socket return ClinetSocket;}/** 串連 Socket @param client_socket Socket @param server_ip 伺服器IP @param port 連接埠 @return 返回時候串連成功,返回0則串連成功,-1串連失敗 */static int connectionToServer(int client_socket,const char * server_ip,unsigned short port){ //產生一個sockaddr_in類型結構體 struct sockaddr_in sAddr={0}; sAddr.sin_len=sizeof(sAddr); //設定IPv4, 這個區分可以看前面在解釋Socket方法的時候寫的注釋 sAddr.sin_family=AF_INET; //inet_aton是一個改進的方法來將一個字串IP地址轉換為一個32位的網路序列IP地址 //如果這個函數成功,函數的傳回值非零,如果輸入地址不正確則會返回零。 inet_aton(server_ip, &sAddr.sin_addr); //htons是將整型變數從主機位元組順序轉變成網路位元組順序,賦值連接埠號碼 sAddr.sin_port=htons(port); // 防止發送SO_NOSIGPIPE訊號導致崩潰 int nosigpipe = 1; setsockopt(client_socket, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); //用scoket和服務端地址,發起串連。 //用戶端向特定網路地址的伺服器發送串連請求,串連成功返回0,失敗返回 -1。 //注意:該介面調用會阻塞當前線程,直到伺服器返回。 int connectResult = connect(client_socket, (struct sockaddr *)&sAddr, sizeof(sAddr)); return connectResult;}// 開闢一條線程接收訊息-(void)ReceiveMessageThread{ NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(recieveAction) object:nil]; [thread start];}/** 串連Socket */-(void)connectSocket{ [self initSocket];}/** 斷開Socket串連 */-(void)disConnectSocket{ close(self.clientScoket);}/** 發送訊息 @param msg 訊息內容 */- (void)sendMsg:(NSString *)msg{ const char * send_Message = [msg UTF8String]; send(self.clientScoket,send_Message,strlen(send_Message)+1,0);}/** 死迴圈,看是否有訊息發送過來 */- (void)recieveAction{ while (1) { char recv_Message[1024] = {0}; recv(self.clientScoket, recv_Message, sizeof(recv_Message), 0); printf("%s\n",recv_Message); }}@end
這個是服務端代碼,先把代碼給出來,下面我會仔細和大家說說你怎樣把這個運行起來!
var net = require(‘net‘); var HOST = ‘127.0.0.1‘; var PORT = 6969; // 建立一個TCP伺服器執行個體,調用listen函數開始監聽指定連接埠 // 傳入net.createServer()的回呼函數將作為”connection“事件的處理函數 // 在每一個“connection”事件中,該回呼函數接收到的socket對象是唯一的 net.createServer(function(sock) { // 我們獲得一個串連 - 該串連自動關聯一個socket對象 console.log(‘CONNECTED: ‘ + sock.remoteAddress + ‘:‘ + sock.remotePort); sock.write(‘服務端發出:串連成功‘); // 為這個socket執行個體添加一個"data"事件處理函數 sock.on(‘data‘, function(data) { console.log(‘DATA ‘ + sock.remoteAddress + ‘: ‘ + data); // 回傳該資料,用戶端將收到來自服務端的資料 sock.write(‘You said "‘ + data + ‘"‘); }); // 為這個socket執行個體添加一個"close"事件處理函數 sock.on(‘close‘, function(data) { console.log(‘CLOSED: ‘ + sock.remoteAddress + ‘ ‘ + sock.remotePort); }); }).listen(PORT, HOST); console.log(‘Server listening on ‘ + HOST +‘:‘+ PORT);
說說上面服務端代碼怎樣運行
為什麼要說這個呢,因為不是每一個iOS開發都是會懂那些JS的,上面的服務端代碼就是我給大家推薦的第一篇文章中的,我自己寫這試了試,這個運行起來推薦給大家的是這款 Sublime Text2 這款軟體,這個 Sublime Text3是已經出了,但你能下載到MAC版本上面的只能是Sublime Text2,所以你可以先百度把這東西下載了!
接下來,你還需要安裝它 Node.js , 下載並安裝Node.js , 你下載安裝之後,需要在 Sublime Text2添加支援JS 的 Build System , 選中這個 New Build System... ,添加下面代碼:
{"cmd":["node","$file"],"selector":"source.js"}
NOTE: 要是你的node是一步一步點擊直接安裝的,上面代碼中的node要變成你安裝的node的絕對路徑,怎麼找node的絕對路徑??
簡單呢:開啟你的終端 which node 搞定!!
完結
這篇部落格寫的也是花了許久的時間,由於篇幅的問題,不能再寫一下去了,再寫就真的太長了,後面的心跳、重連等等問題再下一篇文章中總結,在下一篇中打算通過結合CocosAsnySocket這個三方總結Socket剩下的問題,最後,還是給Telegram群打個廣告,有在看這個開源項目的朋友可以加一下下面的群,一起學習!篇幅長難免可能會有問題,要有什麼總結的不好的或者有問題的地方,加我QQ或者下面留言交流!
群號可以直接粘貼這裡:485718322
iOS Socket 整理以及CocoaAsyncSocket、SRWebSocket源碼解析(一)