基于WebSocket構建移動端實時應用

jopen 10年前發布 | 28K 次閱讀 WebSocket iOS開發 移動開發

前言

對于傳統的網絡應用,大部分情況下使用的是Http短連接,這意味著你發送一次請求,服務器給你返回響應信息,然后連接就被斷掉了。然而現實生活中,很多應用實際上是需要一種實時機制的支持,比如微信,你就需要實時收到對方發送的回復信息。對于應用處于后臺的情況下,你可以使用系統級別的推送服務,比如iOS下的APNS和Android下的GCM。應用處于前臺時呢,則需要自己去和服務端建立一個Http長連接或者輪詢,這種方式對于服務器的性能要求還是比較高的。HTML5中提出了一種新的雙向通信協議--WebSocket,本文嘗試采用這種技術來實現以上的實時推送功能。

WebSocket

WebSocket是HTML5開始提供的一種在客戶端和服務器間持久連接的雙向通信網絡技術。 WebSocket通信協議于2011年被IETF定為標準 RFC 6455,WebSocketAPI被W3C定為標準。協議本身使用新的ws://URL格式,但它是在標準HTTP上實現的。通過使用HTTP和 HTTPS端口,它避免了從Web代理后的網絡連接站點時引入的各種問題。HTML5規范不只描述了協議本身,還描述了使用WebSocket編寫客戶端代碼所需要的瀏覽器API。在WebSocket API中,瀏覽器和服務器只需要做一個握手的動作,然后,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。

服務端實現

服務端采用了非死book開源的Tornado框架,由于Tornado原生支持WebSocket協議,用它來實現服務端非常方便。

Tornado在WebSocket模塊中提供了一個WebSocketHandler類。這個類提供了和已連接的客戶端通信的WebSocket 事件和方法的鉤子。當一個新的WebSocket連接打開時,open方法被調用,而on_message和on_close方法分別在連接接收到新的消息和客戶端關閉時被調用。

此外,WebSocketHandler類還提供了write_message方法用于向客戶端發送消息,close方法用于關閉連接。

以開關狀態改變檢測為例,服務端部署的代碼如下:

class Switch(BaseModel):
    tablename = 'switch'

id = Column(Integer, primary_key = True)
name = Column(String(30))
status = Column(Boolean, server_default = text('False')) #開關當前狀態
level = Column(Integer)                                  #最小可操作等級

callbacks = []
@classmethod
def register(cls, callback):
    cls.callbacks.append(callback)

@classmethod
def unregister(cls, callback):
    cls.callbacks.remove(callback)

def notifyCallbacks(self):
    for callback in self.callbacks:
        callback(self.id,self.status)</pre><br />

</figure>

以上是封裝的開關對象,使用了SQLAlchemy作為ORM,其中BaseModel是繼承自declarative_base。定義了兩個類方法,分別是注冊和移除回調,另外一個實例方法來通知回調。

對外處理請求的handler: def callback(self,switch_id,status): self.write_message('{"switch_id":"%s","status":"%d"}'%(switch_id,status))

class GetSwitchStatusHandler(BaseWebsockHandler):

def open(self):
    Switch.register(self.callback)

def on_close(self):
    Switch.unregister(self.callback)

def on_message(self,msg):
    pass

def callback(self,switch_id,status):
    self.write_message('{"switch_id":"%s","status":"%d"}'%(switch_id,status))</pre><br />

</figure>

BaseWebsockHandler繼承自tornado.websocket.WebSocketHandler,在initialize中對于SQLAlchemy的session進行了初始化

由于在本例中不需要取客戶端的上行消息,故直接pass調了on_message方法。callback方法則用來處理回調,將改變后的開關信息返回給客戶端。

客戶端實現

客戶端使用了Square開源的SocketRocket

在iOS工程中安裝可以直接使用源碼,也可以用CocoaPods安裝,將以下依賴加入到PodFile中,再執行install命令即可

pod 'SocketRocket', '~> 0.3.1-beta2'

SocketRocket的核心是SRWebSocket這個類,需要在使用WebSocket連接的ViewController中實現SRWebSocketDelegate。

- (void)connect{

webSocket.delegate = nil;
[webSocket close]; 
webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:SWITCH_STATUS_URL]]];
webSocket.delegate = self;
NSLog(@"Opening Connection...");
[webSocket open];

}

···

pragma mark - SRWebSocketDelegate

  • (void)webSocketDidOpen:(SRWebSocket *)webSocket;{ NSLog(@"Websocket Connected"); }

  • (void)webSocket:(SRWebSocket )webSocket didFailWithError:(NSError )error;{ NSLog(@":( Websocket Failed With Error %@", error); webSocket = nil; }

  • (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;{ NSLog(@"Received \"%@\"", message); }

  • (void)webSocket:(SRWebSocket )webSocket didCloseWithCode:(NSInteger)code reason:(NSString )reason wasClean:(BOOL)wasClean;{ NSLog(@"WebSocket closed"); webSocket = nil; }</pre>
    </figure>

    把delegate指向自身,然后調用SRWebSocket中的方法發送請求即可。收到下行消息會調用didReceiveMessage這個方法,如果需要在請求時攜帶參數,可以用類似get請求的方法,將請求放在url字符串中。

    到此為止WebSocket的基本功能就實現完畢了,完整demo可以參考我的Github

    參考

    《Introduction to Tornado》http://book.douban.com/subject/7906788/

    來自:http://shiningio.com/2014/03/16/build-real-time-app-with-websocket/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!