IOS Socket使用大全

Socket使用大全

第一部分、概念的理解

1、什麼是Socket?

Socket又稱之爲“套接字”,是系統提供的用於網絡通信的方法。它的實質並不是一種協議,沒有規定計算機應當怎麼樣傳遞消息,只是給程序員提供了一個發送消息的接口,程序員使用這個接口提供的方法,發送與接收消息。

Socket描述了一個IP、端口對。它簡化了程序員的操作,知道對方的IP以及PORT就可以給對方發送消息,再由服務器端來處理髮送的這些消息。所以,Socket一定包含了通信的雙發,即客戶端(Client)與服務端(server)。

2、Socket的通信過程?

每一個應用或者說服務,都有一個端口。比如DNS的53端口,http的80端口。我們能由DNS請求到查詢信息,是因爲DNS服務器時時刻刻都在監聽53端口,當收到我們的查詢請求以後,就能夠返回我們想要的IP信息。所以,從程序設計上來講,應該包含以下步驟:

1)服務端利用Socket監聽端口;

2)客戶端發起連接;

3)服務端返回信息,建立連接,開始通信;

4)客戶端,服務端斷開連接。

3、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連接究竟有什麼區別,希望通過自己的淺顯理解能對初學者有所幫助。

1、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連接的請求,斷開過程需要經過“四次握手”(過程就不細寫了,就是服務器和客戶端交互,最終確定斷開)

 

2、HTTP連接

HTTP協議即超文本傳送協議(HypertextTransfer Protocol ),是Web聯網的基礎,也是手機聯網常用的協議之一,HTTP協議是建立在TCP協議之上的一種應用。

HTTP連接最顯著的特點是客戶端發送的每次請求都需要服務器回送響應,在請求結束後,會主動釋放連接。從建立連接到關閉連接的過程稱爲“一次連接”。

1)在HTTP 1.0中,客戶端的每次請求都要求建立一次單獨的連接,在處理完本次請求後,就自動釋放連接。

 

2)在HTTP 1.1中則可以在一次連接中處理多個請求,並且多個請求可以重疊進行,不需要等待一個請求結束後再發送下一個請求。

 

由於HTTP在每次請求結束後都會主動釋放連接,因此HTTP連接是一種“短連接”,要保持客戶端程序的在線狀態,需要不斷地向服務器發起連接請求。通常的做法是即時不需要獲得任何數據,客戶端也保持每隔一段固定的時間向服務器發送一次“保持連接”的請求,服務器在收到該請求後對客戶端進行回覆,表明知道客戶端“在線”。若服務器長時間無法收到客戶端的請求,則認爲客戶端“下線”,若客戶端長時間無法收到服務器的回覆,則認爲網絡已經斷開。

 

3、SOCKET原理

3.1套接字(socket)概念

套接字(socket)是通信的基石,是支持TCP/IP協議的網絡通信的基本操作單元。它是網絡通信過程中端點的抽象表示,包含進行網絡通信必須的五種信息:連接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。

應用層通過傳輸層進行數據通信時,TCP會遇到同時爲多個應用程序進程提供併發服務的問題。多個TCP連接或多個應用程序進程可能需要通過同一個 TCP協議端口傳輸數據。爲了區別不同的應用程序進程和連接,許多計算機操作系統爲應用程序與TCP/IP協議交互提供了套接字(Socket)接口。應用層可以和傳輸層通過Socket接口,區分來自不同應用程序進程或網絡連接的通信,實現數據傳輸的併發服務。

 

3.2 建立socket連接

建立Socket連接至少需要一對套接字,其中一個運行於客戶端,稱爲ClientSocket,另一個運行於服務器端,稱爲ServerSocket。

套接字之間的連接過程分爲三個步驟:服務器監聽,客戶端請求,連接確認。

服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求。

客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然後就向服務器端套接字提出連接請求。

連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。

 

4、SOCKET連接與TCP連接

創建Socket連接時,可以指定使用的傳輸層協議,Socket可以支持不同的傳輸層協議(TCP或UDP),當使用TCP協議進行連接時,該Socket連接就是一個TCP連接。

 

5、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對象

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章