iOS socket編程

前言

Socket網絡編程在任何一門編程語言中都很重要,並且socket底層是純C語言,跨平臺,瞭解並熟悉底層交互是提升本身編程水平重要的一步.環環在此稍加總結,若是有童鞋要面試還能用的上,結尾附有demo案例(IOS).git

正文

  • 首先明確Socket在網絡模型中哪裏:是應用層與傳輸層之間的橋樑github


     
    image
  • 回顧一下網絡模型: OSI七層網絡模型:
    1.應用層.2.表示層.3.會話層.4.傳輸層.5.網絡層.6.數據鏈路層.7.物理層
    TCP/IP四層網絡模型:應用層.傳輸層.網絡層.網絡接入層面試

  • HTTP協議:屬於應用層面向對象的協議(超文本傳輸協議),常基於TCP鏈接方式, 特色是:
    1.支持客戶/服務端模式
    2.簡單快捷靈活
    3.客戶端發送的每次請求都須要服務器回送響應,請求結束後主動釋放鏈接.俗稱」短鏈接"編程

  • 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狀態,完成三次握手。服務器

  • UDP協議:用戶數據報協議,面向非鏈接,不保證可靠性的數據傳輸服務,沒有超時重發等機制,故而傳輸速度很快.

特色:它不與對方創建鏈接,而是直接就把數據包發送過去, UDP適用於一次只傳送少許數據、對可靠性要求不高的應用環境。網絡

  • Socket:又稱」套接字」,應用程序經過」套接字」向網絡發送請求或應答,它是一個針對TCP和UDP編程的接口,藉助它創建TCP/UDP鏈接。socket鏈接就是所謂的長鏈接,理論上客戶端和服務器端一旦創建起鏈接將不會主動斷掉.數據結構

  • HTTP協議—Socket鏈接--TCP鏈接關係:多線程

1.HTTP協議提供了封裝或者顯示數據的具體形式;
2.Socket鏈接提供了網絡通訊的能力;
3.TCP鏈接提供如何在網絡中傳輸;
4.socket是純C語言的,跨平臺;
5.HTTP協議是基於socket的,底層使用的就是socket;
6.建立Socket鏈接時,能夠指定使用的傳輸層協議(TCP或UDP),當使用TCP協議進行鏈接時,該Socket鏈接就是一個TCP鏈接。socket

  • TCP和UDP區別

1.基於鏈接和無鏈接
2.對系統資源要求(TCP較多,UDP較少)
3.UDP程序結構較簡單
4.TCP是流模式,UDP是數據報模式
5.可靠性:TCP保證數據正確性,UDP可能丟包,不保證數據準確性

  • Socket通訊流程圖
 
image

以socket客戶端編程爲例:
0.導入頭文件

#import <arpa/inet.h> #import <netinet/in.h> #import <sys/socket.h> 

1.建立socket

@implementation ViewController { int _clientSocket; } /* 1.AF_INET: ipv4 執行ip協議的版本 2.SOCK_STREAM:指定Socket類型,面向鏈接的流式socket 傳輸層的協議 3.IPPROTO_TCP:指定協議。 IPPROTO_TCP 傳輸方式TCP傳輸協議 返回值 大於0 建立成功 */ _clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 

2.創建鏈接(與服務器)

/* 終端裏面 命令模擬服務器 netcat nc -lk 12345 參數一:套接字描述符 參數二:指向數據結構sockaddr的指針,其中包括目的端口和IP地址 參數三:參數二sockaddr的長度,能夠經過sizeof(struct sockaddr)得到 返回值 int -1失敗 0 成功 */ struct sockaddr_in addr; /* 填寫sockaddr_in結構*/ addr.sin_family = AF_INET; addr.sin_port=htons(12345); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); int connectResult= connect( _clientSocket, (const struct sockaddr *)&addr, sizeof(addr)); 

3.發送數據(到服務器)

/* 第一個參數指定發送端套接字描述符; 第二個參數指明一個存放應用程式要發送數據的緩衝區; 第三個參數指明實際要發送的數據的字符數; 第四個參數通常置0。 成功則返回實際傳送出去的字符數,失敗返回-1, */ char * str = "itcast"; ssize_t sendLen = send( _clientSocket, str, strlen(str), 0); 

4.接送數據(從服務器)

/* 第一個參數socket 第二個參數存放數據的緩衝區 第三個參數緩衝區長度。 第四個參數指定調用方式,通常置0 返回值 接收成功的字符數 */ char *buf[1024]; ssize_t recvLen = recv( _clientSocket, buf, sizeof(buf), 0); NSLog(@"---->%ld",recvLen); 

5.關閉鏈接

close( _clientSocket);

6.demo封裝方法:

//創建鏈接 - (void)connectToServer:(NSString *)ip port:(int)port { _clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in addr; /* 填寫sockaddr_in結構*/ addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip.UTF8String); int connectResult = connect(_clientSocket, (const struct sockaddr *)&addr, sizeof(addr)); if (connectResult == 0) { NSLog(@"conn ok"); } } //發送數據並等待返回數據 - (NSString *)sentAndRecv:(NSString *)msg { const char *str = msg.UTF8String; //發消息 ssize_t sendLen = send(_clientSocket, str, strlen(str), 0); //收消息 char *buf[1024]; ssize_t recvLen = recv(_clientSocket, buf, sizeof(buf), 0); NSString *recvStr = [[NSString alloc] initWithBytes:buf length:recvLen encoding:NSUTF8StringEncoding]; return recvStr; } 

案例效果圖:

 

 
image

案例一:多線程實現服務端與客戶端簡單的交互,個人demo地址:
服務端: https://github.com/zhonghphuan/ServerSocket.git
客戶端: https://github.com/zhonghphuan/ClientSocket.git

 

案例二:利用Socket發送HTTP格式的請求而且經過瀏覽器監控:
https://github.com/FieldsOfHope/Socket_Interactive.git

做者:鍾環 連接:https://www.jianshu.com/p/1883977690b3 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索