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對象
}