版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2019.07.09 星期二 |
前言
實(shí)時(shí)通信在很多App上都有應(yīng)用,包括我在上一家公司做的App也使用了實(shí)時(shí)通信技術(shù),用于私聊和群聊,所以這里特意開一個(gè)專題一起學(xué)習(xí)一下實(shí)時(shí)通信技術(shù)。
開始
首先看一下寫作環(huán)境
Swift 5, iOS 12, Xcode 10
從一開始,人類就夢想有更好的方式與他的弟兄們進(jìn)行廣泛的溝通。 從信鴿到無線電波,我們一直在努力更清晰有效地進(jìn)行溝通。
在這個(gè)現(xiàn)代時(shí)代,一種技術(shù)已成為我們尋求相互理解的重要工具:簡潔的網(wǎng)絡(luò)socket
。
現(xiàn)在位于我們現(xiàn)代網(wǎng)絡(luò)基礎(chǔ)設(shè)施第4層layer 4的某個(gè)地方,sockets
是任何在線通信的核心,從短信到在線游戲。
1. Why Sockets?
您可能想知道,“為什么我首先需要比URLSession
更靠近底層的東西?”(如果您不想知道,那么請(qǐng)繼續(xù)并假裝你想知道。)
這是一個(gè)好問題!與URLSession
通信的事情是它基于HTTP網(wǎng)絡(luò)協(xié)議。使用HTTP,通信以請(qǐng)求 - 響應(yīng)(request-response)
方式發(fā)生。這意味著大多數(shù)應(yīng)用程序中的大多數(shù)網(wǎng)絡(luò)代碼遵循相同的模式:
- 1) 從服務(wù)器請(qǐng)求一些JSON。
- 2) 在回調(diào)或代理方法中接收和使用所述JSON。
但是,當(dāng)你希望服務(wù)器能夠告訴你的應(yīng)用程序時(shí)呢?這對(duì)HTTP來說并不是很有效。
當(dāng)然,您可以通過不斷ping
服務(wù)器并查看它是否有更新(也就是輪詢)來使其工作,或者您可以使用長輪詢long-polling等技術(shù)。但是這些技術(shù)可能會(huì)有點(diǎn)不自然,每個(gè)都有自己的陷阱。
在一天結(jié)束時(shí),如果它不適合這項(xiàng)工作,為什么要限制自己使用這種請(qǐng)求 - 響應(yīng)模式呢?
在這個(gè)iOS流教程中,您將學(xué)習(xí)如何使用底層抽象級(jí)別并直接使用sockets
來創(chuàng)建實(shí)時(shí)聊天室應(yīng)用程序。
您的聊天室應(yīng)用程序?qū)⑹褂迷诹奶鞎?huì)話期間保持打開狀態(tài)的輸入和輸出流,而不是使用每個(gè)客戶端都必須檢查服務(wù)器是否有新消息這種方式。
首先,打開聊天應(yīng)用程序和用Go
編寫的簡單服務(wù)器。
您不必?fù)?dān)心自己編寫任何Go代碼,但是您需要啟動(dòng)并運(yùn)行此服務(wù)器才能為其編寫客戶端。
2. Getting Your Server to Run
初始材料中的服務(wù)器使用Go
并為您預(yù)編譯。 如果您不是那種信任您在網(wǎng)絡(luò)上找到的預(yù)編譯可執(zhí)行文件的人,您可以使用材料中的源代碼自行編譯。
要運(yùn)行預(yù)編譯的服務(wù)器,請(qǐng)打開終端,導(dǎo)航到初始材料目錄并輸入以下命令:
sudo ./server
出現(xiàn)提示時(shí),請(qǐng)輸入您的密碼。 輸入密碼后,您應(yīng)該看到:Listening on 127.0.0.1:80
。
注意:您必須使用特權(quán)運(yùn)行服務(wù)器 - 因此是
sudo
命令 - 因?yàn)樗鼈陕牰丝?code>80。所有小于1024
的端口號(hào)都是特權(quán)端口,需要root訪問權(quán)限才能綁定它們。
您的聊天服務(wù)器已準(zhǔn)備就緒! 您現(xiàn)在可以跳到下一部分。
如果您想自己編譯服務(wù)器,則需要使用Homebrew
安裝Go
。
如果您還沒有Homebrew
,那么您必須先安裝它然后才能開始。 打開終端并粘貼以下行:
/usr/bin/ruby -e \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
然后使用下面這個(gè)命令安裝Go
:
brew install go
完成后,導(dǎo)航到入門材料的目錄并使用build
構(gòu)建服務(wù)器。
go build server.go
最后,您可以使用本節(jié)開頭列出的命令啟動(dòng)服務(wù)器。
3. Looking at the Existing App
接下來,打開DogeChat
項(xiàng)目并構(gòu)建并運(yùn)行它以查看已經(jīng)為您構(gòu)建的內(nèi)容。
如上所示,DogeChat
當(dāng)前設(shè)置為允許用戶輸入用戶名然后進(jìn)入聊天室。
不幸的是,上一個(gè)工作的人不知道如何編寫聊天應(yīng)用程序,所以你得到的只是用戶界面和基本的管道,你必須實(shí)現(xiàn)網(wǎng)絡(luò)層。
Creating a Chat Room
要開始實(shí)際編碼,請(qǐng)導(dǎo)航到ChatRoomViewController.swift
。在這里,您可以看到您有一個(gè)準(zhǔn)備好的視圖控制器,并且能夠從輸入欄接收字符串作為消息。它還可以通過table view
顯示消息,其中包含使用Message
對(duì)象配置的自定義單元格。
由于您已經(jīng)有了ChatRoomViewController
,因此創(chuàng)建一個(gè)ChatRoom
類來處理繁重的工作是有意義的。
在開始編寫新類之前,請(qǐng)快速列出其職責(zé)。您希望此類負(fù)責(zé)以下任務(wù):
- 1) 打開與聊天室服務(wù)器的連接。
- 2) 允許用戶通過提供用戶名加入聊天室。
- 3) 允許用戶發(fā)送和接收消息。
- 4) 完成后關(guān)閉連接。
既然您知道自己想要什么,請(qǐng)按Command-N
創(chuàng)建一個(gè)新文件。選擇Swift文件并將其命名為ChatRoom
。
1. Creating Input and Output Streams
接下來,將ChatRoom.swift
中的代碼替換為:
import UIKit
class ChatRoom: NSObject {
//1
var inputStream: InputStream!
var outputStream: OutputStream!
//2
var username = ""
//3
let maxReadLength = 4096
}
在這里,您已經(jīng)定義了ChatRoom
類并聲明了您需要進(jìn)行通信的屬性。
- 1) 首先,聲明輸入和輸出流。 通過一起使用這對(duì)類,您可以在應(yīng)用程序和聊天服務(wù)器之間創(chuàng)建基于
socket
的連接。 當(dāng)然,您將通過輸出流發(fā)送消息并通過輸入流接收消息。 - 2) 接下來,您定義
username
,您將在其中存儲(chǔ)當(dāng)前用戶的名稱。 - 3) 最后,您聲明
maxReadLength
。 此常量會(huì)限制您可以在任何單個(gè)消息中發(fā)送的數(shù)據(jù)量。
接下來,轉(zhuǎn)到ChatRoomViewController.swift
并將chat room
屬性添加到頂部的屬性列表中。
let chatRoom = ChatRoom()
現(xiàn)在您已經(jīng)設(shè)置了類的基本結(jié)構(gòu),現(xiàn)在是時(shí)候敲掉清單中的第一件事了:打開應(yīng)用程序和服務(wù)器之間的連接。
Opening a Connection
返回ChatRoom.swift
,在屬性定義下面添加以下方法:
func setupNetworkCommunication() {
// 1
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
// 2
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
"localhost" as CFString,
80,
&readStream,
&writeStream)
}
這是發(fā)生了什么:
- 1) 首先,設(shè)置兩個(gè)未初始化的套接字流,而不進(jìn)行自動(dòng)內(nèi)存管理。
- 2) 然后將讀取和寫入套接字流綁定在一起,并將它們連接到主機(jī)的套接字,在這種情況下,它位于端口80上。
該函數(shù)有四個(gè)參數(shù)。第一個(gè)是初始化流時(shí)要使用的分配器allocator
類型。你應(yīng)該盡可能使用kCFAllocatorDefault
,但如果你遇到需要的東西有點(diǎn)不同的情況,還有其他選擇。
接下來,指定hostname
。在這種情況下,您將連接到本地計(jì)算機(jī);如果您有遠(yuǎn)程服務(wù)器的特定IP地址,您也可以在此處使用它。
然后,指定您通過port 80
連接,這是服務(wù)器偵聽的端口。
最后,將指針傳遞給讀取和寫入流,以便函數(shù)可以使用它在內(nèi)部創(chuàng)建的連接讀取和寫入流來初始化它們。
現(xiàn)在您已經(jīng)初始化了流,您可以通過在setupNetworkCommunication()
的末尾添加以下行來存儲(chǔ)對(duì)它們的保留引用:
inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()
在非管理對(duì)象上調(diào)用takeRetainedValue()
允許您同時(shí)獲取保留的引用并去掉不平衡的retain
,以便以后不會(huì)泄漏內(nèi)存。 現(xiàn)在,您可以在需要時(shí)使用輸入和輸出流。
接下來,您需要將這些流添加到run loop
中,以便您的應(yīng)用程序能夠正確地響應(yīng)網(wǎng)絡(luò)事件。 通過將這兩行添加到setupNetworkCommunication()
的末尾來實(shí)現(xiàn):
inputStream.schedule(in: .current, forMode: .common)
outputStream.schedule(in: .current, forMode: .common)
最后,你準(zhǔn)備打開防洪閘了! 要開始派對(duì),請(qǐng)?zhí)砑拥?code>setupNetworkCommunication()的底部:
inputStream.open()
outputStream.open()
這就是它的全部內(nèi)容。 要完成,請(qǐng)轉(zhuǎn)到ChatRoomViewController.swift
并將以下行添加到viewWillAppear(_ :)
:
chatRoom.setupNetworkCommunication()
您現(xiàn)在在客戶端應(yīng)用程序和在localhost
上運(yùn)行的服務(wù)器之間建立了打開的連接。
如果需要,您可以構(gòu)建和運(yùn)行您的應(yīng)用程序,但是您會(huì)看到之前看到的相同內(nèi)容,因?yàn)槟形磭L試對(duì)連接執(zhí)行任何操作。
Joining the Chat
現(xiàn)在您已經(jīng)建立了與服務(wù)器的連接,現(xiàn)在是時(shí)候開始進(jìn)行通信了! 你要說的第一件事就是你認(rèn)為自己到底是誰。 之后,您將要開始向人們發(fā)送消息。
這提出了一個(gè)重點(diǎn):由于您有兩種消息,因此您需要找到一種方法來區(qū)分它們。
1. The Communication Protocol
使用底層TCP級(jí)別的一個(gè)優(yōu)點(diǎn)是您可以定義自己的“協(xié)議”來決定消息是否有效。
使用HTTP
,您需要考慮所有那些討厭的動(dòng)詞,如GET,PUT和PATCH
。 您需要構(gòu)建URL
并使用適當(dāng)?shù)?code>header和各種東西。
這里你只有兩種消息。 你可以發(fā)送:
iam:Luke
進(jìn)入房間并告知世界你的消息,你可以說:
msg:Hey, how goes it, man?
向房間里的其他人發(fā)送消息。
這很簡單但也很不安全,所以不要在工作中使用它。
現(xiàn)在您已了解服務(wù)器的期望,您可以在ChatRoom
上編寫一個(gè)方法以允許用戶進(jìn)入聊天室。 它需要的唯一參數(shù)是所需的用戶名。
要實(shí)現(xiàn)它,請(qǐng)?jiān)谀鷦倓傇?code>ChatRoom.swift中編寫的setup
方法下面添加以下方法:
func joinChat(username: String) {
//1
let data = "iam:\(username)".data(using: .utf8)!
//2
self.username = username
//3
_ = data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
print("Error joining chat")
return
}
//4
outputStream.write(pointer, maxLength: data.count)
}
}
- 1) 首先,使用簡單的聊天室協(xié)議構(gòu)建您的消息。
- 2) 然后,您保存名稱,以便以后可以使用它來發(fā)送聊天消息。
- 3)
withUnsafeBytes(_ :)
提供了一種方便的方法來處理閉包安全范圍內(nèi)的某些數(shù)據(jù)的不安全指針版本。 - 4) 最后,將消息寫入輸出流。 這可能看起來比你想象的要復(fù)雜一點(diǎn),但是
write(_:maxLength :)
引用了一個(gè)不安全的指向字節(jié)的指針作為你在上一步中創(chuàng)建的第一個(gè)參數(shù)。
現(xiàn)在您的方法已準(zhǔn)備就緒,打開ChatRoomViewController.swift
并添加一個(gè)調(diào)用以在viewWillAppear(_ :)
底部加入聊天。
chatRoom.joinChat(username: username)
現(xiàn)在,構(gòu)建并運(yùn)行您的應(yīng)用程序。 輸入您的姓名,然后點(diǎn)按return
以查看...
還是一樣沒變化?
現(xiàn)在,堅(jiān)持下去,有一個(gè)很好的解釋。 轉(zhuǎn)到您的終端。 在Listening on 127.0.0.1:80
下,如果你的名字不是Brody
,你應(yīng)該看到Brody has joined
,或類似的東西。
這是個(gè)好消息,但你寧愿在手機(jī)屏幕上看到一些成功的跡象。
Reacting to Incoming Messages
服務(wù)器發(fā)送傳入的消息,例如您剛剛發(fā)送給房間中每個(gè)人的加入消息,包括您。 幸運(yùn)的是,你的應(yīng)用程序已經(jīng)設(shè)置為在ChatRoomViewController
的消息表中顯示任何類型的傳入消息作為單元格。
您需要做的就是使用inputStream
來捕獲這些消息,將它們轉(zhuǎn)換為Message
對(duì)象,然后將它們傳遞給該表來完成它的工作。
為了對(duì)收到的消息做出反應(yīng),您需要做的第一件事就是讓您的chat room
成為輸入流的代理。
為此,請(qǐng)轉(zhuǎn)到ChatRoom.swift
的底部并添加以下擴(kuò)展名。
extension ChatRoom: StreamDelegate {
}
既然您已經(jīng)說過遵循StreamDelegate
,那么您可以聲稱自己是inputStream
的代理。
在調(diào)度調(diào)用schedule(in:forMode:)
之前直接將以下行添加到setupNetworkCommunication()
:
inputStream.delegate = self
接下來,將此實(shí)現(xiàn)的stream(_:handle :)
添加到擴(kuò)展名:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case .hasBytesAvailable:
print("new message received")
case .endEncountered:
print("new message received")
case .errorOccurred:
print("error occurred")
case .hasSpaceAvailable:
print("has space available")
default:
print("some other event...")
}
}
1. Handling the Incoming Messages
在這里,您已經(jīng)準(zhǔn)備好對(duì)與Stream
相關(guān)的傳入事件做些什么。 您真正感興趣的事件是.hasBytesAvailable
,因?yàn)樗硎居幸粭l要傳入的消息要讀取。
接下來,您將編寫一個(gè)方法來處理這些傳入的消息。 在您剛添加的方法下方,添加:
private func readAvailableBytes(stream: InputStream) {
//1
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxReadLength)
//2
while stream.hasBytesAvailable {
//3
let numberOfBytesRead = inputStream.read(buffer, maxLength: maxReadLength)
//4
if numberOfBytesRead < 0, let error = stream.streamError {
print(error)
break
}
// Construct the Message object
}
}
- 1) 首先,設(shè)置一個(gè)緩沖區(qū),您可以在其中讀取傳入的字節(jié)。
- 2) 接下來,只要輸入流具有要讀取的字節(jié),就會(huì)循環(huán)。
- 3) 在每一點(diǎn)上,您將調(diào)用
read(_:maxLength :)
,它將從流中讀取字節(jié)并將它們放入您傳入的緩沖區(qū)中。 - 4) 如果對(duì)
read
的調(diào)用返回負(fù)值,則會(huì)發(fā)生一些錯(cuò)誤并退出。
您需要在輸入流具有可用字節(jié)的情況下調(diào)用此方法,因此請(qǐng)?jiān)?code>stream(_:handle :)中的switch
語句中轉(zhuǎn)到.hasBytesAvailable
,并在print
語句下面調(diào)用您正在處理的方法。
readAvailableBytes(stream: aStream as! InputStream)
在這一點(diǎn)上,你有一個(gè)充滿字節(jié)的甜蜜緩沖區(qū)!
在完成此方法之前,您需要編寫另一個(gè)幫助程序以將buffer
轉(zhuǎn)換為Message
對(duì)象。
將以下方法定義放在readAvailableBytes(stream :)
下面。
private func processedMessageString(buffer: UnsafeMutablePointer<UInt8>,
length: Int) -> Message? {
//1
guard
let stringArray = String(
bytesNoCopy: buffer,
length: length,
encoding: .utf8,
freeWhenDone: true)?.components(separatedBy: ":"),
let name = stringArray.first,
let message = stringArray.last
else {
return nil
}
//2
let messageSender: MessageSender =
(name == self.username) ? .ourself : .someoneElse
//3
return Message(message: message, messageSender: messageSender, username: name)
}
- 1) 首先,使用傳入的緩沖區(qū)
buffer
和長度length
初始化String
。將文本視為UTF-8
,告訴String
在完成文本時(shí)釋放字節(jié)緩沖區(qū)然后將傳入消息拆分為:character
,這樣你可以將發(fā)件人的姓名和消息視為單獨(dú)的字符串。 - 2) 接下來,您將確定您或其他人是否根據(jù)姓名發(fā)送了消息。 在生產(chǎn)應(yīng)用程序中,您需要使用某種唯一的
token
,但就目前而言,這已經(jīng)足夠了。 - 3) 最后,使用您收集的部分構(gòu)造一條消息并將其返回。
要使用Message
構(gòu)造方法,請(qǐng)?jiān)?code>readAvailableBytes(stream :)中,在最后一條注釋的正下方將以下if-let
添加到while
循環(huán)的末尾:
if let message =
processedMessageString(buffer: buffer, length: numberOfBytesRead) {
// Notify interested parties
}
到這里以后,你們都準(zhǔn)備將Message
傳遞給某人......但是誰呢?
2. Creating the ChatRoomDelegate Protocol
好吧,你真的想告訴ChatRoomViewController.swift
關(guān)于新消息,但你沒有對(duì)它的引用。 由于它擁有對(duì)ChatRoom
的強(qiáng)引用,因此您不希望顯式創(chuàng)建循環(huán)依賴關(guān)系并創(chuàng)建ChatRoomViewController
。
這是設(shè)置代理協(xié)議的最佳時(shí)機(jī)。 ChatRoom
不關(guān)心什么樣的對(duì)象想知道新消息,它只是想告訴別人。
在ChatRoom.swift
的頂部,添加簡單的協(xié)議定義:
protocol ChatRoomDelegate: class {
func received(message: Message)
}
接下來,在ChatRoom
類的頂部,添加一個(gè)弱的可選屬性來保存對(duì)任何人決定成為ChatRoom
委托的引用:
weak var delegate: ChatRoomDelegate?
現(xiàn)在,您可以返回ChatRoom.swift
并通過為message
在if-let
中添加以下內(nèi)容來完成readAvailableBytes(stream :)
,方法中的最后一條注釋下方:
delegate?.received(message: message)
要完成,請(qǐng)返回ChatRoomViewController.swift
并添加以下擴(kuò)展名,該擴(kuò)展名符合此協(xié)議,位于MessageInputDelegate
下方:
extension ChatRoomViewController: ChatRoomDelegate {
func received(message: Message) {
insertNewMessageCell(message)
}
}
入門項(xiàng)目包括其余的管道,因此insertNewMessageCell(_ :)
將接收您的消息并將適當(dāng)?shù)膯卧裉砑拥奖碇小?/p>
現(xiàn)在,通過在viewWillAppear(_ :)
中調(diào)用super
之后立即添加以下行,將視圖控制器指定為chatRoom
的代理:
chatRoom.delegate = self
再次構(gòu)建并運(yùn)行您的應(yīng)用并在text field
中輸入您的名字,然后點(diǎn)按return
。
??聊天室現(xiàn)在成功顯示一個(gè)單元格,說明您已進(jìn)入房間。 您已經(jīng)正式向基于socket
的TCP
服務(wù)器發(fā)送消息并從其接收消息。
Sending Messages
現(xiàn)在您已經(jīng)設(shè)置了ChatRoom
來發(fā)送和接收消息,現(xiàn)在是時(shí)候允許用戶來回發(fā)送實(shí)際文本了。
在ChatRoom.swift
中,將以下方法添加到類定義的底部:
func send(message: String) {
let data = "msg:\(message)".data(using: .utf8)!
_ = data.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
print("Error joining chat")
return
}
outputStream.write(pointer, maxLength: data.count)
}
}
這個(gè)方法就像你之前寫的joinChat(username :)
,除了它將msg
添加到你發(fā)送的文本以表示為實(shí)際消息。
由于您想在inputBar
告訴ChatRoomViewController
用戶已點(diǎn)擊Send
時(shí)發(fā)送消息,請(qǐng)返回ChatRoomViewController.swift
并找到MessageInputDelegate
。
在這里,您將看到一個(gè)名為sendWasTapped(message :)
的空方法,該方法在此時(shí)被調(diào)用。 要發(fā)送消息,請(qǐng)將其傳遞給chatRoom
:
chatRoom.send(message: message)
這就完成了全部! 由于服務(wù)器將收到此消息,然后將其轉(zhuǎn)發(fā)給所有人,因此ChatRoom
會(huì)以與您加入房間時(shí)相同的方式收到新消息的通知。
構(gòu)建并運(yùn)行您的應(yīng)用程序,然后繼續(xù)為自己嘗試消息傳遞。
如果您想看到有人聊天,請(qǐng)轉(zhuǎn)到新的終端窗口并輸入:
nc localhost 80
這將允許您在命令行上連接到TCP
服務(wù)器。 現(xiàn)在,您可以發(fā)出應(yīng)用程序用于在那里聊天的相同命令。
iam:gregg
然后,發(fā)送一個(gè)消息:
msg:Ay mang, wut's good?
恭喜,你已經(jīng)成功寫了一個(gè)聊天客戶端!
Cleaning up After Yourself
如果您曾對(duì)文件進(jìn)行過任何編程,那么您應(yīng)該知道好的習(xí)慣是在完成文件后會(huì)close files。 事實(shí)證明Unix
通過文件句柄代表一個(gè)開放的套接字連接,就像其他一切一樣。 這意味著你需要在完成后關(guān)閉它,就像任何其他文件一樣。
為此,在ChatRoom.swift
中定義send(message :)
后添加以下方法:
func stopChatSession() {
inputStream.close()
outputStream.close()
}
正如您可能已經(jīng)猜到的那樣,這會(huì)關(guān)閉流并使其無法發(fā)送或接收信息。 這些調(diào)用還會(huì)從您之前調(diào)度的運(yùn)行循環(huán)中刪除流。
要完成這項(xiàng)任務(wù),請(qǐng)將此方法調(diào)用添加到stream(_:handle :)
內(nèi)的switch
語句中的.endEncountered
案例中:
stopChatSession()
然后,返回ChatRoomViewController.swift
并將相同的行添加到viewWillDisappear(_ :)
:
chatRoom.stopChatSession()
這樣就全部完成了。
現(xiàn)在你已經(jīng)掌握了(或者至少看過一個(gè)簡單的例子)與套接字聯(lián)網(wǎng)的基礎(chǔ)知識(shí),有幾個(gè)地方可以擴(kuò)展你的視野。
UDP Sockets
這個(gè)iOS流教程是使用TCP
進(jìn)行通信的一個(gè)示例,它打開了一個(gè)連接,并保證數(shù)據(jù)包將盡可能到達(dá)目的地。
或者,您也可以使用UDP或數(shù)據(jù)報(bào)套接字進(jìn)行通信。這些套接字無法保證數(shù)據(jù)包將到達(dá),這意味著它們的速度更快,開銷也更少。
它們對(duì)游戲等應(yīng)用非常有用。曾經(jīng)歷過滯后?這意味著你的連接不良,你應(yīng)該收到的很多UDP數(shù)據(jù)包都會(huì)被丟棄。
WebSockets
對(duì)這樣的應(yīng)用程序使用HTTP
的另一種替代方法是稱為WebSockets
的技術(shù)。
與傳統(tǒng)的TCP
套接字不同,WebSocket
至少與HTTP
保持關(guān)系,并且可以實(shí)現(xiàn)與傳統(tǒng)套接字相同的實(shí)時(shí)通信目標(biāo),所有這些都來自瀏覽器的舒適性和安全性。
當(dāng)然,您也可以將WebSockets
與iOS應(yīng)用程序一起使用。
Beej's Guide to Network Programming
最后,如果您真的想深入了解網(wǎng)絡(luò),請(qǐng)查看免費(fèi)在線書籍Beej的網(wǎng)絡(luò)編程指南Beej's Guide to Network Programming。
本書提供了有關(guān)套接字編程的詳盡解釋。如果你害怕C
,那么這本書可能有點(diǎn)令人生畏,但話又說回來,也許今天是你面對(duì)恐懼的那一天。
后記
本篇主要講述了基于Socket和TCP網(wǎng)絡(luò)的實(shí)時(shí)聊天信息流的實(shí)現(xiàn),感興趣的給個(gè)贊或者關(guān)注~~~