關(guān)于網(wǎng)絡(luò)編程,這幾篇文章是對iOS開發(fā)中網(wǎng)絡(luò)編程實(shí)用的一些指導(dǎo),簡單整理出來以備查詢.各位大神如果有深入的研究的或者有什么好的使用技巧了都可以交流下哦,歡迎指出使用不當(dāng)之處
- iOS中網(wǎng)絡(luò)編程大體分為三類,第一種是基于C的BSD socket編程,是最底層的,第二種是基于C的CFNetwork,屬于Core Foundation框架,第三種是屬于cocoa類的,NSURL,NSStream,Bonjour等.第三種是比較常用的,其中NSStream 其實(shí)是 對 CFNetwork 簡單封裝,對于數(shù)據(jù)的處理,NSStream是用的代理,CFNetwork是用的函數(shù)回調(diào).
客戶端和服務(wù)端編程流程:
1,客戶端socket編程:創(chuàng)建套接字socket(),建立套接字鏈接connect(),數(shù)據(jù)傳輸send()和recv(),關(guān)閉套接字close().
2,服務(wù)端scoket編程:創(chuàng)建套接字socket(),指定本地地址bind(),監(jiān)聽鏈接listen(),接收鏈接accept(),數(shù)據(jù)傳輸send()和recv(),關(guān)閉套接字close(),關(guān)閉最初套接字close().
** socket編程時序圖:面向連接 **
** 使用socket有一個模式,即所有的程序幾乎毫無例外地按照相同的順序調(diào)用相同的函數(shù),套路基本是固定的,如果第一次做socket編程的時候封裝好了,以后就可以直接用了**
- ** 客戶端的套接字系統(tǒng)調(diào)用說明 **
1.創(chuàng)建并初始化套接字──socket()
需要導(dǎo)入的頭文件:
#import <sys/socket.h>
#import <netinet/in.h>
#import <netdb.h>
/**
第一個參數(shù):指定通信的發(fā)生區(qū)域,通常用的是AF_INET(IPv4)和AF_INET6(IPv6)
第二個參數(shù):套接字的類型,總共有三種:流式套接字(SOCK_STREAM),數(shù)據(jù)報文datagram(SOCK_DGRAM),原始式套接字(SOCK_RAW),最后一個不常用.
第三個參數(shù):套接字使用的特定協(xié)議,一般傳0,使用默認(rèn)的鏈接模式
返回值:整型的套接字號或者叫文件描述符,創(chuàng)建失敗返回-1
int socket(int, int, int);
*/
// 創(chuàng)建socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
2.建立套接字連接──connect(),客戶端通過調(diào)用該接口向服務(wù)器發(fā)起建立連接請求
/**
其實(shí),在建立連接請求前,需要對IP地址進(jìn)行查找,如果找不到對應(yīng)的IP返回NULL.
參數(shù)傳遞的是IP的char類型的字符串,返回一個指向結(jié)構(gòu)體hostent的指針,結(jié)構(gòu)體hostent記錄著主機(jī)的信息,包括主機(jī)名番宁、別名、地址類型拐纱、地址長度和地址列表
struct hostent *gethostbyname(const char *);
*/
// 事例代碼,host是IP地址的OC字符串
struct hostent *remoteHp = gethostbyname([host UTF8String]);
/**
第一個參數(shù):準(zhǔn)備建立的本地套接字描述符(套接字號)
第二個參數(shù):套接字地址結(jié)構(gòu)體指針,就是連接的參數(shù)
第三個參數(shù):套接字地址結(jié)構(gòu)體的長度
返回值:整型,判斷連接是否成功,如果連接成功返回0,失敗返回-1.
說明:對于 UDP 來說,該接口是可選的惕味,如果調(diào)用了該接口垒拢,表明設(shè)置了該 UDP socket 默認(rèn)的網(wǎng)絡(luò)地址。對 TCP socket來說這就是傳說中三次握手建立連接發(fā)生的地方
注意:該接口調(diào)用會阻塞當(dāng)前線程悦荒,直到服務(wù)器返回
int connect(int, const struct sockaddr *, socklen_t)
*/
// 事例代碼
/**
struct sockaddr_in 用來描述套接字地址的結(jié)構(gòu)體
*/
struct sockaddr_in sockPara;
/**
memcpy:內(nèi)存copy函數(shù),從源src所指的內(nèi)存地址的起始位置開始拷貝n個字節(jié)到目標(biāo)dest所指的內(nèi)存地址的起始位置中
void *memcpy(void *dest, const void *src, size_t n);
*/
// 下面的代碼可以替換成:
// sockPara.sin_addr = (struct in_addr*)remoteHp->h_addr_list[0];
memcpy((char *)&sockPara.sin_addr, (char *)remoteHp->h_addr_list[0], remoteHp->h_length);
sockPara.sin_family = ip;
sockPara.sin_port = htons([port intValue]);
int connectRet = connect(sock, (struct sockaddr *)&sockPara, sizeof(sockPara));
3.數(shù)據(jù)傳輸──send()和recv()
// 注意:數(shù)據(jù)的發(fā)送和接收回阻塞當(dāng)前線程
// 發(fā)送數(shù)據(jù)
/**
第一個參數(shù):已經(jīng)連接的本地套接字描述符
第二個參數(shù):指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針
第三個參數(shù):指定第二個參數(shù)的長度
第四個參數(shù):指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等
返回值:如果沒有發(fā)生錯誤,返回的是總共發(fā)送的字節(jié)數(shù),否則返回-1
ssize_t send(int, const void *, size_t, int);
*/
// 接收數(shù)據(jù)
/**
第一個參數(shù):已經(jīng)連接的本地套接字描述符
第二個參數(shù):指向接收輸入數(shù)據(jù)緩沖區(qū)的指針
第三個參數(shù):指定第二個參數(shù)的長度
第四個參數(shù):指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等
返回值:返回總共接收的字節(jié)數(shù),如果連接被關(guān)閉返回0,否則返回-1
ssize_t recv(int, void *, size_t, int)
*/
// 事例代碼
ssize_t result = recv(sock, &buf, sizeof(buf), 0);
4.關(guān)閉──close()
// 傳入的參數(shù):已經(jīng)打開的本地套接字描述符
// 關(guān)閉套接字,并釋放分配給該套接字的資源
int close(int)
- 事例代碼GGSockClient
參考資料:
- 羅朝輝的iOS網(wǎng)絡(luò)編程之Socket
- BSD-socket 第十六章:網(wǎng)絡(luò)系統(tǒng)
- socket編程原理 第二章:socket 編程原理
- Socket網(wǎng)絡(luò)編程
非常感謝羅朝輝大神的無償分享