實時通信技術(shù)研究(一) —— 基于Socket和TCP網(wǎng)絡(luò)的實時聊天信息流的實現(xiàn)(一)

版本記錄

版本號 時間
V1.0 2019.07.09 星期二

前言

實時通信在很多App上都有應(yīng)用,包括我在上一家公司做的App也使用了實時通信技術(shù)叉瘩,用于私聊和群聊涵但,所以這里特意開一個專題一起學(xué)習(xí)一下實時通信技術(shù)涧尿。

開始

首先看一下寫作環(huán)境

Swift 5, iOS 12, Xcode 10

從一開始履腋,人類就夢想有更好的方式與他的弟兄們進行廣泛的溝通珊燎。 從信鴿到無線電波惭嚣,我們一直在努力更清晰有效地進行溝通。

在這個現(xiàn)代時代悔政,一種技術(shù)已成為我們尋求相互理解的重要工具:簡潔的網(wǎng)絡(luò)socket晚吞。

現(xiàn)在位于我們現(xiàn)代網(wǎng)絡(luò)基礎(chǔ)設(shè)施第4層layer 4的某個地方,sockets是任何在線通信的核心谋国,從短信到在線游戲槽地。

1. Why Sockets?

您可能想知道,“為什么我首先需要比URLSession更靠近底層的東西芦瘾?”(如果您不想知道捌蚊,那么請繼續(xù)并假裝你想知道。)

這是一個好問題旅急!與URLSession通信的事情是它基于HTTP網(wǎng)絡(luò)協(xié)議逢勾。使用HTTP牡整,通信以請求 - 響應(yīng)(request-response)方式發(fā)生藐吮。這意味著大多數(shù)應(yīng)用程序中的大多數(shù)網(wǎng)絡(luò)代碼遵循相同的模式:

  • 1) 從服務(wù)器請求一些JSON。
  • 2) 在回調(diào)或代理方法中接收和使用所述JSON逃贝。

但是谣辞,當(dāng)你希望服務(wù)器能夠告訴你的應(yīng)用程序時呢?這對HTTP來說并不是很有效沐扳。

當(dāng)然泥从,您可以通過不斷ping服務(wù)器并查看它是否有更新(也就是輪詢)來使其工作,或者您可以使用長輪詢long-polling等技術(shù)沪摄。但是這些技術(shù)可能會有點不自然躯嫉,每個都有自己的陷阱。

在一天結(jié)束時杨拐,如果它不適合這項工作祈餐,為什么要限制自己使用這種請求 - 響應(yīng)模式呢?

在這個iOS流教程中哄陶,您將學(xué)習(xí)如何使用底層抽象級別并直接使用sockets來創(chuàng)建實時聊天室應(yīng)用程序帆阳。

您的聊天室應(yīng)用程序?qū)⑹褂迷诹奶鞎捚陂g保持打開狀態(tài)的輸入和輸出流,而不是使用每個客戶端都必須檢查服務(wù)器是否有新消息這種方式屋吨。

首先蜒谤,打開聊天應(yīng)用程序和用Go編寫的簡單服務(wù)器。

您不必?fù)?dān)心自己編寫任何Go代碼至扰,但是您需要啟動并運行此服務(wù)器才能為其編寫客戶端鳍徽。

2. Getting Your Server to Run

初始材料中的服務(wù)器使用Go并為您預(yù)編譯。 如果您不是那種信任您在網(wǎng)絡(luò)上找到的預(yù)編譯可執(zhí)行文件的人敢课,您可以使用材料中的源代碼自行編譯旬盯。

要運行預(yù)編譯的服務(wù)器,請打開終端,導(dǎo)航到初始材料目錄并輸入以下命令:

sudo ./server

出現(xiàn)提示時胖翰,請輸入您的密碼接剩。 輸入密碼后,您應(yīng)該看到:Listening on 127.0.0.1:80萨咳。

注意:您必須使用特權(quán)運行服務(wù)器 - 因此是sudo命令 - 因為它偵聽端口80懊缺。所有小于1024的端口號都是特權(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)"

然后使用下面這個命令安裝Go

brew install go

完成后馋记,導(dǎo)航到入門材料的目錄并使用build構(gòu)建服務(wù)器。

go build server.go

最后懊烤,您可以使用本節(jié)開頭列出的命令啟動服務(wù)器梯醒。

3. Looking at the Existing App

接下來,打開DogeChat項目并構(gòu)建并運行它以查看已經(jīng)為您構(gòu)建的內(nèi)容腌紧。

如上所示茸习,DogeChat當(dāng)前設(shè)置為允許用戶輸入用戶名然后進入聊天室。

不幸的是壁肋,上一個工作的人不知道如何編寫聊天應(yīng)用程序号胚,所以你得到的只是用戶界面和基本的管道,你必須實現(xiàn)網(wǎng)絡(luò)層浸遗。


Creating a Chat Room

要開始實際編碼猫胁,請導(dǎo)航到ChatRoomViewController.swift。在這里乙帮,您可以看到您有一個準(zhǔn)備好的視圖控制器杜漠,并且能夠從輸入欄接收字符串作為消息。它還可以通過table view顯示消息察净,其中包含使用Message對象配置的自定義單元格驾茴。

由于您已經(jīng)有了ChatRoomViewController,因此創(chuàng)建一個ChatRoom類來處理繁重的工作是有意義的氢卡。

在開始編寫新類之前锈至,請快速列出其職責(zé)。您希望此類負(fù)責(zé)以下任務(wù):

  • 1) 打開與聊天室服務(wù)器的連接译秦。
  • 2) 允許用戶通過提供用戶名加入聊天室峡捡。
  • 3) 允許用戶發(fā)送和接收消息击碗。
  • 4) 完成后關(guān)閉連接。

既然您知道自己想要什么们拙,請按Command-N創(chuàng)建一個新文件稍途。選擇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類并聲明了您需要進行通信的屬性。

  • 1) 首先装盯,聲明輸入和輸出流坷虑。 通過一起使用這對類,您可以在應(yīng)用程序和聊天服務(wù)器之間創(chuàng)建基于socket的連接埂奈。 當(dāng)然迄损,您將通過輸出流發(fā)送消息并通過輸入流接收消息。
  • 2) 接下來账磺,您定義username芹敌,您將在其中存儲當(dāng)前用戶的名稱。
  • 3) 最后绑谣,您聲明maxReadLength党窜。 此常量會限制您可以在任何單個消息中發(fā)送的數(shù)據(jù)量拗引。

接下來借宵,轉(zhuǎn)到ChatRoomViewController.swift并將chat room屬性添加到頂部的屬性列表中。

let chatRoom = ChatRoom()

現(xiàn)在您已經(jīng)設(shè)置了類的基本結(jié)構(gòu)矾削,現(xiàn)在是時候敲掉清單中的第一件事了:打開應(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è)置兩個未初始化的套接字流欲间,而不進行自動內(nèi)存管理。
  • 2) 然后將讀取和寫入套接字流綁定在一起断部,并將它們連接到主機的套接字猎贴,在這種情況下,它位于端口80上蝴光。

該函數(shù)有四個參數(shù)她渴。第一個是初始化流時要使用的分配器allocator類型。你應(yīng)該盡可能使用kCFAllocatorDefault蔑祟,但如果你遇到需要的東西有點不同的情況趁耗,還有其他選擇。

接下來疆虚,指定hostname苛败。在這種情況下满葛,您將連接到本地計算機;如果您有遠程服務(wù)器的特定IP地址罢屈,您也可以在此處使用它嘀韧。

然后,指定您通過port 80連接缠捌,這是服務(wù)器偵聽的端口乳蛾。

最后,將指針傳遞給讀取和寫入流鄙币,以便函數(shù)可以使用它在內(nèi)部創(chuàng)建的連接讀取和寫入流來初始化它們肃叶。

現(xiàn)在您已經(jīng)初始化了流,您可以通過在setupNetworkCommunication()的末尾添加以下行來存儲對它們的保留引用:

inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()

在非管理對象上調(diào)用takeRetainedValue()允許您同時獲取保留的引用并去掉不平衡的retain十嘿,以便以后不會泄漏內(nèi)存因惭。 現(xiàn)在,您可以在需要時使用輸入和輸出流绩衷。

接下來蹦魔,您需要將這些流添加到run loop中,以便您的應(yīng)用程序能夠正確地響應(yīng)網(wǎng)絡(luò)事件咳燕。 通過將這兩行添加到setupNetworkCommunication()的末尾來實現(xiàn):

inputStream.schedule(in: .current, forMode: .common)
outputStream.schedule(in: .current, forMode: .common)

最后勿决,你準(zhǔn)備打開防洪閘了! 要開始派對招盲,請?zhí)砑拥?code>setupNetworkCommunication()的底部:

inputStream.open()
outputStream.open()

這就是它的全部內(nèi)容低缩。 要完成望门,請轉(zhuǎn)到ChatRoomViewController.swift并將以下行添加到viewWillAppear(_ :)

chatRoom.setupNetworkCommunication()

您現(xiàn)在在客戶端應(yīng)用程序和在localhost上運行的服務(wù)器之間建立了打開的連接辣恋。

如果需要,您可以構(gòu)建和運行您的應(yīng)用程序艾船,但是您會看到之前看到的相同內(nèi)容顶籽,因為您尚未嘗試對連接執(zhí)行任何操作玩般。


Joining the Chat

現(xiàn)在您已經(jīng)建立了與服務(wù)器的連接,現(xiàn)在是時候開始進行通信了礼饱! 你要說的第一件事就是你認(rèn)為自己到底是誰坏为。 之后,您將要開始向人們發(fā)送消息镊绪。

這提出了一個重點:由于您有兩種消息匀伏,因此您需要找到一種方法來區(qū)分它們。

1. The Communication Protocol

使用底層TCP級別的一個優(yōu)點是您可以定義自己的“協(xié)議”來決定消息是否有效镰吆。

使用HTTP帘撰,您需要考慮所有那些討厭的動詞,如GET万皿,PUT和PATCH摧找。 您需要構(gòu)建URL并使用適當(dāng)?shù)?code>header和各種東西核行。

這里你只有兩種消息。 你可以發(fā)送:

iam:Luke

進入房間并告知世界你的消息蹬耘,你可以說:

msg:Hey, how goes it, man?

向房間里的其他人發(fā)送消息芝雪。

這很簡單但也很不安全,所以不要在工作中使用它综苔。

現(xiàn)在您已了解服務(wù)器的期望惩系,您可以在ChatRoom上編寫一個方法以允許用戶進入聊天室。 它需要的唯一參數(shù)是所需的用戶名如筛。

要實現(xiàn)它堡牡,請在您剛剛在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ù)雜一點爬坑,但是write(_:maxLength :)引用了一個不安全的指向字節(jié)的指針作為你在上一步中創(chuàng)建的第一個參數(shù)。

現(xiàn)在您的方法已準(zhǔn)備就緒涂臣,打開ChatRoomViewController.swift并添加一個調(diào)用以在viewWillAppear(_ :)底部加入聊天盾计。

chatRoom.joinChat(username: username)

現(xiàn)在,構(gòu)建并運行您的應(yīng)用程序肉康。 輸入您的姓名闯估,然后點按return以查看...

還是一樣沒變化?

現(xiàn)在灼舍,堅持下去吼和,有一個很好的解釋。 轉(zhuǎn)到您的終端骑素。 在Listening on 127.0.0.1:80下炫乓,如果你的名字不是Brody,你應(yīng)該看到Brody has joined献丑,或類似的東西末捣。

這是個好消息,但你寧愿在手機屏幕上看到一些成功的跡象创橄。


Reacting to Incoming Messages

服務(wù)器發(fā)送傳入的消息箩做,例如您剛剛發(fā)送給房間中每個人的加入消息,包括您妥畏。 幸運的是邦邦,你的應(yīng)用程序已經(jīng)設(shè)置為在ChatRoomViewController的消息表中顯示任何類型的傳入消息作為單元格安吁。

您需要做的就是使用inputStream來捕獲這些消息,將它們轉(zhuǎn)換為Message對象燃辖,然后將它們傳遞給該表來完成它的工作鬼店。

為了對收到的消息做出反應(yīng),您需要做的第一件事就是讓您的chat room成為輸入流的代理黔龟。

為此妇智,請轉(zhuǎn)到ChatRoom.swift的底部并添加以下擴展名。

extension ChatRoom: StreamDelegate {
}

既然您已經(jīng)說過遵循StreamDelegate氏身,那么您可以聲稱自己是inputStream的代理巍棱。

在調(diào)度調(diào)用schedule(in:forMode:)之前直接將以下行添加到setupNetworkCommunication()

inputStream.delegate = self

接下來,將此實現(xiàn)的stream(_:handle :)添加到擴展名:

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)備好對與Stream相關(guān)的傳入事件做些什么拉盾。 您真正感興趣的事件是.hasBytesAvailable,因為它表示有一條要傳入的消息要讀取豁状。

接下來捉偏,您將編寫一個方法來處理這些傳入的消息。 在您剛添加的方法下方泻红,添加:

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è)置一個緩沖區(qū),您可以在其中讀取傳入的字節(jié)谊路。
  • 2) 接下來讹躯,只要輸入流具有要讀取的字節(jié),就會循環(huán)缠劝。
  • 3) 在每一點上潮梯,您將調(diào)用read(_:maxLength :),它將從流中讀取字節(jié)并將它們放入您傳入的緩沖區(qū)中惨恭。
  • 4) 如果對read的調(diào)用返回負(fù)值秉馏,則會發(fā)生一些錯誤并退出。

您需要在輸入流具有可用字節(jié)的情況下調(diào)用此方法脱羡,因此請在stream(_:handle :)中的switch語句中轉(zhuǎn)到.hasBytesAvailable萝究,并在print語句下面調(diào)用您正在處理的方法。

readAvailableBytes(stream: aStream as! InputStream)

在這一點上锉罐,你有一個充滿字節(jié)的甜蜜緩沖區(qū)帆竹!

在完成此方法之前,您需要編寫另一個幫助程序以將buffer轉(zhuǎn)換為Message對象脓规。

將以下方法定義放在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在完成文本時釋放字節(jié)緩沖區(qū)然后將傳入消息拆分為:character舷暮,這樣你可以將發(fā)件人的姓名和消息視為單獨的字符串。
  • 2) 接下來噩茄,您將確定您或其他人是否根據(jù)姓名發(fā)送了消息下面。 在生產(chǎn)應(yīng)用程序中,您需要使用某種唯一的token绩聘,但就目前而言沥割,這已經(jīng)足夠了。
  • 3) 最后凿菩,使用您收集的部分構(gòu)造一條消息并將其返回机杜。

要使用Message構(gòu)造方法,請在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)于新消息蚀苛,但你沒有對它的引用。 由于它擁有對ChatRoom的強引用玷氏,因此您不希望顯式創(chuàng)建循環(huán)依賴關(guān)系并創(chuàng)建ChatRoomViewController堵未。

這是設(shè)置代理協(xié)議的最佳時機。 ChatRoom不關(guān)心什么樣的對象想知道新消息盏触,它只是想告訴別人渗蟹。

ChatRoom.swift的頂部,添加簡單的協(xié)議定義:

protocol ChatRoomDelegate: class {
  func received(message: Message)
}

接下來赞辩,在ChatRoom類的頂部雌芽,添加一個弱的可選屬性來保存對任何人決定成為ChatRoom委托的引用:

weak var delegate: ChatRoomDelegate?

現(xiàn)在,您可以返回ChatRoom.swift并通過為messageif-let中添加以下內(nèi)容來完成readAvailableBytes(stream :)辨嗽,方法中的最后一條注釋下方:

delegate?.received(message: message)

要完成世落,請返回ChatRoomViewController.swift并添加以下擴展名,該擴展名符合此協(xié)議召庞,位于MessageInputDelegate下方:

extension ChatRoomViewController: ChatRoomDelegate {
  func received(message: Message) {
    insertNewMessageCell(message)
  }
}

入門項目包括其余的管道岛心,因此insertNewMessageCell(_ :)將接收您的消息并將適當(dāng)?shù)膯卧裉砑拥奖碇小?/p>

現(xiàn)在,通過在viewWillAppear(_ :)中調(diào)用super之后立即添加以下行篮灼,將視圖控制器指定為chatRoom的代理:

chatRoom.delegate = self

再次構(gòu)建并運行您的應(yīng)用并在text field中輸入您的名字,然后點按return徘禁。

??聊天室現(xiàn)在成功顯示一個單元格诅诱,說明您已進入房間。 您已經(jīng)正式向基于socketTCP服務(wù)器發(fā)送消息并從其接收消息送朱。


Sending Messages

現(xiàn)在您已經(jīng)設(shè)置了ChatRoom來發(fā)送和接收消息娘荡,現(xiàn)在是時候允許用戶來回發(fā)送實際文本了干旁。

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)
  }
}

這個方法就像你之前寫的joinChat(username :)炮沐,除了它將msg添加到你發(fā)送的文本以表示為實際消息争群。

由于您想在inputBar告訴ChatRoomViewController用戶已點擊Send時發(fā)送消息,請返回ChatRoomViewController.swift并找到MessageInputDelegate大年。

在這里换薄,您將看到一個名為sendWasTapped(message :)的空方法,該方法在此時被調(diào)用翔试。 要發(fā)送消息轻要,請將其傳遞給chatRoom

chatRoom.send(message: message)

這就完成了全部! 由于服務(wù)器將收到此消息垦缅,然后將其轉(zhuǎn)發(fā)給所有人冲泥,因此ChatRoom會以與您加入房間時相同的方式收到新消息的通知。

構(gòu)建并運行您的應(yīng)用程序壁涎,然后繼續(xù)為自己嘗試消息傳遞凡恍。

如果您想看到有人聊天,請轉(zhuǎn)到新的終端窗口并輸入:

nc localhost 80

這將允許您在命令行上連接到TCP服務(wù)器怔球。 現(xiàn)在咳焚,您可以發(fā)出應(yīng)用程序用于在那里聊天的相同命令。

iam:gregg

然后庞溜,發(fā)送一個消息:

msg:Ay mang, wut's good?

恭喜革半,你已經(jīng)成功寫了一個聊天客戶端!


Cleaning up After Yourself

如果您曾對文件進行過任何編程流码,那么您應(yīng)該知道好的習(xí)慣是在完成文件后會close files又官。 事實證明Unix通過文件句柄代表一個開放的套接字連接,就像其他一切一樣漫试。 這意味著你需要在完成后關(guān)閉它六敬,就像任何其他文件一樣。

為此驾荣,在ChatRoom.swift中定義send(message :)后添加以下方法:

func stopChatSession() {
  inputStream.close()
  outputStream.close()
}

正如您可能已經(jīng)猜到的那樣外构,這會關(guān)閉流并使其無法發(fā)送或接收信息。 這些調(diào)用還會從您之前調(diào)度的運行循環(huán)中刪除流播掷。

要完成這項任務(wù)审编,請將此方法調(diào)用添加到stream(_:handle :)內(nèi)的switch語句中的.endEncountered案例中:

stopChatSession()

然后,返回ChatRoomViewController.swift并將相同的行添加到viewWillDisappear(_ :)

chatRoom.stopChatSession()

這樣就全部完成了歧匈。

現(xiàn)在你已經(jīng)掌握了(或者至少看過一個簡單的例子)與套接字聯(lián)網(wǎng)的基礎(chǔ)知識垒酬,有幾個地方可以擴展你的視野。

UDP Sockets

這個iOS流教程是使用TCP進行通信的一個示例,它打開了一個連接勘究,并保證數(shù)據(jù)包將盡可能到達目的地矮湘。

或者,您也可以使用UDP或數(shù)據(jù)報套接字進行通信口糕。這些套接字無法保證數(shù)據(jù)包將到達缅阳,這意味著它們的速度更快,開銷也更少景描。

它們對游戲等應(yīng)用非常有用十办。曾經(jīng)歷過滯后?這意味著你的連接不良伏伯,你應(yīng)該收到的很多UDP數(shù)據(jù)包都會被丟棄橘洞。

WebSockets

對這樣的應(yīng)用程序使用HTTP的另一種替代方法是稱為WebSockets的技術(shù)。

與傳統(tǒng)的TCP套接字不同说搅,WebSocket至少與HTTP保持關(guān)系炸枣,并且可以實現(xiàn)與傳統(tǒng)套接字相同的實時通信目標(biāo),所有這些都來自瀏覽器的舒適性和安全性弄唧。

當(dāng)然适肠,您也可以將WebSockets與iOS應(yīng)用程序一起使用。

Beej's Guide to Network Programming

最后候引,如果您真的想深入了解網(wǎng)絡(luò)侯养,請查看免費在線書籍Beej的網(wǎng)絡(luò)編程指南Beej's Guide to Network Programming

本書提供了有關(guān)套接字編程的詳盡解釋澄干。如果你害怕C逛揩,那么這本書可能有點令人生畏,但話又說回來麸俘,也許今天是你面對恐懼的那一天辩稽。

后記

本篇主要講述了基于Socket和TCP網(wǎng)絡(luò)的實時聊天信息流的實現(xiàn),感興趣的給個贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末从媚,一起剝皮案震驚了整個濱河市逞泄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拜效,老刑警劉巖喷众,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異紧憾,居然都是意外死亡到千,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門稻励,熙熙樓的掌柜王于貴愁眉苦臉地迎上來父阻,“玉大人愈涩,你說我怎么就攤上這事望抽〖用” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵煤篙,是天一觀的道長斟览。 經(jīng)常有香客問我,道長辑奈,這世上最難降的妖魔是什么苛茂? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鸠窗,結(jié)果婚禮上妓羊,老公的妹妹穿的比我還像新娘。我一直安慰自己稍计,他們只是感情好躁绸,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著臣嚣,像睡著了一般净刮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上硅则,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天淹父,我揣著相機與錄音,去河邊找鬼怎虫。 笑死暑认,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的大审。 我是一名探鬼主播蘸际,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼饥努!你這毒婦竟也來了捡鱼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酷愧,失蹤者是張志新(化名)和其女友劉穎驾诈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溶浴,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡乍迄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了士败。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闯两。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡褥伴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漾狼,到底是詐尸還是另有隱情重慢,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布逊躁,位于F島的核電站似踱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏稽煤。R本人自食惡果不足惜核芽,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酵熙。 院中可真熱鬧轧简,春花似錦、人聲如沸匾二。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽假勿。三九已至借嗽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間转培,已是汗流浹背恶导。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浸须,地道東北人惨寿。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像删窒,于是被迫代替她去往敵國和親裂垦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容