第一部分、概念的理解程序員
一、什麼是Socket?數據庫
Socket又稱之爲「套接字」,是系統提供的用於網絡通訊的方法。它的實質並非一種協議,沒有規定計算機應當怎麼樣傳遞消息,只是給程序員提供了一個發送消息的接口,程序員使用這個接口提供的方法,發送與接收消息。編程
Socket描述了一個IP、端口對。它簡化了程序員的操做,知道對方的IP以及PORT就能夠給對方發送消息,再由服務器端來處理髮送的這些消息。因此,Socket必定包含了通訊的雙發,即客戶端(Client)與服務端(server)。瀏覽器
二、Socket的通訊過程?緩存
每個應用或者說服務,都有一個端口。好比DNS的53端口,http的80端口。咱們能由DNS請求到查詢信息,是由於DNS服務器時時刻刻都在監聽53端口,當收到咱們的查詢請求之後,就可以返回咱們想要的IP信息。因此,從程序設計上來說,應該包含如下步驟:服務器
1)服務端利用Socket監聽端口;網絡
2)客戶端發起鏈接;併發
3)服務端返回信息,創建鏈接,開始通訊;異步
4)客戶端,服務端斷開鏈接。socket
三、Socket雙方如何創建起鏈接?
如下過程用代碼表示:
Server端:
1 intport = 2000;
2 IPEndPointServerEP = new IPEndPoint(IPAddress.Any,port);
3 Socketserver = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
4 server.Bind(ServerEP);
5 server.Listen(0);
Client端:
1 intport = 2000;
2 IPAddressserverip = IPAddress.Parse("192.168.1.100");
3 IPEndPointEP = new IPEndPoint(server,port);
4 Socketserver = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
5 server.Bind(EP);
當服務器端接收到來自客戶端的鏈接之後,須要新建一個socket來處理遠端的信息。
下面一段代碼應該在服務器端:
1 Socketclient = server.Accept();
以上很簡單的幾行代碼,將在之後的網絡編程中常常用到,後面還會有同步通信、異步通信、線程、委託與事件等等
第二部分、各協議的區別
TCP/IP SOCKET HTTP
網絡七層由下往上分別爲物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。
其中物理層、數據鏈路層和網絡層一般被稱做媒體層,是網絡工程師所研究的對象;
傳輸層、會話層、表示層和應用層則被稱做主機層,是用戶所面向和關心的內容。
http協議 對應於應用層
tcp協議 對應於傳輸層
ip協議 對應於網絡層
三者本質上沒有可比性。 況且HTTP協議是基於TCP鏈接的。
TCP/IP是傳輸層協議,主要解決數據如何在網絡中傳輸;而HTTP是應用層協議,主要解決如何包裝數據。
咱們在傳輸數據時,能夠只使用傳輸層(TCP/IP),可是那樣的話,因爲沒有應用層,便沒法識別數據內容,若是想要使傳輸的數據有意義,則必須使用應用層協議,應用層協議不少,有HTTP、FTP、TELNET等等,也能夠本身定義應用層協議。WEB使用HTTP做傳輸層協議,以封裝HTTP文本信息,而後使用TCP/IP作傳輸層協議將它發送到網絡上。
Socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API),經過Socket,咱們才能使用TCP/IP協議。
Http和Socket鏈接區別
相信很多初學手機聯網開發的朋友都想知道Http與Socket鏈接究竟有什麼區別,但願經過本身的淺顯理解能對初學者有所幫助。
一、TCP鏈接
要想明白Socket鏈接,先要明白TCP鏈接。手機可以使用聯網功能是由於手機底層實現了TCP/IP協議,可使手機終端經過無線網絡創建TCP鏈接。TCP協議能夠對上層網絡提供接口,使上層網絡數據的傳輸創建在「無差異」的網絡之上。
創建起一個TCP鏈接須要通過「三次握手」:
第一次握手:客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP鏈接一旦創建,在通訊雙方中的任何一方主動關閉鏈接以前,TCP 鏈接都將被一直保持下去。斷開鏈接時服務器和客戶端都可以主動發起斷開TCP鏈接的請求,斷開過程須要通過「四次握手」(過程就不細寫了,就是服務器和客戶端交互,最終肯定斷開)
二、HTTP鏈接
HTTP協議即超文本傳送協議(HypertextTransfer Protocol ),是Web聯網的基礎,也是手機聯網經常使用的協議之一,HTTP協議是創建在TCP協議之上的一種應用。
HTTP鏈接最顯著的特色是客戶端發送的每次請求都須要服務器回送響應,在請求結束後,會主動釋放鏈接。從創建鏈接到關閉鏈接的過程稱爲「一次鏈接」。
1)在HTTP 1.0中,客戶端的每次請求都要求創建一次單獨的鏈接,在處理完本次請求後,就自動釋放鏈接。
2)在HTTP 1.1中則能夠在一次鏈接中處理多個請求,而且多個請求能夠重疊進行,不須要等待一個請求結束後再發送下一個請求。
因爲HTTP在每次請求結束後都會主動釋放鏈接,所以HTTP鏈接是一種「短鏈接」,要保持客戶端程序的在線狀態,須要不斷地向服務器發起鏈接請求。一般的作法是即時不須要得到任何數據,客戶端也保持每隔一段固定的時間向服務器發送一次「保持鏈接」的請求,服務器在收到該請求後對客戶端進行回覆,代表知道客戶端「在線」。若服務器長時間沒法收到客戶端的請求,則認爲客戶端「下線」,若客戶端長時間沒法收到服務器的回覆,則認爲網絡已經斷開。
三、SOCKET原理
3.1套接字(socket)概念
套接字(socket)是通訊的基石,是支持TCP/IP協議的網絡通訊的基本操做單元。它是網絡通訊過程當中端點的抽象表示,包含進行網絡通訊必須的五種信息:鏈接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。
應用層經過傳輸層進行數據通訊時,TCP會遇到同時爲多個應用程序進程提供併發服務的問題。多個TCP鏈接或多個應用程序進程可能須要經過同一個 TCP協議端口傳輸數據。爲了區別不一樣的應用程序進程和鏈接,許多計算機操做系統爲應用程序與TCP/IP協議交互提供了套接字(Socket)接口。應用層能夠和傳輸層經過Socket接口,區分來自不一樣應用程序進程或網絡鏈接的通訊,實現數據傳輸的併發服務。
3.2 創建socket鏈接
創建Socket鏈接至少須要一對套接字,其中一個運行於客戶端,稱爲ClientSocket,另外一個運行於服務器端,稱爲ServerSocket。
套接字之間的鏈接過程分爲三個步驟:服務器監聽,客戶端請求,鏈接確認。
服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待鏈接的狀態,實時監控網絡狀態,等待客戶端的鏈接請求。
客戶端請求:指客戶端的套接字提出鏈接請求,要鏈接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要鏈接的服務器的套接字,指出服務器端套接字的地址和端口號,而後就向服務器端套接字提出鏈接請求。
鏈接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的鏈接請求時,就響應客戶端套接字的請求,創建一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式創建鏈接。而服務器端套接字繼續處於監聽狀態,繼續接收其餘客戶端套接字的鏈接請求。
四、SOCKET鏈接與TCP鏈接
建立Socket鏈接時,能夠指定使用的傳輸層協議,Socket能夠支持不一樣的傳輸層協議(TCP或UDP),當使用TCP協議進行鏈接時,該Socket鏈接就是一個TCP鏈接。
五、Socket鏈接與HTTP鏈接
因爲一般狀況下Socket鏈接就是TCP鏈接,所以Socket鏈接一旦創建,通訊雙方便可開始相互發送數據內容,直到雙方鏈接斷開。但在實際網絡應用中,客戶端到服務器之間的通訊每每須要穿越多箇中間節點,例如路由器、網關、防火牆等,大部分防火牆默認會關閉長時間處於非活躍狀態的鏈接而致使 Socket 鏈接斷連,所以須要經過輪詢告訴網絡,該鏈接處於活躍狀態。
而HTTP鏈接使用的是「請求—響應」的方式,不只在請求時須要先創建鏈接,並且須要客戶端向服務器發出請求後,服務器端才能回覆數據。
不少狀況下,須要服務器端主動向客戶端推送數據,保持客戶端與服務器數據的實時與同步。此時若雙方創建的是Socket鏈接,服務器就能夠直接將數據傳送給客戶端;若雙方創建的是HTTP鏈接,則服務器須要等到客戶端發送一次請求後才能將數據傳回給客戶端,所以,客戶端定時向服務器端發送鏈接請求,不只能夠保持在線,同時也是在「詢問」服務器是否有新的數據,若是有就將數據傳給客戶端。
HTTP鏈接是什麼意思
HTTP是一個屬於應用層的面向對象的協議,因爲其簡捷、快速的方式,適用於分佈式超媒體信息系統。它於1990年提出,通過幾年的使用與發展,獲得不斷地完善和擴展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的規範化工做正在進行之中,並且HTTP-NG(Next Generation of HTTP)的建議已經提出.(協議,算是全球定位!)
WWW的核心——HTTP協議
衆所周知,Internet的基本協議是TCP/IP協議,目前普遍採用的FTP、Archie Gopher等是創建在TCP/IP協議之上的應用層協議,不一樣的協議對應着不一樣的應用。WWW服務器使用的主要協議是HTTP協議,即超文體傳輸協議。因爲HTTP協議支持的服務不限於WWW,還能夠是其它服務,於是HTTP協議容許用戶在統一的界面下,採用不一樣的協議訪問不一樣的服務,如FTP、Archie、SMTP、NNTP等。另外,HTTP協議還可用於名字服務器和分佈式對象管理。
2.1 HTTP協議簡介
HTTP是一個屬於應用層的面向對象的協議,因爲其簡捷、快速的方式,適用於分佈式超媒體信息系統。它於1990年提出,通過幾年的使用與發展,獲得不斷地完善和擴展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的規範化工做正在進行之中,並且HTTP-NG(Next Generation of HTTP)的建議已經提出。
HTTP協議的主要特色可歸納以下:
1.支持客戶/服務器模式。
2.簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法經常使用的有GET、HEAD, POST。每種方法規定了客戶與服務器聯繫的類型不一樣。 因爲HTTP協議簡單,使得HTTP服務器的程序規模小,於是通訊速度很快。
3.靈活:HTTP容許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
4.無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。
5.無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息,則它必須重傳,這樣可能致使每次鏈接傳送的數據量增大。另外一方面,在服務器不須要先前信息時它的應答就較快。
2.2 HTTP協議的幾個重要概念
1.鏈接(Connection):一個傳輸層的實際環流,它是創建在兩個相互通信的應用程序之間。
2.消息(Message):HTTP通信的基本單位,包括一個結構化的八元組序列並經過鏈接傳輸。
3.請求(Request):一個從客戶端到服務器的請求信息包括應用於資源的方法、資源的標識符和協議的版本號
4.響應(Response):一個從服務器返回的信息包括HTTP協議的版本號、請求的狀態(例如「成功」或「沒找到」)和文檔的MIME類型。
5.資源(Resource):由URI標識的網絡數據對象或服務。
6.實體(Entity):數據資源或來自服務資源的回映的一種特殊表示方法,它可能被包圍在一個請求或響應信息中。一個實體包括實體頭信息和實體的自己內容。
7.客戶機(Client):一個爲發送請求目的而創建鏈接的應用程序。
8.用戶代理(User agent):初始化一個請求的客戶機。它們是瀏覽器、編輯器或其它用戶工具。
9.服務器(Server):一個接受鏈接並對請求返回信息的應用程序。
10.源服務器(Origin server):是一個給定資源能夠在其上駐留或被建立的服務器。
11.代理(Proxy):一箇中間程序,它能夠充當一個服務器,也能夠充當一個客戶機,爲其它客戶機創建請求。請求是經過可能的翻譯在內部或通過傳遞到其它的服務器中。一個代理在發送請求信息以前,必須解釋而且若是可能重寫它。
代理常常做爲經過防火牆的客戶機端的門戶,代理還能夠做爲一個幫助應用來經過協議處理沒有被用戶代理完成的請求。
12.網關(Gateway):一個做爲其它服務器中間媒介的服務器。與代理不一樣的是,網關接受請求就好象對被請求的資源來講它就是源服務器;發出請求的客戶機並無意識到它在同網關打交道。
網關常常做爲經過防火牆的服務器端的門戶,網關還能夠做爲一個協議翻譯器以便存取那些存儲在非HTTP系統中的資源。
13.通道(Tunnel):是做爲兩個鏈接中繼的中介程序。一旦激活,通道便被認爲不屬於HTTP通信,儘管通道多是被一個HTTP請求初始化的。當被中繼的鏈接兩端關閉時,通道便消失。當一個門戶(Portal)必須存在或中介(Intermediary)不能解釋中繼的通信時通道被常用。
14.緩存(Cache):反應信息的局域存儲。
2.3 HTTP協議的運做方式
HTTP協議是基於請求/響應範式的。一個客戶機與服務器創建鏈接後,發送一個請求給服務器,請求方式的格式爲,統一資源標識符、協議版本號,後邊是MIME信息包括請求修飾符、客戶機信息和可能的內容。服務器接到請求後,給予相應的響應信息,其格式爲一個狀態行包括信息的協議版本號、一個成功或錯誤的代碼,後邊是MIME信息包括服務器信息、實體信息和可能的內容。
許多HTTP通信是由一個用戶代理初始化的而且包括一個申請在源服務器上資源的請求。最簡單的狀況多是在用戶代理(UA)和源服務器(O)之間經過一個單獨的鏈接來完成(見圖2-1)。
當一個或多箇中介出如今請求/響應鏈中時,狀況就變得複雜一些。中介由三種:代理(Proxy)、網關(Gateway)和通道(Tunnel)。
一個代理根據URI的絕對格式來接受請求,重寫所有或部分消息,經過URI的標識把已格式化過的請求發送到服務器。
網關是一個接收代理,做爲一些其它服務器的上層,而且若是必須的話,能夠把請求翻譯給下層的服務器協議。
一個通道做爲不改變消息的兩個鏈接之間的中繼點。當通信須要經過一箇中介(例如:防火牆等)或者是中介不能識別消息的內容時,通道常常被使用。圖2-2
上面的圖2-2代表了在用戶代理(UA)和源服務器(O)之間有三個中介(A,B和C)。一個經過整個鏈的請求或響應消息必須通過四個鏈接段。這個區別是重要的,由於一些HTTP通信選擇可能應用於最近的鏈接、沒有通道的鄰居,應用於鏈的終點或應用於沿鏈的全部鏈接。儘管圖2-2是線性的,每一個參與者均可能從事多重的、併發的通信。例如,B可能從許多客戶機接收請求而不經過A,而且/或者不經過C把請求送到A,在同時它還可能處理A的請求。
任何針對不做爲通道的匯聚可能爲處理請求啓用一個內部緩存。緩存的效果是請求/響應鏈被縮短,條件是沿鏈的參與者之一具備一個緩存的響應做用於那個請求。下圖說明結果鏈,其條件是針對一個未被UA或A加緩存的請求,B有一個通過C來自O的一個前期響應的緩存拷貝。
圖2-3
在Internet上,HTTP通信一般發生在TCP/IP鏈接之上。缺省端口是TCP 80,但其它的端口也是可用的。但這並不預示着HTTP協議在Internet或其它網絡的其它協議之上才能完成。HTTP只預示着一個可靠的傳輸。
以上簡要介紹了HTTP協議的宏觀運做方式,下面介紹一下HTTP協議的內部操做過程。
首先,簡單介紹基於HTTP協議的客戶/服務器模式的信息交換過程,如圖2-4所示,它分四個過程,創建鏈接、發送請求信息、發送響應信息、關閉鏈接。
圖2-4
在WWW中,「客戶」與「服務器」是一個相對的概念,只存在於一個特定的鏈接期間,即在某個鏈接中的客戶在另外一個鏈接中可能做爲服務器。WWW服務器運行時,一直在TCP80端口(WWW的缺省端口)監聽,等待鏈接的出現。
下面,討論HTTP協議下客戶/服務器模式中信息交換的實現。
1.創建鏈接鏈接的創建是經過申請套接字(Socket)實現的。客戶打開一個套接字並把它約束在一個端口上,若是成功,就至關於創建了一個虛擬文件。之後就能夠在該虛擬文件上寫數據並經過網絡向外傳送。
2.發送請求
打開一個鏈接後,客戶機把請求消息送到服務器的停留端口上,完成提出請求動做。
HTTP/1.0 請求消息的格式爲:
請求消息=請求行(通用信息|請求頭|實體頭) CRLF[實體內容]
請求 行=方法請求URL HTTP版本號 CRLF
方法=GET|HEAD|POST|擴展方法
U R L=協議名稱+宿主名+目錄與文件名
請求行中的方法描述指定資源中應該執行的動做,經常使用的方法有GET、HEAD和POST。
不一樣的請求對象對應GET的結果是不一樣的,對應關係以下:
對象 GET的結果
文件文件的內容
程序該程序的執行結果
數據庫查詢 查詢結果
HEAD——要求服務器查找某對象的元信息,而不是對象自己。
POST——從客戶機向服務器傳送數據,在要求服務器和CGI作進一步處理時會用到POST方法。POST主要用於發送HTML文本中FORM的內容,讓CGI程序處理。
一個請求的例子爲:
GEThttp://networking.zju.edu.cn/zju/index.htm HTTP/1.0
頭信息又稱爲元信息,即信息的信息,利用元信息能夠實現有條件的請求或應答 。
請求頭——告訴服務器怎樣解釋本次請求,主要包括用戶能夠接受的數據類型、壓縮方法和語言等。
實體頭——實體信息類型、長度、壓縮方法、最後一次修改時間、數據有效期等。
實體——請求或應答對象自己。
3.發送響應
服務器在處理完客戶的請求以後,要向客戶機發送響應消息。
HTTP/1.0的響應消息格式以下:
響應消息=狀態行(通用信息頭|響應頭|實體頭) CRLF 〔實體內容〕
狀態 行=HTTP版本號 狀態碼緣由敘述
狀態碼錶示響應類型
1×× 保留
2×× 表示請求成功地接收
3×× 爲完成請求客戶需進一步細化請求
4×× 客戶錯誤
5×× 服務器錯誤
響應頭的信息包括:服務程序名,通知客戶請求的URL須要認證,請求的資源什麼時候能使用。
4.關閉鏈接
客戶和服務器雙方均可以經過關閉套接字來結束TCP/IP對話
第三部分、在IOS裏面的使用
在CFSocket中,TCP鏈接的建立爲
csocket = CFSocketCreate(
kCFAllocatorDefault,
PF_INET,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketReadCallBack,
TCPServerConnectCallBack,
&ctx);
。。。。
sError = CFSocketConnectToAddress(csocket,address, -1);
這裏在鏈接成功時回調用TCPServerConnectCallBack方法,
那麼若是須要UDP傳輸數據的話,
csocket = CFSocketCreate(
kCFAllocatorDefault,
PF_INET,
SOCK_DGRAM,
IPPROTO_UDP,
kCFSocketConnectCallBack,
TCPServerConnectCallBack,
&ctx);
1.TCP是有鏈接的,可靠的、可控制的、無邊界的socket通訊。
2.UDP是無鏈接的、不可靠的 數據報通訊。可是效率高。
因此在TCP中要用回調來肯定是否鏈接成功,而原生的socket鏈接成功是經過serveice發回信息來肯定。UDP無鏈接,因此不須要回調,而無論是否發送成功。
第三部分、完整的使用方法
客戶端:
導入頭文件:
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <unistd.h>
1. 建立鏈接
CFSocketContext sockContext = {0, // 結構體的版本,必須爲0
self,
// 一個任意指針的數據,能夠用在建立時CFSocket對象相關聯。這個指針被傳遞給全部的上下文中定義的回調。
NULL, // 一個定義在上面指針中的retain的回調, 能夠爲NULL
NULL, NULL};
CFSocketRef _socket = (kCFAllocatorDefault,// 爲新對象分配內存,能夠爲nil
PF_INET, // 協議族,若是爲0或者負數,則默認爲PF_INET
SOCK_STREAM, // 套接字類型,若是協議族爲PF_INET,則它會默認爲SOCK_STREAM
IPPROTO_TCP, // 套接字協議,若是協議族是PF_INET且協議是0或者負數,它會默認爲IPPROTO_TCP
kCFSocketConnectCallBack, // 觸發回調函數的socket消息類型,具體見CallbackTypes
TCPServerConnectCallBack, // 上面狀況下觸發的回調函數
&sockContext // 一個持有CFSocket結構信息的對象,能夠爲nil
);
if (_socket != nil) {
struct sockaddr_in addr4; //IPV4
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8888);
addr4.sin_addr.s_addr = inet_addr([strAddress UTF8String]); // 把字符串的地址轉換爲機器可識別的網絡地址
// 把sockaddr_in結構體中的地址轉換爲Data
CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr4, sizeof(addr4));
CFSocketConnectToAddress(_socket, // 鏈接的socket
address, // CFDataRef類型的包含上面socket的遠程地址的對象
-1 // 鏈接超時時間,若是爲負,則不嘗試鏈接,而是把鏈接放在後臺進行,若是_socket消息類型爲kCFSocketConnectCallBack,將會在鏈接成功或失敗的時候在後臺觸發回調函數
);
CFRunLoopRef cRunRef = CFRunLoopGetCurrent(); // 獲取當前線程的循環
// 建立一個循環,但並無真正加如到循環中,須要調用CFRunLoopAddSource
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault,_socket, 0);
CFRunLoopAddSource(cRunRef, // 運行循環
sourceRef, // 增長的運行循環源, 它會被retain一次
kCFRunLoopCommonModes // 增長的運行循環源的模式
);
CFRelease(courceRef);
}
2. 設置回調函數
// socket回調函數的格式:
static void TCPServerConnectCallBack(CFSocketRefsocket, CFSocketCallBackType type, CFDataRef address, const void *data, void*info) {
if (data != NULL) {
// 當socket爲kCFSocketConnectCallBack時,失敗時回調失敗會返回一個錯誤代碼指針,其餘狀況返回NULL
NSLog(@"鏈接失敗");
return;
}
TCPClient *client = (TCPClient *)info;
// 讀取接收的數據
[info performSlectorInBackground:@selector(readStream) withObject:nil];
}
3. 接收發送數據
// 讀取接收的數據
- (void)readStream {
char buffer[1024];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
while (recv(CFSocketGetNative(_socket), //與本機關聯的Socket 若是已經失效返回-1:INVALID_SOCKET
buffer, sizeof(buffer), 0)) {
NSLog(@"%@", [NSString stringWithUTF8String:buffer]);
}
}
// 發送數據
- (void)sendMessage {
NSString*stringTosend = @"你好";
char *data = [stringTosend UTF8String];
send(SFSocketGetNative(_socket), data, strlen(data) + 1, 0);
}
服務器端:
CFSockteRef _socket;
CFWriteStreamRef outputStream = NULL;
int setupSocket() {
_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM,IPPROTO_TCP, kCFSocketAcceptCallBack, TCPServerAcceptCallBack, NULL);
if (NULL == _socket) {
NSLog(@"Cannot create socket!");
return 0;
}
int optval = 1;
setsockopt(CFSocketGetNative(_socket), SOL_SOCKET, SO_REUSEADDR, // 容許重用本地地址和端口
(void *)&optval, sizeof(optval));
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr4, sizeof(addr4));
if (kCFSocketSuccess != CFSocketSetAddress(_socket, address)) {
NSLog(@"Bind to address failed!");
if (_socket)
CFRelease(_socket);
_socket = NULL;
return 0;
}
CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault,_socket, 0);
CFRunLoopAddSource(cfRunLoop, source, kCFRunLoopCommonModes);
CFRelease(source);
return 1;
}
// socket回調函數,同客戶端
void TCPServerAcceptCallBack(CFSocketRefsocket, CFSocketCallBackType type, CFDataRef address, const void *data, void*info) {
if (kCFSocketAcceptCallBack == type) {
// 本地套接字句柄
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t nameLen = sizeof(name);
if (0 != getpeername(nativeSocketHandle, (struct sockaddr *)name,&nameLen)) {
NSLog(@"error");
exit(1);
}
NSLog(@"%@ connected.", inet_ntoa( ((struct sockaddr_in*)name)->sin_addr )):
CFReadStreamRef iStream;
CFWriteStreamRef oStream;
// 建立一個可讀寫的socket鏈接
CFStreamCreatePairWithSocket(kCFAllocatorDefault,nativeSocketHandle, &iStream, &oStream);
if (iStream && oStream) {
CFStreamClientContext streamContext = {0, NULL, NULL, NULL};
if (!CFReadStreamSetClient(iStream, kCFStreamEventHasBytesAvaiable,
readStream, // 回調函數,當有可讀的數據時調用
&streamContext)){
exit(1);
}
if (!CFReadStreamSetClient(iStream, kCFStreamEventCanAcceptBytes,writeStream, &streamContext)){
exit(1);
}
CFReadStreamScheduleWithRunLoop(iStream, CFRunLoopGetCurrent(),kCFRunLoopCommomModes);
CFWriteStreamScheduleWithRunLoop(wStream, CFRunLoopGetCurrent(),kCFRunLoopCommomModes);
CFReadStreamOpen(iStream);
CFWriteStreamOpen(wStream);
} else {
close(nativeSocketHandle);
}
}
}
// 讀取數據
void readStream(CFReadStreamRef stream,CFStreamEventType eventType, void *clientCallBackInfo) {
UInt8 buff[255];
CFReadStreamRead(stream, buff, 255);
printf("received: %s", buff);
}
void writeStream (CFWriteStreamRef stream, CFStreamEventTypeeventType, void *clientCallBackInfo) {
outputStream = stream;
}
main {
char *str = "nihao";
if (outputStream != NULL) {
CFWriteStreamWrite(outputStream, str, strlen(line) + 1);
} else {
NSLog(@"Cannot send data!");
}
}
// 開闢一個線程線程函數中
void runLoopInThread() {
int res = setupSocket();
if (!res) {
exit(1);
}
CFRunLoopRun(); // 運行當前線程的CFRunLoop對象
}